diff --git a/.gitignore b/.gitignore index 1f96f40..a5a72f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,28 +1,44 @@ -# ---> Swift -# Xcode -# -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Xcode ### +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/xcshareddata/ +!*.xcodeproj/project.xcworkspace/ +!*.xcworkspace/contents.xcworkspacedata +/*.gcno +**/xcshareddata/WorkspaceSettings.xcsettings ## User settings xcuserdata/ -## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) -*.xcscmblueprint -*.xccheckout - -## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) -build/ -DerivedData/ -*.moved-aside -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 - ## Obj-C/Swift specific *.hmap @@ -31,70 +47,18 @@ DerivedData/ *.dSYM.zip *.dSYM -## Playgrounds -timeline.xctimeline -playground.xcworkspace - -# Swift Package Manager -# -# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. -# Packages/ -# Package.pins -# Package.resolved -# *.xcodeproj -# -# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata -# hence it is not needed unless you have added a package configuration file to your project -# .swiftpm - -.build/ - -# CocoaPods -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# -# Pods/ -# -# Add this line if you want to avoid checking in source code from the Xcode workspace -# *.xcworkspace - -# Carthage -# -# Add this line if you want to avoid checking in source code from Carthage dependencies. -# Carthage/Checkouts - -Carthage/Build/ - -# Accio dependency management -Dependencies/ -.accio/ - # fastlane -# -# It is recommended to not store the screenshots in the git repo. -# Instead, use fastlane to re-generate the screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://docs.fastlane.tools/best-practices/source-control/#source-control - fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output -# Code Injection -# -# After new code Injection tools there's a generated folder /iOSInjectionProject -# https://github.com/johnno1962/injectionforxcode +### Swift ### -iOSInjectionProject/ - -# ---> Xcode -## User settings -xcuserdata/ - -## Xcode 8 and earlier -*.xcscmblueprint -*.xccheckout +## Playgrounds +timeline.xctimeline +playground.xcworkspace +### SwiftPM ### +Packages +.build/ \ No newline at end of file diff --git a/Apps/Locations/Libraries/Package.swift b/Apps/Locations/Libraries/Package.swift new file mode 100644 index 0000000..03df4b5 --- /dev/null +++ b/Apps/Locations/Libraries/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version: 5.8 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Libraries", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "Libraries", + targets: ["Libraries"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "Libraries", + dependencies: []), + .testTarget( + name: "LibrariesTests", + dependencies: ["Libraries"]), + ] +) diff --git a/Apps/Locations/Libraries/Sources/Libraries/Libraries.swift b/Apps/Locations/Libraries/Sources/Libraries/Libraries.swift new file mode 100644 index 0000000..c1536b6 --- /dev/null +++ b/Apps/Locations/Libraries/Sources/Libraries/Libraries.swift @@ -0,0 +1,6 @@ +public struct Libraries { + public private(set) var text = "Hello, World!" + + public init() { + } +} diff --git a/Apps/Locations/Libraries/Tests/LibrariesTests/LibrariesTests.swift b/Apps/Locations/Libraries/Tests/LibrariesTests/LibrariesTests.swift new file mode 100644 index 0000000..dd47066 --- /dev/null +++ b/Apps/Locations/Libraries/Tests/LibrariesTests/LibrariesTests.swift @@ -0,0 +1,11 @@ +import XCTest +@testable import Libraries + +final class LibrariesTests: XCTestCase { + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct + // results. + XCTAssertEqual(Libraries().text, "Hello, World!") + } +} diff --git a/Apps/Locations/Resources/Assets.xcassets/AccentColor.colorset/Contents.json b/Apps/Locations/Resources/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Apps/Locations/Resources/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apps/Locations/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/Apps/Locations/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Apps/Locations/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apps/Locations/Resources/Assets.xcassets/Contents.json b/Apps/Locations/Resources/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Apps/Locations/Resources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apps/Locations/Resources/Base.lproj/LaunchScreen.storyboard b/Apps/Locations/Resources/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/Apps/Locations/Resources/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Locations/Resources/Base.lproj/Main.storyboard b/Apps/Locations/Resources/Base.lproj/Main.storyboard new file mode 100644 index 0000000..25a7638 --- /dev/null +++ b/Apps/Locations/Resources/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Locations/Resources/Info.plist b/Apps/Locations/Resources/Info.plist new file mode 100644 index 0000000..dd3c9af --- /dev/null +++ b/Apps/Locations/Resources/Info.plist @@ -0,0 +1,25 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + + diff --git a/Apps/Locations/Resources/Locations.xcdatamodeld/.xccurrentversion b/Apps/Locations/Resources/Locations.xcdatamodeld/.xccurrentversion new file mode 100644 index 0000000..593642b --- /dev/null +++ b/Apps/Locations/Resources/Locations.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ + + + + + _XCCurrentVersionName + Locations.xcdatamodel + + diff --git a/Apps/Locations/Resources/Locations.xcdatamodeld/Locations.xcdatamodel/contents b/Apps/Locations/Resources/Locations.xcdatamodeld/Locations.xcdatamodel/contents new file mode 100644 index 0000000..50d2514 --- /dev/null +++ b/Apps/Locations/Resources/Locations.xcdatamodeld/Locations.xcdatamodel/contents @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Apps/Locations/Sources/AppDelegate.swift b/Apps/Locations/Sources/AppDelegate.swift new file mode 100644 index 0000000..c144996 --- /dev/null +++ b/Apps/Locations/Sources/AppDelegate.swift @@ -0,0 +1,82 @@ +// +// AppDelegate.swift +// Locations +// +// Created by Javier Cicchelli on 08/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import UIKit +import CoreData + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + // MARK: - Core Data stack + + lazy var persistentContainer: NSPersistentContainer = { + /* + The persistent container for the application. This implementation + creates and returns a container, having loaded the store for the + application to it. This property is optional since there are legitimate + error conditions that could cause the creation of the store to fail. + */ + let container = NSPersistentContainer(name: "Locations") + container.loadPersistentStores(completionHandler: { (storeDescription, error) in + if let error = error as NSError? { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + + /* + Typical reasons for an error here include: + * The parent directory does not exist, cannot be created, or disallows writing. + * The persistent store is not accessible, due to permissions or data protection when the device is locked. + * The device is out of space. + * The store could not be migrated to the current model version. + Check the error message to determine what the actual problem was. + */ + fatalError("Unresolved error \(error), \(error.userInfo)") + } + }) + return container + }() + + // MARK: - Core Data Saving support + + func saveContext () { + let context = persistentContainer.viewContext + if context.hasChanges { + do { + try context.save() + } catch { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + let nserror = error as NSError + fatalError("Unresolved error \(nserror), \(nserror.userInfo)") + } + } + } + +} + diff --git a/Apps/Locations/Sources/SceneDelegate.swift b/Apps/Locations/Sources/SceneDelegate.swift new file mode 100644 index 0000000..d108611 --- /dev/null +++ b/Apps/Locations/Sources/SceneDelegate.swift @@ -0,0 +1,56 @@ +// +// SceneDelegate.swift +// Locations +// +// Created by Javier Cicchelli on 08/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let _ = (scene as? UIWindowScene) else { return } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + + // Save changes in the application's managed object context when the application transitions to the background. + (UIApplication.shared.delegate as? AppDelegate)?.saveContext() + } + + +} + diff --git a/Apps/Locations/Sources/ViewController.swift b/Apps/Locations/Sources/ViewController.swift new file mode 100644 index 0000000..ea2b4e7 --- /dev/null +++ b/Apps/Locations/Sources/ViewController.swift @@ -0,0 +1,20 @@ +// +// ViewController.swift +// Locations +// +// Created by Javier Cicchelli on 08/04/2023. +// Copyright © 2023 Röck+Cöde. All rights reserved. +// + +import UIKit + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + + +} + diff --git a/Apps/Wikipedia/.circleci/config.yml b/Apps/Wikipedia/.circleci/config.yml new file mode 100644 index 0000000..797a075 --- /dev/null +++ b/Apps/Wikipedia/.circleci/config.yml @@ -0,0 +1,44 @@ +# For a detailed guide to building and testing on iOS, read the docs: +# https://circleci.com/docs/2.0/testing-ios/ + +version: 2.1 + +executors: + xcode: + macos: + xcode: 14.2.0 + +commands: + install_dependencies: + description: "Install dependencies" + steps: + - restore_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + - run: bundle check || bundle install --path vendor/bundle + - save_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + paths: + - vendor/bundle + work_around_swift_package_manager_bug: + description: "Work around a Swift package manager bug" # https://support.circleci.com/hc/en-us/articles/360044709573?input_string=unable%2Bto%2Baccess%2Bprivate%2Bswift%2Bpackage%2Brepository + steps: + - run: sudo defaults write com.apple.dt.Xcode IDEPackageSupportUseBuiltinSCM YES + - run: rm ~/.ssh/id_rsa || true + - run: for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts || true + +jobs: + test_pr: + executor: xcode + steps: + - checkout + - install_dependencies + - work_around_swift_package_manager_bug + - run: + name: Fastlane + command: bundle exec fastlane verify_pull_request + - store_test_results: + path: fastlane/test_output/ +workflows: + test_pr: + jobs: + - test_pr diff --git a/Apps/Wikipedia/.clang-format b/Apps/Wikipedia/.clang-format new file mode 100644 index 0000000..b3e903d --- /dev/null +++ b/Apps/Wikipedia/.clang-format @@ -0,0 +1,61 @@ +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: true +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 8 +UseTab: Never +SortIncludes: false \ No newline at end of file diff --git a/Apps/Wikipedia/.github/FUNDING.yml b/Apps/Wikipedia/.github/FUNDING.yml new file mode 100644 index 0000000..1602ca5 --- /dev/null +++ b/Apps/Wikipedia/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: ['https://donate.wikimedia.org/?utm_medium=githubRepo'] diff --git a/Apps/Wikipedia/.github/pull_request_template.md b/Apps/Wikipedia/.github/pull_request_template.md new file mode 100644 index 0000000..3207c93 --- /dev/null +++ b/Apps/Wikipedia/.github/pull_request_template.md @@ -0,0 +1,10 @@ +**Phabricator:** + +### Notes +* + +### Test Steps +1. + +### Screenshots/Videos + diff --git a/Apps/Wikipedia/.github/workflows/localization.yml b/Apps/Wikipedia/.github/workflows/localization.yml new file mode 100644 index 0000000..0fa81de --- /dev/null +++ b/Apps/Wikipedia/.github/workflows/localization.yml @@ -0,0 +1,29 @@ +# Transform localizations from TranslateWiki.net +# +# As of late 2020, TranslateWiki creates PRs automatically, in line with +# how they work with other repos. However, the strings still need to be +# translated to work well with the iOS app. This script runs when a new PR +# is created by TranslateWiki, to add everything the app needs to the PR. +# +name: Import localizations from TranslateWiki + +on: + push: + branches: + - twn + +jobs: + update-localizations: + runs-on: macOS-latest + steps: + - uses: actions/checkout@v2 + + - name: Update localizations + continue-on-error: true + run: | + $GITHUB_WORKSPACE/scripts/localization $GITHUB_WORKSPACE + git config user.name github-actions + git config user.email github-actions@github.com + git add . + git commit -m "Import translations from TranslateWiki" + git push diff --git a/Apps/Wikipedia/.jshintignore b/Apps/Wikipedia/.jshintignore new file mode 100644 index 0000000..16d5f6a --- /dev/null +++ b/Apps/Wikipedia/.jshintignore @@ -0,0 +1,3 @@ +Wikipedia/assets/** +fastlane/** +www/node_modules/** \ No newline at end of file diff --git a/Apps/Wikipedia/.node-version b/Apps/Wikipedia/.node-version new file mode 100644 index 0000000..89da89d --- /dev/null +++ b/Apps/Wikipedia/.node-version @@ -0,0 +1 @@ +10.16.0 \ No newline at end of file diff --git a/Apps/Wikipedia/.ruby-version b/Apps/Wikipedia/.ruby-version new file mode 100644 index 0000000..eca690e --- /dev/null +++ b/Apps/Wikipedia/.ruby-version @@ -0,0 +1 @@ +3.0.5 diff --git a/Apps/Wikipedia/.swiftlint-autocorrect.yml b/Apps/Wikipedia/.swiftlint-autocorrect.yml new file mode 100644 index 0000000..72082da --- /dev/null +++ b/Apps/Wikipedia/.swiftlint-autocorrect.yml @@ -0,0 +1,37 @@ +disabled_rules: + - class_delegate_protocol + - colon + - comma + - compiler_protocol_init + - cyclomatic_complexity + - file_length + - force_cast + - force_try + - for_where + - function_body_length + - function_parameter_count + - identifier_name + - is_disjoint + - large_tuple + - legacy_random + - line_length + - multiple_closures_with_trailing_closure + - nesting + - no_fallthrough_only + - operator_whitespace + - private_over_fileprivate + - redundant_objc_attribute + - redundant_optional_initialization + - redundant_string_enum_value + - shorthand_operator + - todo + - trailing_newline + - trailing_whitespace + - type_body_length + - type_name + - unneeded_break_in_switch + - unused_closure_parameter + - notification_center_detachment + +vertical_whitespace: + max_empty_lines: 2 diff --git a/Apps/Wikipedia/.swiftlint.yml b/Apps/Wikipedia/.swiftlint.yml new file mode 100644 index 0000000..72082da --- /dev/null +++ b/Apps/Wikipedia/.swiftlint.yml @@ -0,0 +1,37 @@ +disabled_rules: + - class_delegate_protocol + - colon + - comma + - compiler_protocol_init + - cyclomatic_complexity + - file_length + - force_cast + - force_try + - for_where + - function_body_length + - function_parameter_count + - identifier_name + - is_disjoint + - large_tuple + - legacy_random + - line_length + - multiple_closures_with_trailing_closure + - nesting + - no_fallthrough_only + - operator_whitespace + - private_over_fileprivate + - redundant_objc_attribute + - redundant_optional_initialization + - redundant_string_enum_value + - shorthand_operator + - todo + - trailing_newline + - trailing_whitespace + - type_body_length + - type_name + - unneeded_break_in_switch + - unused_closure_parameter + - notification_center_detachment + +vertical_whitespace: + max_empty_lines: 2 diff --git a/Apps/Wikipedia/.xctool-args b/Apps/Wikipedia/.xctool-args new file mode 100644 index 0000000..da5b3f9 --- /dev/null +++ b/Apps/Wikipedia/.xctool-args @@ -0,0 +1,7 @@ +[ + "-workspace", "Wikipedia.xcworkspace", + "-scheme", "Wikipedia", + "-configuration", "Debug", + "-sdk", "iphonesimulator", + "-destination", "platform=iOS Simulator,name=iPhone 6,OS=10.0" +] diff --git a/Apps/Wikipedia/.xcversion b/Apps/Wikipedia/.xcversion new file mode 100644 index 0000000..9714cb3 --- /dev/null +++ b/Apps/Wikipedia/.xcversion @@ -0,0 +1 @@ +14.2.0 \ No newline at end of file diff --git a/Apps/Wikipedia/CODE_OF_CONDUCT.md b/Apps/Wikipedia/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..2279c40 --- /dev/null +++ b/Apps/Wikipedia/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Code_of_Conduct). \ No newline at end of file diff --git a/Apps/Wikipedia/CONTRIBUTING.md b/Apps/Wikipedia/CONTRIBUTING.md new file mode 100644 index 0000000..9497bae --- /dev/null +++ b/Apps/Wikipedia/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contributing +We welcome volunteers to contribute to the Wikipedia iOS app codebase. + +## Development instructions +Before developing, please read the [setup instructions](README.md). + +Once your contributions are ready for review, add yourself in alphabetical order under contributors in `Code/AboutViewController.plist` and post a pull request on GitHub. One of the maintainers will review the PR. Thanks for contributing! 🎉 + +## What can I work on? +If you're looking for easy work, look at the tasks marked with the "good first bug" tag. [This link](https://phabricator.wikimedia.org/project/board/782/query/7vYTqNgpvqjh/) will show you all the "good first bug" tasks in the iOS backlog. + +If you're ready to pick up more difficult work, look at the iOS backlog and pick something from the Bug Backlog column. [This link](https://phabricator.wikimedia.org/project/board/782/) will show you all the tasks in the iOS backlog. If the status of the task is unclear or you need more information, feel free to leave a comment and we'll try to respond as soon as possible. + +## I found my task. What next? +Now you want to let the team know what you're working on. + +1. In Phabricator, assign the task to yourself. +2. Add the tag representing the current release to the task. [This link](https://phabricator.wikimedia.org/search/query/WlSMhOAWTG73/) will take you to currently open releases. Tagging your task with the name of the release will add it to the release board. +3. On the release board, move the task to the "Doing" column. +4. When you're done developing, move the task to the "Needs Code Review" column. + +## I don't want to work on my task any more. +You can let us know by unassigning the task and moving it back to the "Tasks from Product Backlog" column. + +## How will I know that my contribution was accepted? +Your PR will be merged and your task will get moved to the "Ready for PM Signoff" column. This means that your contribution will be included in the upcoming release. + +## Tips +[Wiki on how to use Phabricator](https://www.mediawiki.org/wiki/Phabricator/Project_management) + + + diff --git a/Apps/Wikipedia/Command Line Tools/Update Languages/WikipediaLanguageCommandLineUtility.swift b/Apps/Wikipedia/Command Line Tools/Update Languages/WikipediaLanguageCommandLineUtility.swift new file mode 100644 index 0000000..bc4fca2 --- /dev/null +++ b/Apps/Wikipedia/Command Line Tools/Update Languages/WikipediaLanguageCommandLineUtility.swift @@ -0,0 +1,106 @@ +import Foundation +import Combine + +/// Command line tool for updating prebuilt language lists and lookup tables used in the app +class WikipediaLanguageCommandLineUtility { + let pathComponents: [String] + let api = WikipediaLanguageCommandLineUtilityAPI() + + /// - Parameter path: the path to the wikipedia-ios project folder + init(path: String) { + pathComponents = path.components(separatedBy: "/") + } + + var cancellable: AnyCancellable? + + /// Generates all the necessary files + func run(_ completion: @escaping () -> Void) { + cancellable = api.getSites().sink(receiveCompletion: { (result) in + switch result { + case .failure(let error): + print("Error fetching sites: \(error)") + abort() + default: + break + } + }) { (sites) in + let sortedSites = sites.sorted { (a, b) -> Bool in + return a.languageCode < b.languageCode + } + self.writeCodable(sortedSites, to: ["Wikipedia", "Code", "wikipedia-languages.json"]) + self.cancellable = self.writeNamespaceFiles(with: sites) { + self.cancellable = self.writeCodemirrorConfig(with: sites, completion: { + completion() + }) + } + } + + } + + private func getOutputFileURL(with components: [String]) -> URL { + let outputComponents = pathComponents + components + let outputPath = outputComponents.joined(separator: "/") + return URL(fileURLWithPath: outputPath) + } + + private func writeCodable(_ codable: T, to pathComponents: [String]) { + do { + let encoder = JSONEncoder() + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] + let data = try encoder.encode(codable) + let outputFileURL = getOutputFileURL(with: pathComponents) + try data.write(to: outputFileURL) + } catch let error { + print("Error writing to file: \(error)") + } + } + + private func writeCodemirrorConfig(with sites: [Wikipedia], completion: @escaping () -> Void) -> AnyCancellable { + Publishers.MergeMany(sites.map { site in + api.getCodeMirrorConfigJSON(for: site.languageCode) + .map { ($0, site) } + }) + .sink(receiveCompletion: { (result) in + switch result { + case .finished: + break + case .failure(let error): + print("Error writing codemirror config: \(error)") + } + completion() + }) { (value) in + let outputURL = self.getOutputFileURL(with: ["Wikipedia", "assets", "codemirror", "config", "codemirror-config-\(value.1.languageCode).json"]) + try! value.0.write(to: outputURL, atomically: true, encoding: .utf8) + } + } + + private func writeNamespaceFiles(with sites: [Wikipedia], completion: @escaping () -> Void) -> AnyCancellable? { + return Publishers.MergeMany(sites.map { site in + api.getSiteInfo(with: site.languageCode) + .map { ($0, site) } + }) + .map { + (self.getSiteInfoLookup(with: $0.0), $0.1) + } + .sink(receiveCompletion: { (result) in + completion() + }) { (siteInfoTuple) in + self.writeCodable(siteInfoTuple.0, to: ["Wikipedia", "Code", "wikipedia-namespaces", "\(siteInfoTuple.1.languageCode).json"]) + } + } + + private func getSiteInfoLookup(with siteInfo: SiteInfo) -> WikipediaSiteInfoLookup { + var namespaces = [String: PageNamespace].init(minimumCapacity: siteInfo.query.namespaces.count + siteInfo.query.namespacealiases.count) + for (_, namespace) in siteInfo.query.namespaces { + namespaces[namespace.name.uppercased()] = PageNamespace(rawValue: namespace.id) + guard let canonical = namespace.canonical else { + continue + } + namespaces[canonical.uppercased()] = PageNamespace(rawValue: namespace.id) + } + for namespaceAlias in siteInfo.query.namespacealiases { + namespaces[namespaceAlias.alias.uppercased()] = PageNamespace(rawValue: namespaceAlias.id) + } + return WikipediaSiteInfoLookup(namespace: namespaces, mainpage: siteInfo.query.general.mainpage.uppercased()) + } +} diff --git a/Apps/Wikipedia/Command Line Tools/Update Languages/WikipediaLanguageCommandLineUtilityAPI.swift b/Apps/Wikipedia/Command Line Tools/Update Languages/WikipediaLanguageCommandLineUtilityAPI.swift new file mode 100644 index 0000000..831ef32 --- /dev/null +++ b/Apps/Wikipedia/Command Line Tools/Update Languages/WikipediaLanguageCommandLineUtilityAPI.swift @@ -0,0 +1,126 @@ +import Foundation +import Combine + +/// Utility for making API calls that update prebuilt lists of information about different language Wikipedias +class WikipediaLanguageCommandLineUtilityAPI { + // We can't use codable because this API's response has a dictionary with arbitrary keys mapped to values of mixed type. + // For example, the mixing of ints and dictionaries in "sitematrix": + // "sitematrix": { + // "count": 951, + // "0": { + // "code": "aa", + // "name": "Qaf\u00e1r af", + // ... + // }, + // "1": { + // "code": "ab", + // "name":"аԥсшәа", + // ... + // }, + // ... + // } + func getSites() -> AnyPublisher<[Wikipedia], Error> { + let sitematrixURL = URL(string: "https://meta.wikimedia.org/w/api.php?action=sitematrix&smsiteprop=url%7Cdbname%7Ccode%7Csitename%7Clang&format=json&formatversion=2&origin=*")! + return URLSession.shared + .dataTaskPublisher(for: sitematrixURL) + .tryMap { result -> [String: Any] in + /// See above as to why all of this is necessary instead of using codable + guard let jsonObject = try JSONSerialization.jsonObject(with: result.data, options: .allowFragments) as? [String: Any] else { + throw WikipediaLanguageUtilityAPIError.generic + } + return jsonObject + } + .map { jsonObject -> [Wikipedia] in + /// See above as to why all of this is necessary instead of using codable + guard let sitematrix = jsonObject["sitematrix"] as? [String: Any] else { + return [] + } + var wikipedias = sitematrix.compactMap { (kv) -> Wikipedia? in + guard + let result = kv.value as? [String: Any], + let code = result["code"] as? String, + let name = result["name"] as? String, + let localname = result["localname"] as? String + else { + return nil + } + + guard code != "no" else { + // Norwegian (Bokmål) has a different ISO code than it's subdomain, which is useful to reference in some instances (prepopulating preferredLanguages from iOS device languages, and choosing the correct alternative article language from the langlinks endpoint). + // https://phabricator.wikimedia.org/T276645 + // https://phabricator.wikimedia.org/T272193 + return Wikipedia(languageCode: code, languageName: name, localName: localname, altISOCode: "nb") + } + + return Wikipedia(languageCode: code, languageName: name, localName: localname, altISOCode: nil) + } + // Add testwiki, it's not returned by the site matrix + wikipedias.append(Wikipedia(languageCode: "test", languageName: "Test", localName: "Test", altISOCode: nil)) + return wikipedias + }.eraseToAnyPublisher() + } + + func getSiteInfo(with languageCode: String) -> AnyPublisher { + let siteInfoURL = URL(string: "https://\(languageCode).wikipedia.org/w/api.php?action=query&format=json&prop=&list=&meta=siteinfo&siprop=namespaces%7Cgeneral%7Cnamespacealiases&formatversion=2&origin=*")! + return URLSession.shared + .dataTaskPublisher(for: siteInfoURL) + .tryMap { (result) -> SiteInfo in + try JSONDecoder().decode(SiteInfo.self, from: result.data) + }.eraseToAnyPublisher() + } + + func getCodeMirrorConfigJSON(for wikiLanguage: String) -> AnyPublisher { + let codeMirrorConfigURL = URL(string: "http://\(wikiLanguage).wikipedia.org/w/load.php?debug=false&lang=en&modules=ext.CodeMirror.data")! + return URLSession.shared.dataTaskPublisher(for: codeMirrorConfigURL) + .tryMap { (result) -> String in + guard + let responseString = String(data: result.data, encoding: .utf8), + let soughtSubstring = self.extractJSONString(from: responseString) + else { + throw WikipediaLanguageUtilityAPIError.generic + } + return soughtSubstring.replacingOccurrences(of: "!0", with: "true") + }.eraseToAnyPublisher() + } + + private let jsonExtractionRegex = try! NSRegularExpression(pattern: #"(?:mw\.config\.set\()(.*?)(?:\);\n*\}\);)"#, options: [.dotMatchesLineSeparators]) + + private func extractJSONString(from responseString: String) -> String? { + let results = jsonExtractionRegex.matches(in: responseString, range: NSRange(responseString.startIndex..., in: responseString)) + guard + results.count == 1, + let firstResult = results.first, + firstResult.numberOfRanges == 2, + let soughtCaptureGroupRange = Range(firstResult.range(at: 1), in: responseString) + else { + return nil + } + return String(responseString[soughtCaptureGroupRange]) + } + +} + +enum WikipediaLanguageUtilityAPIError: Error { + case generic +} + +struct SiteInfo: Codable { + struct Namespace: Codable { + let id: Int + let name: String + let canonical: String? + } + struct NamespaceAlias: Codable { + let id: Int + let alias: String + } + struct General: Codable { + let mainpage: String + } + struct Query: Codable { + let general: General + let namespaces: [String: Namespace] + let namespacealiases: [NamespaceAlias] + } + let query: Query +} diff --git a/Apps/Wikipedia/Command Line Tools/Update Languages/main.swift b/Apps/Wikipedia/Command Line Tools/Update Languages/main.swift new file mode 100644 index 0000000..5dcfe42 --- /dev/null +++ b/Apps/Wikipedia/Command Line Tools/Update Languages/main.swift @@ -0,0 +1,17 @@ +import Foundation +import Combine + +/// **THIS IS NOT PART OF THE MAIN APP - IT'S A COMMAND LINE UTILITY** + +let count = CommandLine.arguments.count +guard count > 1 else { + abort() +} + +let path = CommandLine.arguments[1] +let utility = WikipediaLanguageCommandLineUtility(path: path) +utility.run { + exit(0) +} + +dispatchMain() diff --git a/Apps/Wikipedia/Command Line Tools/Update Localizations/localization.swift b/Apps/Wikipedia/Command Line Tools/Update Localizations/localization.swift new file mode 100644 index 0000000..08dafb7 --- /dev/null +++ b/Apps/Wikipedia/Command Line Tools/Update Localizations/localization.swift @@ -0,0 +1,609 @@ +import Foundation + +/// **THIS IS NOT PART OF THE MAIN APP - IT'S A COMMAND LINE UTILITY** + +fileprivate var dictionaryRegex: NSRegularExpression? = { + do { + return try NSRegularExpression(pattern: "(?:[{][{])(:?[^{]*)(?:[}][}])", options: []) + } catch { + assertionFailure("Localization regex failed to compile") + } + return nil +}() + +fileprivate var curlyBraceRegex: NSRegularExpression? = { + do { + return try NSRegularExpression(pattern: "(?:[{][{][a-z]+:)(:?[^{]*)(?:[}][}])", options: [.caseInsensitive]) + } catch { + assertionFailure("Localization regex failed to compile") + } + return nil +}() + + +fileprivate var twnTokenRegex: NSRegularExpression? = { + do { + return try NSRegularExpression(pattern: "(?:[$])(:?[0-9]+)", options: []) + } catch { + assertionFailure("Localization token regex failed to compile") + } + return nil +}() + +fileprivate var iOSTokenRegex: NSRegularExpression? = { + do { + return try NSRegularExpression(pattern: "%([0-9]*)\\$?([@dDuUxXoOfeEgGcCsSpaAF])", options: []) + } catch { + assertionFailure("Localization token regex failed to compile") + } + return nil +}() + +fileprivate var mwLocalizedStringRegex: NSRegularExpression? = { + do { + return try NSRegularExpression(pattern: "(?:WMLocalizedString\\(@\\\")(:?[^\"]+)(?:[^\\)]*\\))", options: []) + } catch { + assertionFailure("mwLocalizedStringRegex failed to compile") + } + return nil +}() + +fileprivate var countPrefixRegex: NSRegularExpression? = { + do { + return try NSRegularExpression(pattern: "(:?^[^\\=]+)(?:=)", options: []) + } catch { + assertionFailure("countPrefixRegex failed to compile") + } + return nil +}() + +// lookup from translatewiki prefix to iOS-supported stringsdict key +let keysByPrefix = [ + "0":"zero", + // "1":"one" digits on translatewiki mean only use the translation when the replacement number === that digit. On iOS one, two, and few are more generic. for example, the translation for one could map to 1, 21, 31, etc + // "2":"two", + // "3":"few" + "zero":"zero", + "one":"one", + "two":"two", + "few":"few", + "many":"many", + "other":"other" +] + +extension String { + var fullRange: NSRange { + return NSRange(startIndex.. NSDictionary? { + // https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html#//apple_ref/doc/uid/10000171i-CH16-SW1 + guard let dictionaryRegex = dictionaryRegex else { + return nil + } + var remainingKeys = keys + let fullRange = self.fullRange + let mutableDictionary = NSMutableDictionary(capacity: 5) + let results = dictionaryRegex.matches(in: self, options: [], range: fullRange) + let nsSelf = self as NSString + + // format is the full string with the plural tokens replaced by variables + // it will be built by enumerating through the matches for the plural regex + var format = "" + var location = 0 + for result in results { + // append the next part of the string after the last match and before this one + format += nsSelf.substring(with: NSRange(location: location, length: result.range.location - location)).iOSNativeLocalization(tokens: tokens) + location = result.range.location + result.range.length + + // get the contents of the match - the content between {{ and }} + let contents = dictionaryRegex.replacementString(for: result, in: self, offset: 0, template: "$1") + + let components = contents.components(separatedBy: "|") + + let countOfComponents = components.count + guard countOfComponents > 1 else { + continue + } + + let pluralPrefix = "PLURAL:" + let firstComponent = components[0] + guard firstComponent.hasPrefix(pluralPrefix) else { + continue + } + + if firstComponent == pluralPrefix { + print("Incorrectly formatted plural: \(self)") + abort() + } + + let token = firstComponent.suffix(from: firstComponent.index(firstComponent.startIndex, offsetBy: 7)).trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + let nsToken = (token as NSString) + let tokenNumber = nsToken.substring(from: 1) + + guard + let tokenInt = Int(tokenNumber), + tokenInt > 0 + else { + continue + } + + let keyDictionary = NSMutableDictionary(capacity: 5) + let formatValueType = tokens[tokenNumber] ?? "d" + keyDictionary["NSStringFormatSpecTypeKey"] = "NSStringPluralRuleType" + keyDictionary["NSStringFormatValueTypeKey"] = formatValueType + + guard let countPrefixRegex = countPrefixRegex else { + abort() + } + + var unlabeledComponents: [String] = [] + for component in components[1.. Bool in + return key != aKey + }) + actualComponent = String(component.suffix(from: component.index(component.startIndex, offsetBy: match.range.length))) + } else { + #if DEBUG + print("Translatewiki prefix \(numberString) not supported on iOS for this language. Ignoring \(String(describing: component))") + #endif + } + + guard let keyToInsert = keyForComponent, let componentToInsert = actualComponent else { + continue + } + + keyDictionary[keyToInsert] = componentToInsert.iOSNativeLocalization(tokens: tokens) + } + + if let other = unlabeledComponents.last { + keyDictionary["other"] = other.iOSNativeLocalization(tokens: tokens) + + for (keyIndex, component) in unlabeledComponents.enumerated() { + guard + keyIndex < unlabeledComponents.count - 1, + keyIndex < remainingKeys.count + else { + break + } + keyDictionary[remainingKeys[keyIndex]] = component.iOSNativeLocalization(tokens: tokens) + } + } else if keyDictionary["other"] == nil { + if keyDictionary["many"] != nil { + keyDictionary["other"] = keyDictionary["many"] + } else { + print("missing default translation") + abort() + } + } + + // set the variable name for the plural replacement + let key = "v\(tokenInt)" + // include the dictionary of possible replacements for the plural token + mutableDictionary[key] = keyDictionary + // append the variable name to the format string where the plural token used to be + format += "%#@\(key)@" + } + // append the final part of the string after the last match + format += nsSelf.substring(with: NSRange(location: location, length: nsSelf.length - location)).iOSNativeLocalization(tokens: tokens) + mutableDictionary["NSStringLocalizedFormatKey"] = format + return mutableDictionary + } + + public func replacingMatches(fromRegex regex: NSRegularExpression, withFormat format: String) -> String { + let nativeLocalization = NSMutableString(string: self) + var offset = 0 + let fullRange = NSRange(location: 0, length: nativeLocalization.length) + var index = 1 + regex.enumerateMatches(in: self, options: [], range: fullRange) { (result, flags, stop) in + guard let result = result else { + return + } + var token = regex.replacementString(for: result, in: nativeLocalization as String, offset: offset, template: "$1") + // If the token doesn't have an index, give it one + // This allows us to support unordered tokens for single token strings + if token == "" { + token = "\(index)" + } + let replacement = String(format: format, token) + let replacementRange = NSRange(location: result.range.location + offset, length: result.range.length) + nativeLocalization.replaceCharacters(in: replacementRange, with: replacement) + offset += (replacement as NSString).length - result.range.length + index += 1 + } + return nativeLocalization as String + } + + public func replacingMatches(fromTokenRegex regex: NSRegularExpression, withFormat format: String, tokens: [String: String]) -> String { + let nativeLocalization = NSMutableString(string: self) + var offset = 0 + let fullRange = NSRange(location: 0, length: nativeLocalization.length) + regex.enumerateMatches(in: self, options: [], range: fullRange) { (result, flags, stop) in + guard let result = result else { + return + } + let token = regex.replacementString(for: result, in: nativeLocalization as String, offset: offset, template: "$1") + let replacement = String(format: format, token, tokens[token] ?? "@") + let replacementRange = NSRange(location: result.range.location + offset, length: result.range.length) + nativeLocalization.replaceCharacters(in: replacementRange, with: replacement) + offset += (replacement as NSString).length - result.range.length + } + return nativeLocalization as String + } + + func iOSNativeLocalization(tokens: [String: String]) -> String { + guard let tokenRegex = twnTokenRegex, let braceRegex = curlyBraceRegex else { + return "" + } + return self.replacingMatches(fromRegex: braceRegex, withFormat: "%@").replacingMatches(fromTokenRegex: tokenRegex, withFormat: "%%%@$%@", tokens: tokens) + } + + var twnNativeLocalization: String { + guard let tokenRegex = iOSTokenRegex else { + return "" + } + return self.replacingMatches(fromRegex: tokenRegex, withFormat: "$%@") + } + + var iOSTokenDictionary: [String: String] { + guard let iOSTokenRegex = iOSTokenRegex else { + print("Unable to compile iOS token regex") + abort() + } + var tokenDictionary = [String:String]() + iOSTokenRegex.enumerateMatches(in: self, options: [], range:self.fullRange, using: { (result, flags, stop) in + guard let result = result else { + return + } + var number = iOSTokenRegex.replacementString(for: result, in: self as String, offset: 0, template: "$1") + // treat an un-numbered token as 1 + if number == "" { + number = "1" + } + let token = iOSTokenRegex.replacementString(for: result, in: self as String, offset: 0, template: "$2") + if tokenDictionary[number] == nil { + tokenDictionary[number] = token + } else if token != tokenDictionary[number] { + print("Internal token mismatch: \(self)") + abort() + } + }) + return tokenDictionary + } +} + +func writeStrings(fromDictionary dictionary: NSDictionary, toFile: String) throws { + var shouldWrite = true + + if let existingDictionary = NSDictionary(contentsOfFile: toFile) { + shouldWrite = existingDictionary.count != dictionary.count + if !shouldWrite { + for (key, value) in dictionary { + guard let value = value as? String, let existingValue = existingDictionary[key] as? NSString else { + shouldWrite = true + break + } + shouldWrite = !existingValue.isEqual(to: value) + if shouldWrite { + break + } + } + } + } + + + guard shouldWrite else { + return + } + + let folder = (toFile as NSString).deletingLastPathComponent + do { + try FileManager.default.createDirectory(atPath: folder, withIntermediateDirectories: true, attributes: nil) + } catch { } + let output = dictionary.descriptionInStringsFileFormat + try output.write(toFile: toFile, atomically: true, encoding: .utf16) // From Apple: Note: It is recommended that you save strings files using the UTF-16 encoding, which is the default encoding for standard strings files. It is possible to create strings files using other property-list formats, including binary property-list formats and XML formats that use the UTF-8 encoding, but doing so is not recommended. For more information about Unicode and its text encodings, go to http://www.unicode.org/ or http://en.wikipedia.org/wiki/Unicode. +} + +// See "Localized Metadata" section here: https://docs.fastlane.tools/actions/deliver/ +func fileURLForFastlaneMetadataFolder(for locale: String) -> URL { + return URL(fileURLWithPath:"\(path)/fastlane/metadata/\(locale)") +} + +func fileURLForFastlaneMetadataFile(_ file: String, for locale: String) -> URL { + return fileURLForFastlaneMetadataFolder(for: locale).appendingPathComponent(file) +} + +let defaultAppStoreMetadataLocale = "en-us" +func writeFastlaneMetadata(_ metadata: Any?, to filename: String, for locale: String) throws { + let metadataFileURL = fileURLForFastlaneMetadataFile(filename, for: locale) + guard let metadata = metadata as? String, metadata.count > 0 else { + let defaultDescriptionFileURL = fileURLForFastlaneMetadataFile(filename, for: defaultAppStoreMetadataLocale) + let fm = FileManager.default + try fm.removeItem(at: metadataFileURL) + try fm.copyItem(at: defaultDescriptionFileURL, to: metadataFileURL) + return + } + try metadata.write(to: metadataFileURL, atomically: true, encoding: .utf8) +} + +func writeTWNStrings(fromDictionary dictionary: [String: String], toFile: String, escaped: Bool) throws { + var output = "" + let sortedDictionary = dictionary.sorted(by: { (kv1, kv2) -> Bool in + return kv1.key < kv2.key + }) + for (key, value) in sortedDictionary { + output.append("\"\(key)\" = \"\(escaped ? value.escapedString : value)\";\n") + } + + try output.write(toFile: toFile, atomically: true, encoding: .utf8) +} + +func exportLocalizationsFromSourceCode(_ path: String) { + let iOSENPath = "\(path)/Wikipedia/iOS Native Localizations/en.lproj/Localizable.strings" + let twnQQQPath = "\(path)/Wikipedia/Localizations/qqq.lproj/Localizable.strings" + let twnENPath = "\(path)/Wikipedia/Localizations/en.lproj/Localizable.strings" + guard let iOSEN = NSDictionary(contentsOfFile: iOSENPath) else { + print("Unable to read \(iOSENPath)") + abort() + } + + let twnQQQ = NSMutableDictionary() + let twnEN = NSMutableDictionary() + + do { + let commentSet = CharacterSet(charactersIn: "/* ") + let quoteSet = CharacterSet(charactersIn: "\"") + let string = try String(contentsOfFile: iOSENPath) + let lines = string.components(separatedBy: .newlines) + var currentComment: String? + var currentKey: String? + var commentsByKey = [String: String]() + for line in lines { + let cleanedLine = line.trimmingCharacters(in: .whitespaces) + if cleanedLine.hasPrefix("/*") { + currentComment = cleanedLine.trimmingCharacters(in: commentSet) + currentKey = nil + } else if currentComment != nil { + let quotesRemoved = cleanedLine.trimmingCharacters(in: quoteSet) + + if let range = quotesRemoved.range(of: "\" = \"") { + currentKey = String(quotesRemoved.prefix(upTo: range.lowerBound)) + } + } + if let key = currentKey, let comment = currentComment { + commentsByKey[key] = comment + } + } + + for (key, comment) in commentsByKey { + twnQQQ[key] = comment.twnNativeLocalization + } + try writeTWNStrings(fromDictionary: twnQQQ as! [String: String], toFile: twnQQQPath, escaped: false) + + for (key, value) in iOSEN { + guard let value = value as? String, let key = key as? String else { + continue + } + twnEN[key] = value.twnNativeLocalization + } + try writeTWNStrings(fromDictionary: twnEN as! [String: String], toFile: twnENPath, escaped: true) + } catch let error { + print("Error exporting localizations: \(error)") + abort() + } +} + +let locales: Set = { + var identifiers = Locale.availableIdentifiers + if let filenames = try? FileManager.default.contentsOfDirectory(atPath: "\(path)/Wikipedia/iOS Native Localizations") { + let additional = filenames.compactMap { $0.components(separatedBy: ".").first?.lowercased() } + identifiers += additional + } + identifiers += ["ku"] // iOS 13 added support for ku but macOS 10.14 doesn't include it, add it manually. This line can be removed when macOS 10.15 ships. + return Set(identifiers) +}() + +// See supportsOneEquals documentation. Utilized this list: https://unicode-org.github.io/cldr-staging/charts/37/supplemental/language_plural_rules.html to verify languages where that applies for the cardinal -> one rule +let localesWhereMediaWikiPluralRulesDoNotMatchiOSPluralRulesForOne = { + return Set(["be", "bs", "br", "ceb", "tzm", "hr", "fil", "is", "lv", "lt", "dsb", "mk", "gv", "prg", "ru", "gd", "sr", "sl", "uk", "hsb"]).intersection(locales) +}() + +func localeIsAvailable(_ locale: String) -> Bool { + let prefix = locale.components(separatedBy: "-").first ?? locale + return locales.contains(prefix) +} + +func importLocalizationsFromTWN(_ path: String) { + let enPath = "\(path)/Wikipedia/iOS Native Localizations/en.lproj/Localizable.strings" + + guard let enDictionary = NSDictionary(contentsOfFile: enPath) as? [String: String] else { + print("Unable to read \(enPath)") + abort() + } + + var enTokensByKey = [String: [String: String]]() + + for (key, value) in enDictionary { + enTokensByKey[key] = value.iOSTokenDictionary + } + + let fm = FileManager.default + do { + let keysByLanguage = ["pl": ["one", "few"], "sr": ["one", "few", "many"], "ru": ["one", "few", "many"]] + let defaultKeys = ["one"] + let appStoreMetadataLocales: [String: [String]] = [ + "da": ["da"], + "de": ["de-de"], + "el": ["el"], + // "en": ["en-au", "en-ca", "en-gb"], + "es": ["es-mx", "es-es"], + "fi": ["fi"], + "fr": ["fr-ca", "fr-fr"], + "id": ["id"], + "it": ["it"], + "ja": ["ja"], + "ko": ["ko"], + "ms": ["ms"], + "nl": ["nl-nl"], + "no": ["no"], + "pt": ["pt-br", "pt-pt"], + "ru": ["ru"], + "sv": ["sv"], + "th": ["th"], + "tr": ["tr"], + "vi": ["vi"], + "zh-hans": ["zh-hans"], + "zh-hant": ["zh-hant"] + ] + + let contents = try fm.contentsOfDirectory(atPath: "\(path)/Wikipedia/Localizations") + var pathsForEnglishPlurals: [String] = [] // write english plurals to these paths as placeholders + var englishPluralDictionary: NSMutableDictionary? + for filename in contents { + guard let locale = filename.components(separatedBy: ".").first?.lowercased() else { + continue + } + + let localeFolder = "\(path)/Wikipedia/iOS Native Localizations/\(locale).lproj" + + guard localeIsAvailable(locale), let twnStrings = NSDictionary(contentsOfFile: "\(path)/Wikipedia/Localizations/\(locale).lproj/Localizable.strings") else { + try? fm.removeItem(atPath: localeFolder) + continue + } + + let stringsDictFilePath = "\(localeFolder)/Localizable.stringsdict" + let stringsFilePath = "\(localeFolder)/Localizable.strings" + + let stringsDict = NSMutableDictionary(capacity: twnStrings.count) + let strings = NSMutableDictionary(capacity: twnStrings.count) + for (key, value) in twnStrings { + guard let twnString = value as? String, let key = key as? String, let enTokens = enTokensByKey[key] else { + continue + } + let nativeLocalization = twnString.iOSNativeLocalization(tokens: enTokens) + let nativeLocalizationTokens = nativeLocalization.iOSTokenDictionary + guard nativeLocalizationTokens == enTokens else { + #if DEBUG + print("Mismatched tokens in \(locale) for \(key):\n\(enDictionary[key] ?? "")\n\(nativeLocalization)") + #endif + continue + } + if twnString.contains("{{PLURAL:") { + let lang = locale.components(separatedBy: "-").first ?? "" + let keys = keysByLanguage[lang] ?? defaultKeys + let supportsOneEquals = !localesWhereMediaWikiPluralRulesDoNotMatchiOSPluralRulesForOne.contains(lang) + stringsDict[key] = twnString.pluralDictionary(with: keys, tokens:enTokens, supportsOneEquals: supportsOneEquals) + strings[key] = nativeLocalization + } else { + strings[key] = nativeLocalization + } + } + if locale != "en" { // only write the english plurals, skip the main file + if strings.count > 0 { + try writeStrings(fromDictionary: strings, toFile: stringsFilePath) + } else { + try? fm.removeItem(atPath: stringsFilePath) + } + } else { + englishPluralDictionary = stringsDict + } + + + if let metadataLocales = appStoreMetadataLocales[locale] { + for metadataLocale in metadataLocales { + let folderURL = fileURLForFastlaneMetadataFolder(for: metadataLocale) + try fm.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil) + + let infoPlistPath = "\(path)/Wikipedia/iOS Native Localizations/\(locale).lproj/InfoPlist.strings" + let infoPlist = NSDictionary(contentsOfFile: infoPlistPath) + + try? writeFastlaneMetadata(strings["app-store-short-description"], to: "description.txt", for: metadataLocale) + try? writeFastlaneMetadata(strings["app-store-keywords"], to: "keywords.txt", for: metadataLocale) + try? writeFastlaneMetadata(nil, to: "marketing_url.txt", for: metadataLocale) // use nil to copy from en-US. all fields need to be specified. + try? writeFastlaneMetadata(infoPlist?["CFBundleDisplayName"], to: "name.txt", for: metadataLocale) + try? writeFastlaneMetadata(nil, to: "privacy_url.txt", for: metadataLocale) // use nil to copy from en-US. all fields need to be specified. + try? writeFastlaneMetadata(nil, to: "promotional_text.txt", for: metadataLocale) // use nil to copy from en-US. all fields need to be specified. + try? writeFastlaneMetadata(nil, to: "release_notes.txt", for: metadataLocale) // use nil to copy from en-US. all fields need to be specified. + try? writeFastlaneMetadata(strings["app-store-subtitle"], to: "subtitle.txt", for: metadataLocale) + try? writeFastlaneMetadata(nil, to: "support_url.txt", for: metadataLocale) // use nil to copy from en-US. all fields need to be specified. + } + + } else { + let folderURL = fileURLForFastlaneMetadataFolder(for: locale) + try? fm.removeItem(at: folderURL) + } + + if stringsDict.count > 0 { + stringsDict.write(toFile: stringsDictFilePath, atomically: true) + } else { + pathsForEnglishPlurals.append(stringsDictFilePath) + } + + } + + for stringsDictFilePath in pathsForEnglishPlurals { + englishPluralDictionary?.write(toFile: stringsDictFilePath, atomically: true) + } + + } catch let error { + print("Error importing localizations: \(error)") + abort() + } +} + +// Code that updated source translations +// var replacements = [String: String]() +// for (key, comment) in qqq { +// guard let value = en[key] else { +// continue +// } +// replacements[key] = "WMFLocalizedStringWithDefaultValue(@\"\(key.escapedString)\", nil, NSBundle.mainBundle, @\"\(value.iOSNativeLocalization.escapedString)\", \"\(comment.escapedString)\")" +// } +// +// let codePath = "WMF Framework" +// let contents = try FileManager.default.contentsOfDirectory(atPath: codePath) +// guard let mwLocalizedStringRegex = mwLocalizedStringRegex else { +// abort() +// } +// for filename in contents { +// do { +// let path = codePath + "/" + filename +// let string = try String(contentsOfFile: path) +// //let string = try String(contentsOf: #fileLiteral(resourceName: "WMFContentGroup+WMFFeedContentDisplaying.m")) +// let mutableString = NSMutableString(string: string) +// var offset = 0 +// let fullRange = NSRange(location: 0, length: mutableString.length) +// mwLocalizedStringRegex.enumerateMatches(in: string, options: [], range: fullRange) { (result, flags, stop) in +// guard let result = result else { +// return +// } +// let key = mwLocalizedStringRegex.replacementString(for: result, in: mutableString as String, offset: offset, template: "$1") +// guard let replacement = replacements[key] else { +// return +// } +// let replacementRange = NSRange(location: result.range.location + offset, length: result.range.length) +// mutableString.replaceCharacters(in: replacementRange, with: replacement) +// offset += (replacement as NSString).length - replacementRange.length +// } +// try mutableString.write(toFile: path, atomically: true, encoding: String.Encoding.utf8.rawValue) +// } catch { } +// } diff --git a/Apps/Wikipedia/Command Line Tools/Update Localizations/main.swift b/Apps/Wikipedia/Command Line Tools/Update Localizations/main.swift new file mode 100644 index 0000000..320cf90 --- /dev/null +++ b/Apps/Wikipedia/Command Line Tools/Update Localizations/main.swift @@ -0,0 +1,26 @@ +import Foundation + +/// **THIS IS A COMMAND LINE UTILITY FOR LOCALIZATION** +let count = CommandLine.arguments.count +guard count > 1 else { + print("Please provide a path argument.") + exit(1) +} + +let path = CommandLine.arguments[1] + +print("Extracting localizations from source code...") +let extractProcess = Process.launchedProcess(launchPath: "\(path)/scripts/localization_extract", arguments: [path]) +extractProcess.waitUntilExit() + +if extractProcess.terminationStatus == 0 { + print("Localizations extracted successfully.") + print("Exporting localizations from source code...") + exportLocalizationsFromSourceCode(path) + print("Importing localizations from TWN...") + importLocalizationsFromTWN(path) + print("Localizations imported successfully.") +} else { + print("Failed to extract localizations from source code.") + exit(1) +} diff --git a/Apps/Wikipedia/ContinueReadingWidget/Base.lproj/InfoPlist.strings b/Apps/Wikipedia/ContinueReadingWidget/Base.lproj/InfoPlist.strings new file mode 100644 index 0000000..c0acd07 --- /dev/null +++ b/Apps/Wikipedia/ContinueReadingWidget/Base.lproj/InfoPlist.strings @@ -0,0 +1,4 @@ +/* Localized versions of Info.plist keys */ + +"CFBundleDisplayName" = "Wikipedia continue reading"; +"CFBundleName" = "Wikipedia continue reading"; diff --git a/Apps/Wikipedia/ContinueReadingWidget/Base.lproj/MainInterface.storyboard b/Apps/Wikipedia/ContinueReadingWidget/Base.lproj/MainInterface.storyboard new file mode 100644 index 0000000..12414e2 --- /dev/null +++ b/Apps/Wikipedia/ContinueReadingWidget/Base.lproj/MainInterface.storyboard @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/ContinueReadingWidget/ContinueReadingWidget.entitlements b/Apps/Wikipedia/ContinueReadingWidget/ContinueReadingWidget.entitlements new file mode 100644 index 0000000..67f62b1 --- /dev/null +++ b/Apps/Wikipedia/ContinueReadingWidget/ContinueReadingWidget.entitlements @@ -0,0 +1,16 @@ + + + + + com.apple.developer.associated-domains + + webcredentials:*.wikipedia.org + + com.apple.security.application-groups + + group.org.wikimedia.wikipedia + group.org.wikimedia.wikipedia.alpha + group.org.wikimedia.wikipedia.beta + + + diff --git a/Apps/Wikipedia/ContinueReadingWidget/Info.plist b/Apps/Wikipedia/ContinueReadingWidget/Info.plist new file mode 100644 index 0000000..d7bd544 --- /dev/null +++ b/Apps/Wikipedia/ContinueReadingWidget/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Wikipedia continue reading + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Continue reading + CFBundlePackageType + XPC! + CFBundleShortVersionString + 7.3.0 + CFBundleVersion + 0 + NSExtension + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.widget-extension + + + diff --git a/Apps/Wikipedia/ContinueReadingWidget/WMFTodayContinueReadingWidgetViewController.swift b/Apps/Wikipedia/ContinueReadingWidget/WMFTodayContinueReadingWidgetViewController.swift new file mode 100644 index 0000000..acd701e --- /dev/null +++ b/Apps/Wikipedia/ContinueReadingWidget/WMFTodayContinueReadingWidgetViewController.swift @@ -0,0 +1,173 @@ +import UIKit +import NotificationCenter +import WMF + +@available(*, deprecated, message: "TODO: Rework into iOS 14 home screen widget") +class WMFTodayContinueReadingWidgetViewController: ExtensionViewController, NCWidgetProviding { + @IBOutlet weak var imageView: UIImageView! + + @IBOutlet weak var daysAgoView: UIView! + @IBOutlet weak var daysAgoLabel: UILabel! + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var textLabel: UILabel! + + @IBOutlet weak var emptyView: UIView! + @IBOutlet weak var emptyTitleLabel: UILabel! + @IBOutlet weak var emptyDescriptionLabel: UILabel! + + @IBOutlet var imageWidthConstraint: NSLayoutConstraint! + @IBOutlet var titleLabelTrailingConstraint: NSLayoutConstraint! + + var articleURL: URL? + + override func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + titleLabel.textColor = theme.colors.primaryText + textLabel.textColor = theme.colors.secondaryText + emptyTitleLabel.textColor = theme.colors.primaryText + emptyDescriptionLabel.textColor = theme.colors.secondaryText + daysAgoLabel.textColor = theme.colors.overlayText + daysAgoView.backgroundColor = theme.colors.overlayBackground + } + + override func viewDidLoad() { + super.viewDidLoad() + + imageView.accessibilityIgnoresInvertColors = true + + emptyDescriptionLabel.text = WMFLocalizedString("continue-reading-empty-title", value:"No recently read articles", comment: "No recently read articles") + emptyDescriptionLabel.text = WMFLocalizedString("continue-reading-empty-description", value:"Explore Wikipedia for more articles to read", comment: "Explore Wikipedia for more articles to read") + + view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTapGestureRecognizer(_:)))) + } + + @objc func handleTapGestureRecognizer(_ recognizer: UITapGestureRecognizer) { + switch recognizer.state { + case .ended: + continueReading(self) + default: + break + } + } + + func widgetPerformUpdate(completionHandler: @escaping (NCUpdateResult) -> Void) { + WidgetController.shared.startWidgetUpdateTask(completionHandler) { (dataStore, completion) in + self.updateView(with: dataStore) { didUpdate in + if didUpdate { + completion(.newData) + } else { + completion(.noData) + } + } + } + } + + var emptyViewHidden: Bool = false { + didSet { + emptyView.isHidden = emptyViewHidden + + titleLabel.isHidden = !emptyViewHidden + textLabel.isHidden = !emptyViewHidden + imageView.isHidden = !emptyViewHidden + daysAgoView.isHidden = !emptyViewHidden + } + } + + var collapseImageAndWidenLabels: Bool = true { + didSet { + imageWidthConstraint.constant = collapseImageAndWidenLabels ? 0 : 86 + titleLabelTrailingConstraint.constant = collapseImageAndWidenLabels ? 0 : 10 + self.imageView.alpha = self.collapseImageAndWidenLabels ? 0 : 1 + self.view.layoutIfNeeded() + } + } + + func updateView(with dataStore: MWKDataStore, completion: @escaping (Bool) -> Void) { + let article: WMFArticle + + if let openArticleURL = dataStore.viewContext.openArticleURL, let openArticle = dataStore.fetchArticle(with: openArticleURL) { + article = openArticle + } else if let mostRecentHistoryEntry = dataStore.viewContext.mostRecentlyReadArticle { + article = mostRecentHistoryEntry + } else { + completion(false) + return + } + + let newArticleURL: URL? + if let fragment = article.viewedFragment { + newArticleURL = article.url?.wmf_URL(withFragment: fragment) + } else { + newArticleURL = article.url + } + + guard newArticleURL != nil, newArticleURL?.absoluteString != articleURL?.absoluteString else { + completion(false) + return + } + + articleURL = newArticleURL + + textLabel.text = nil + titleLabel.text = nil + imageView.image = nil + imageView.isHidden = true + daysAgoLabel.text = nil + daysAgoView.isHidden = true + emptyViewHidden = true + + if let subtitle = article.capitalizedWikidataDescriptionOrSnippet { + self.textLabel.text = subtitle + } else { + self.textLabel.text = nil + } + + if let date = article.viewedDate { + self.daysAgoView.isHidden = false + self.daysAgoLabel.text = (date as NSDate).wmf_localizedRelativeDateStringFromLocalDateToNow() + } else { + self.daysAgoView.isHidden = true + } + + self.titleLabel.attributedText = article.displayTitleHTML.byAttributingHTML(with: .headline, matching: traitCollection) + + let combinedCompletion = { + self.updatePreferredContentSize() + completion(true) + } + if let imageURL = article.imageURL(forWidth: self.traitCollection.wmf_nearbyThumbnailWidth) { + self.collapseImageAndWidenLabels = false + self.imageView.wmf_imageController = dataStore.cacheController + self.imageView.wmf_setImage(with: imageURL, detectFaces: true, onGPU: true, failure: { (error) in + self.collapseImageAndWidenLabels = true + combinedCompletion() + }) { + self.collapseImageAndWidenLabels = false + combinedCompletion() + } + } else { + self.collapseImageAndWidenLabels = true + combinedCompletion() + } + } + + func updatePreferredContentSize() { + var fitSize = UIView.layoutFittingCompressedSize + fitSize.width = view.bounds.size.width + fitSize = view.systemLayoutSizeFitting(fitSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.defaultLow) + preferredContentSize = fitSize + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updatePreferredContentSize() + } + + @IBAction func continueReading(_ sender: AnyObject) { + openApp(with: articleURL) + } +} + diff --git a/Apps/Wikipedia/ContinueReadingWidget/en.lproj/InfoPlist.strings b/Apps/Wikipedia/ContinueReadingWidget/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..c0acd07 --- /dev/null +++ b/Apps/Wikipedia/ContinueReadingWidget/en.lproj/InfoPlist.strings @@ -0,0 +1,4 @@ +/* Localized versions of Info.plist keys */ + +"CFBundleDisplayName" = "Wikipedia continue reading"; +"CFBundleName" = "Wikipedia continue reading"; diff --git a/Apps/Wikipedia/Gemfile b/Apps/Wikipedia/Gemfile new file mode 100644 index 0000000..60c66f9 --- /dev/null +++ b/Apps/Wikipedia/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +group :ci do + gem 'fastlane' + gem 'xcode-install' +end diff --git a/Apps/Wikipedia/Gemfile.lock b/Apps/Wikipedia/Gemfile.lock new file mode 100644 index 0000000..5cdd541 --- /dev/null +++ b/Apps/Wikipedia/Gemfile.lock @@ -0,0 +1,222 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.6) + rexml + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) + artifactory (3.0.15) + atomos (0.1.3) + aws-eventstream (1.2.0) + aws-partitions (1.710.0) + aws-sdk-core (3.170.0) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.62.0) + aws-sdk-core (~> 3, >= 3.165.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.119.0) + aws-sdk-core (~> 3, >= 3.165.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.4) + aws-sigv4 (1.5.2) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + declarative (0.0.20) + digest-crc (0.6.4) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.99.0) + faraday (1.10.3) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) + faraday (~> 1.0) + fastimage (2.2.6) + fastlane (2.211.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander (~> 4.6) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (~> 2.0.0) + naturally (~> 2.2) + optparse (~> 0.1.1) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.34.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-core (0.11.0) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + rexml + webrick + google-apis-iamcredentials_v1 (0.16.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-playcustomapp_v1 (0.12.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-storage_v1 (0.19.0) + google-apis-core (>= 0.9.0, < 2.a) + google-cloud-core (1.6.0) + google-cloud-env (~> 1.0) + google-cloud-errors (~> 1.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.3.0) + google-cloud-storage (1.44.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.19.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) + mini_mime (~> 1.0) + googleauth (1.3.0) + faraday (>= 0.17.3, < 3.a) + jwt (>= 1.4, < 3.0) + memoist (~> 0.16) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.5) + domain_name (~> 0.5) + httpclient (2.8.3) + jmespath (1.6.2) + json (2.6.3) + jwt (2.7.0) + memoist (0.16.2) + mini_magick (4.12.0) + mini_mime (1.1.2) + multi_json (1.15.0) + multipart-post (2.0.0) + nanaimo (0.3.0) + naturally (2.2.1) + optparse (0.1.1) + os (1.1.4) + plist (3.6.0) + public_suffix (5.0.1) + rake (13.0.6) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rexml (3.2.5) + rouge (2.0.7) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + security (0.1.3) + signet (0.17.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + terminal-notifier (2.0.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + trailblazer-option (0.1.2) + tty-cursor (0.7.1) + tty-screen (0.8.1) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.8.2) + unicode-display_width (1.8.0) + webrick (1.8.1) + word_wrap (1.0.0) + xcode-install (2.8.1) + claide (>= 0.9.1) + fastlane (>= 2.1.0, < 3.0.0) + xcodeproj (1.22.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + ruby + +DEPENDENCIES + fastlane + xcode-install + +BUNDLED WITH + 2.4.6 diff --git a/Apps/Wikipedia/LICENSE.txt b/Apps/Wikipedia/LICENSE.txt new file mode 100644 index 0000000..cf9dbd4 --- /dev/null +++ b/Apps/Wikipedia/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013–2020 Wikimedia Foundation et al. + +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. diff --git a/Apps/Wikipedia/NotificationServiceExtension/Info.plist b/Apps/Wikipedia/NotificationServiceExtension/Info.plist new file mode 100644 index 0000000..1583590 --- /dev/null +++ b/Apps/Wikipedia/NotificationServiceExtension/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + NotificationServiceExtension + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 7.3.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).NotificationService + + + diff --git a/Apps/Wikipedia/NotificationServiceExtension/NotificationService.swift b/Apps/Wikipedia/NotificationServiceExtension/NotificationService.swift new file mode 100644 index 0000000..eef792f --- /dev/null +++ b/Apps/Wikipedia/NotificationServiceExtension/NotificationService.swift @@ -0,0 +1,105 @@ +import UserNotifications +import WMF + +class NotificationService: UNNotificationServiceExtension { + + var contentHandler: ((UNNotificationContent) -> Void)? + var bestAttemptContent: UNMutableNotificationContent? + + private lazy var apiController: RemoteNotificationsAPIController = { + let configuration = Configuration.current + let session = Session(configuration: configuration) + let controller = RemoteNotificationsAPIController(session: session, configuration: configuration) + return controller + }() + private let sharedCache = SharedContainerCache.init(fileName: SharedContainerCacheCommonNames.pushNotificationsCache, defaultCache: { PushNotificationsCache(settings: .default, notifications: []) }) + + private let fallbackPushContent = WMFLocalizedString("notifications-push-fallback-body-text", value: "New activity on Wikipedia", comment: "Fallback body content of a push notification whose content cannot be determined. Could be either due multiple notifications represented or errors.") + + override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { + self.contentHandler = contentHandler + + guard let bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent else { + contentHandler(request.content) + return + } + + self.bestAttemptContent = bestAttemptContent + + guard bestAttemptContent.body == EchoModelVersion.current else { + bestAttemptContent.body = fallbackPushContent + contentHandler(bestAttemptContent) + return + } + + let cache = sharedCache.loadCache() + let project = WikimediaProject.wikipedia(cache.settings.primaryLanguageCode, cache.settings.primaryLocalizedName, nil) + + let fallbackPushContent = self.fallbackPushContent + + apiController.getUnreadPushNotifications(from: project) { [weak self] fetchedNotifications, error in + + DispatchQueue.main.async { + guard let self = self, + error == nil else { + bestAttemptContent.body = fallbackPushContent + contentHandler(bestAttemptContent) + return + } + + let finalNotifications = NotificationServiceHelper.determineNotificationsToDisplayAndCache(fetchedNotifications: fetchedNotifications, cachedNotifications: cache.notifications) + let finalNotificationsToDisplay = finalNotifications.notificationsToDisplay + let finalNotificationsToCache = finalNotifications.notificationsToCache + + var newCache = cache + newCache.notifications = finalNotificationsToCache + self.sharedCache.saveCache(newCache) + + // specific handling for talk page types (New messages title, bundled body) + if let talkPageContent = NotificationServiceHelper.talkPageContent(for: finalNotificationsToDisplay) { + bestAttemptContent.subtitle = talkPageContent.subtitle + bestAttemptContent.body = talkPageContent.body + } else if finalNotificationsToDisplay.count == 1, + let pushContentText = finalNotificationsToDisplay.first?.pushContentText { + bestAttemptContent.body = pushContentText + } else { + bestAttemptContent.body = fallbackPushContent + } + + // Assigning interruption level and relevance score only available starting on iOS 15 + if #available(iOS 15.0, *) { + if finalNotifications.notificationsToDisplay.count == 1, let notification = finalNotifications.notificationsToDisplay.first { + let priority = RemoteNotification.typeFrom(notification: notification).priority + bestAttemptContent.interruptionLevel = priority.interruptionLevel + bestAttemptContent.relevanceScore = priority.relevanceScore + } else { + if NotificationServiceHelper.allNotificationsAreForSameTalkPage(notifications: finalNotificationsToDisplay) { + bestAttemptContent.interruptionLevel = RemoteNotificationType.mentionInTalkPage.priority.interruptionLevel + bestAttemptContent.relevanceScore = RemoteNotificationType.mentionInTalkPage.priority.relevanceScore + } else { + bestAttemptContent.interruptionLevel = RemoteNotificationType.bulkPriority.interruptionLevel + bestAttemptContent.relevanceScore = RemoteNotificationType.bulkPriority.relevanceScore + } + } + } + + let displayContentIdentifiers = finalNotificationsToDisplay.compactMap { PushNotificationContentIdentifier(key: $0.key, date: $0.date) } + PushNotificationContentIdentifier.save(displayContentIdentifiers, to: &bestAttemptContent.userInfo) + + bestAttemptContent.badge = NSNumber(value: newCache.currentUnreadCount + finalNotificationsToDisplay.count) + + contentHandler(bestAttemptContent) + } + } + } + + override func serviceExtensionTimeWillExpire() { + // Called just before the extension will be terminated by the system. + // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. + if let contentHandler = contentHandler, + let bestAttemptContent = bestAttemptContent { + bestAttemptContent.body = fallbackPushContent + contentHandler(bestAttemptContent) + } + } +} diff --git a/Apps/Wikipedia/NotificationServiceExtension/NotificationServiceExtension.entitlements b/Apps/Wikipedia/NotificationServiceExtension/NotificationServiceExtension.entitlements new file mode 100644 index 0000000..e3d4cc9 --- /dev/null +++ b/Apps/Wikipedia/NotificationServiceExtension/NotificationServiceExtension.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.application-groups + + group.org.wikimedia.wikipedia + group.org.wikimedia.wikipedia.alpha + group.org.wikimedia.wikipedia.beta + + + diff --git a/Apps/Wikipedia/README.md b/Apps/Wikipedia/README.md new file mode 100644 index 0000000..af2066e --- /dev/null +++ b/Apps/Wikipedia/README.md @@ -0,0 +1,84 @@ +# Wikipedia iOS +The official Wikipedia iOS app. + +[![Wikipedia](https://circleci.com/gh/wikimedia/wikipedia-ios.svg?style=shield)](https://github.com/wikimedia/wikipedia-ios) +[![MIT license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://raw.githubusercontent.com/wikimedia/wikipedia-ios/main/LICENSE.txt) + +* **License**: MIT License +* **Source repo**: https://github.com/wikimedia/wikipedia-ios +* **Planning (bugs & features)**: https://phabricator.wikimedia.org/project/view/782/ +* **Team page**: https://www.mediawiki.org/wiki/Wikimedia_Apps/Team/iOS + +**Note: The latest `main` branch is set up to build with Xcode 14.2.0.** + +## Building and Running + +In the directory, run `./scripts/setup`. Note: going to `scripts` directory and running `setup` will not work due to relative paths. + +Running `scripts/setup` will setup your computer to build and run the app. The script assumes you have Xcode installed already. It will install [homebrew](https://brew.sh), [SwiftLint](https://github.com/realm/SwiftLint), and [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html). It will also create a pre-commit hook that uses ClangFormat for linting Objective-C code. + +After running `scripts/setup`, you should be able to open `Wikipedia.xcodeproj` and run the app on the iOS Simulator (using the **Wikipedia** scheme and target). If you encounter any issues, please don't hesitate to let us know via a [bug report](https://phabricator.wikimedia.org/maniphest/task/edit/form/1/?title=[BUG]&projects=wikipedia-ios-app-product-backlog,ios-app-bugs&description=%3D%3D%3D+How+many+times+were+you+able+to+reproduce+it?%0D%0A%0D%0A%3D%3D%3D+Steps+to+reproduce%0D%0A%23+%0D%0A%23+%0D%0A%23+%0D%0A%0D%0A%3D%3D%3D+Expected+results%0D%0A%0D%0A%3D%3D%3D+Actual+results%0D%0A%0D%0A%3D%3D%3D+Screenshots%0D%0A%0D%0A%3D%3D%3D+Environments+observed%0D%0A**App+version%3A+**+%0D%0A**OS+versions%3A**+%0D%0A**Device+model%3A**+%0D%0A**Device+language%3A**+%0D%0A%0D%0A%3D%3D%3D+Regression?+%0D%0A%0D%0A+Tag++task+with+%23Regression+%0A) or messaging us on IRC in #wikimedia-mobile on Freenode. + +### Required Dependencies +If you'd rather install the development prerequisites yourself without our script: + +* [**Xcode**](https://itunes.apple.com/us/app/xcode/id497799835) - The easiest way to get Xcode is from the [App Store](https://itunes.apple.com/us/app/xcode/id497799835?mt=12), but you can also download it from [developer.apple.com](https://developer.apple.com/) if you have an Apple ID registered with an Apple Developer account. +* [**SwiftLint**](https://github.com/realm/SwiftLint) - We use this for linting Swift code. +* [**ClangFormat**](https://clang.llvm.org/docs/ClangFormat.html) - We use this for linting Objective-C code. + +## Contributing +Covered in the [contributing document](CONTRIBUTING.md). + +## Development Guidelines +These are general guidelines rather than hard rules. + +### Coding Guidelines +- **Objective-C** - [Apple's Coding Guidelines for Cocoa](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html) +- **Swift** - [swift.org API Design Guidelines](https://swift.org/documentation/api-design-guidelines/) + +### Formatting +We use Xcode's default 4 space indentation and our `.clang-format` file with the pre-commit hook setup by `scripts/setup`. Where possible, our Swift code is automatically formatted by [SwiftLint](https://github.com/realm/SwiftLint) based on the rules defined in `.swiftlint-autocorrect.yml`. + +### Process and Code Review Norms +Covered in the [process document](docs/process.md). + +### Logging +When reading logs, note that the log levels are shortened to emoji. +- 🗣️ Verbose +- 💬 Debug +- ℹ️ Info +- ⚠️ Warning +- 🚨 Error + +### Testing +The **Wikipedia** scheme is configured to execute the project's iOS unit tests, which can be run using the `Cmd+U` hotkey or the **Product → Test** menu bar action. In order for the tests to pass, the test device's language and region must be set to `en-US` in Settings → General → Language & Region. There is a [ticket filed](https://phabricator.wikimedia.org/T259859) to update the tests to pass regardless of language and region. + +### Schemes and Targets +* **Wikipedia** - Points to production servers. +* **Staging** - Pushed to TestFlight as a separate app bundle, and has the ability to toggle different staging environments within the `current` [property](https://github.com/wikimedia/wikipedia-ios/blob/de349525f652ca59c3437cd36fcb13846d737f1e/WMF%20Framework/Configuration.swift#L41) of `Configuration`: + - An option of `appsLabsForPCS` will point to the [Apps team's staging environment](https://mobileapps.wmflabs.org) for page content. + - An option of `deploymentLabsForEventLogging` will point to the [Event Logging](https://wikitech.wikimedia.org/wiki/Analytics/Systems/EventLogging) staging environment. It is for testing analytics events that the app sends to Event Logging. + - An option of `betaCluster` will point to the [MediaWiki beta cluster environment](https://www.mediawiki.org/wiki/Beta_Cluster) for most API calls. This is meant to be a more blanket environment setting, so if this value exists it will also force the beta cluster environment for page content on the article view as well as force the staging environment for event logging. This beta cluster environment is also where developers can test sandbox push notifications triggered across various wikis. This is selected by default. +* **Local Page Content Service and Announcements** - used in Debug mode only, has the ability to toggle different local environments within the `current` [property](https://github.com/wikimedia/wikipedia-ios/blob/de349525f652ca59c3437cd36fcb13846d737f1e/WMF%20Framework/Configuration.swift#L41) of `Configuration`: + - An option of `localPCS` will point to a locally running [mobileapps](https://gerrit.wikimedia.org/r/q/project:mediawiki%252Fservices%252Fmobileapps) repository for page content. This is selected by default. + - An option of `localAnnouncements` will point to a locally running [wikifeeds](https://gerrit.wikimedia.org/r/q/project:mediawiki%252Fservices%252Fwikifeeds) repository for the announcements endpoint. This is selected by default. + - All other endpoints will point to production. +* **RTL** - Launches the app in an RTL locale using the `-AppleLocale` argument. +* **Experimental** - For one off builds. Can point to whatever is needed for the given experiment. Pushed to TestFlight as a separate app bundle. +* **User Testing** - For user testing. Has an alternate configuration so that it can be delivered ad hoc. Pushed to TestFlight as a separate app bundle. +* **WMF** - Bundles up the app logic shared between the main app and the extensions (widgets, notifications). +* **Update Localizations** - Covered in the [localization document](docs/localization.md). +* **Update Languages** - For adding new Wikipedia languages or updating language configurations. Covered in the [languages document](docs/languages.md). +* **{{name}}Widget, {{name}}Notification, {{name}}Stickers** - Extensions for widgets, notifications, and stickers. + +### Continuous Integration +Covered in the [CI document](docs/ci.md). + +### Event Logging +Covered in the [event logging document](docs/event_logging.md). + +### Web Development +The article view and several other components of the app rely on web components. Instructions for working on these components is covered in the [web development document](docs/web_dev.md). + +### Contact Us +If you have any questions or comments, you can email us at mobile-ios-wikipedia[at]wikimedia dot org. We'll also gladly accept any [bug reports](https://phabricator.wikimedia.org/maniphest/task/edit/form/1/?title=[BUG]&projects=wikipedia-ios-app-product-backlog,ios-app-bugs&description=%3D%3D%3D+How+many+times+were+you+able+to+reproduce+it?%0D%0A%0D%0A%3D%3D%3D+Steps+to+reproduce%0D%0A%23+%0D%0A%23+%0D%0A%23+%0D%0A%0D%0A%3D%3D%3D+Expected+results%0D%0A%0D%0A%3D%3D%3D+Actual+results%0D%0A%0D%0A%3D%3D%3D+Screenshots%0D%0A%0D%0A%3D%3D%3D+Environments+observed%0D%0A**App+version%3A+**+%0D%0A**OS+versions%3A**+%0D%0A**Device+model%3A**+%0D%0A**Device+language%3A**+%0D%0A**App+language%3A**+%0D%0A%0D%0A%3D%3D%3D+Regression?+%0D%0A%0D%0A+Tag++task+with+%23Regression+%0A). diff --git a/Apps/Wikipedia/WMF Framework/ABTestsController.swift b/Apps/Wikipedia/WMF Framework/ABTestsController.swift new file mode 100644 index 0000000..627af18 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ABTestsController.swift @@ -0,0 +1,123 @@ +import Foundation + +@objc public protocol ABTestsPersisting: AnyObject { + func libraryValue(for key: String) -> NSCoding? + func setLibraryValue(_ value: NSCoding?, for key: String) +} + +@objc(WMFABTestsController) +public class ABTestsController: NSObject { + + enum ABTestsError: Error { + case invalidPercentage + } + + struct ExperimentConfig { + let experiment: Experiment + let percentageKey: PercentageKey + let bucketKey: BucketKey + let bucketValueControl: BucketValue + let bucketValueTest: BucketValue + } + + public enum Experiment { + case articleAsLivingDoc + + var config: ExperimentConfig { + switch self { + case .articleAsLivingDoc: + return ABTestsController.articleAsLivingDocConfig + } + } + } + + public enum PercentageKey: String { + case articleAsLivingDocPercentKey + } + + enum BucketKey: String { + case articleAsLivingDocBucketKey + } + + public enum BucketValue: String { + case articleAsLivingDocTest = "LivingDoc_Test" + case articleAsLivingDocControl = "LivingDoc_Control" + } + + private static let articleAsLivingDocConfig = ExperimentConfig(experiment: .articleAsLivingDoc, percentageKey: .articleAsLivingDocPercentKey, bucketKey: .articleAsLivingDocBucketKey, bucketValueControl: .articleAsLivingDocControl, bucketValueTest: .articleAsLivingDocTest) + + private let persistanceService: ABTestsPersisting + + @objc public init(persistanceService: ABTestsPersisting) { + self.persistanceService = persistanceService + super.init() + } + + // this will only generate a new bucket as needed (i.e. if the percentage is different than the last time bucket was generated) + @discardableResult + func determineBucketForExperiment(_ experiment: Experiment, withPercentage percentage: NSNumber) throws -> BucketValue { + + guard percentage.intValue >= 0 && percentage.intValue <= 100 else { + throw ABTestsError.invalidPercentage + } + + // if we have previously generated a bucket with the same percentage, return that value + let maybeOldPercentage = percentageForExperiment(experiment) + let maybeOldBucket = bucketForExperiment(experiment) + + if let oldPercentage = maybeOldPercentage, + let oldBucket = maybeOldBucket, + oldPercentage == percentage { + return oldBucket + } + + // otherwise generate new bucket + let randomInt = Int.random(in: 1...100) + let isInTest = randomInt <= percentage.intValue + let bucket: BucketValue + + switch experiment { + case .articleAsLivingDoc: + bucket = isInTest ? .articleAsLivingDocTest : .articleAsLivingDocControl + } + + setBucket(bucket, forExperiment: experiment) + try setPercentage(percentage, forExperiment: experiment) + + return bucket + } + + // MARK: Persistence setters/getters + + func percentageForExperiment(_ experiment: Experiment) -> NSNumber? { + + let key = experiment.config.percentageKey.rawValue + return persistanceService.libraryValue(for: key) as? NSNumber + } + + func setPercentage(_ percentage: NSNumber, forExperiment experiment: Experiment) throws { + + guard percentage.intValue >= 0 && percentage.intValue <= 100 else { + throw ABTestsError.invalidPercentage + } + + let key = experiment.config.percentageKey.rawValue + persistanceService.setLibraryValue(percentage, for: key) + } + + public func bucketForExperiment(_ experiment: Experiment) -> BucketValue? { + + let key = experiment.config.bucketKey.rawValue + guard let rawValue = persistanceService.libraryValue(for: key) as? String else { + return nil + } + + return BucketValue(rawValue: rawValue) + } + + func setBucket(_ bucket: BucketValue, forExperiment experiment: Experiment) { + + let key = experiment.config.bucketKey.rawValue + persistanceService.setLibraryValue((bucket.rawValue as NSString), for: key) + } +} diff --git a/Apps/Wikipedia/WMF Framework/ActionButton.swift b/Apps/Wikipedia/WMF Framework/ActionButton.swift new file mode 100644 index 0000000..84d8052 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ActionButton.swift @@ -0,0 +1,44 @@ +import UIKit + +class ActionButton: SetupButton { + + var titleLabelFont = DynamicTextStyle.semiboldSubheadline + + override func setup() { + super.setup() + contentEdgeInsets = UIEdgeInsets(top: layoutMargins.top + 1, left: layoutMargins.left + 7, bottom: layoutMargins.bottom + 1, right: layoutMargins.right + 7) + titleLabel?.numberOfLines = 0 + updateFonts(with: traitCollection) + } + + // MARK: - Dynamic Type + // Only applies new fonts if the content size category changes + + override open func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + maybeUpdateFonts(with: traitCollection) + } + + var contentSizeCategory: UIContentSizeCategory? + fileprivate func maybeUpdateFonts(with traitCollection: UITraitCollection) { + guard contentSizeCategory == nil || contentSizeCategory != traitCollection.wmf_preferredContentSizeCategory else { + return + } + contentSizeCategory = traitCollection.wmf_preferredContentSizeCategory + updateFonts(with: traitCollection) + } + + // Override this method and call super + open func updateFonts(with traitCollection: UITraitCollection) { + titleLabel?.font = UIFont.wmf_font(titleLabelFont, compatibleWithTraitCollection: traitCollection) + } +} + +extension ActionButton: Themeable { + func apply(theme: Theme) { + setTitleColor(theme.colors.link, for: .normal) + backgroundColor = theme.colors.cardButtonBackground + layer.cornerRadius = 5 + } + +} diff --git a/Apps/Wikipedia/WMF Framework/AnnouncementType.swift b/Apps/Wikipedia/WMF Framework/AnnouncementType.swift new file mode 100644 index 0000000..0d930e2 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/AnnouncementType.swift @@ -0,0 +1,19 @@ +import Foundation + +public enum AnnouncementType: String { + case fundraising + case survey + case announcement + case unknown +} + +public extension WMFAnnouncement { + var announcementType: AnnouncementType { + + guard let type = type else { + return .unknown + } + + return AnnouncementType(rawValue: type) ?? .unknown + } +} diff --git a/Apps/Wikipedia/WMF Framework/Array+SafeIndex.swift b/Apps/Wikipedia/WMF Framework/Array+SafeIndex.swift new file mode 100644 index 0000000..3974c2f --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Array+SafeIndex.swift @@ -0,0 +1,8 @@ +public extension Array { + subscript(safeIndex index: Int) -> Element? { + guard index >= 0, index < endIndex else { + return nil + } + return self[index] + } +} diff --git a/Apps/Wikipedia/WMF Framework/ArticleCacheController.swift b/Apps/Wikipedia/WMF Framework/ArticleCacheController.swift new file mode 100644 index 0000000..1e8b6b3 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ArticleCacheController.swift @@ -0,0 +1,208 @@ +import Foundation + +public final class ArticleCacheController: CacheController { + + init(moc: NSManagedObjectContext, imageCacheController: ImageCacheController, session: Session, configuration: Configuration, preferredLanguageDelegate: WMFPreferredLanguageInfoProvider) { + let articleFetcher = ArticleFetcher(session: session, configuration: configuration) + let imageInfoFetcher = MWKImageInfoFetcher(session: session, configuration: configuration) + imageInfoFetcher.preferredLanguageDelegate = preferredLanguageDelegate + let cacheFileWriter = CacheFileWriter(fetcher: articleFetcher) + + let articleDBWriter = ArticleCacheDBWriter(articleFetcher: articleFetcher, cacheBackgroundContext: moc, imageController: imageCacheController, imageInfoFetcher: imageInfoFetcher) + super.init(dbWriter: articleDBWriter, fileWriter: cacheFileWriter) + } + + enum ArticleCacheControllerError: Error { + case invalidDBWriterType + } + + // syncs already cached resources with mobile-html-offline-resources and media-list endpoints (caches new urls, removes old urls) + public func syncCachedResources(url: URL, groupKey: CacheController.GroupKey, groupCompletion: @escaping GroupCompletionBlock) { + + guard let articleDBWriter = dbWriter as? ArticleCacheDBWriter else { + groupCompletion(.failure(error: ArticleCacheControllerError.invalidDBWriterType)) + return + } + + articleDBWriter.syncResources(url: url, groupKey: groupKey) { (result) in + switch result { + case .success(let syncResult): + + let group = DispatchGroup() + + var successfulAddKeys: [CacheController.UniqueKey] = [] + var failedAddKeys: [(CacheController.UniqueKey, Error)] = [] + var successfulRemoveKeys: [CacheController.UniqueKey] = [] + var failedRemoveKeys: [(CacheController.UniqueKey, Error)] = [] + + // add new urls in file system + for urlRequest in syncResult.addURLRequests { + + guard let uniqueKey = self.fileWriter.uniqueFileNameForURLRequest(urlRequest), urlRequest.url != nil else { + continue + } + + group.enter() + + self.fileWriter.add(groupKey: groupKey, urlRequest: urlRequest) { (fileWriterResult) in + switch fileWriterResult { + case .success(let response, _): + + self.dbWriter.markDownloaded(urlRequest: urlRequest, response: response) { (dbWriterResult) in + + defer { + group.leave() + } + + switch dbWriterResult { + case .success: + successfulAddKeys.append(uniqueKey) + case .failure(let error): + failedAddKeys.append((uniqueKey, error)) + } + } + case .failure(let error): + + defer { + group.leave() + } + + failedAddKeys.append((uniqueKey, error)) + } + } + } + + // remove old urls in file system + for key in syncResult.removeItemKeyAndVariants { + + guard let uniqueKey = self.fileWriter.uniqueFileNameForItemKey(key.itemKey, variant: key.variant) else { + continue + } + + group.enter() + + self.fileWriter.remove(itemKey: key.itemKey, variant: key.variant) { (fileWriterResult) in + + switch fileWriterResult { + case .success: + + self.dbWriter.remove(itemAndVariantKey: key) { (dbWriterResult) in + + defer { + group.leave() + } + + switch dbWriterResult { + case .success: + successfulRemoveKeys.append(uniqueKey) + case .failure(let error): + failedRemoveKeys.append((uniqueKey, error)) + } + } + case .failure(let error): + defer { + group.leave() + } + + failedRemoveKeys.append((uniqueKey, error)) + } + } + } + + group.notify(queue: DispatchQueue.global(qos: .userInitiated)) { + if let error = failedAddKeys.first?.1 ?? failedRemoveKeys.first?.1 { + groupCompletion(.failure(error: CacheControllerError.atLeastOneItemFailedInSync(error))) + return + } + + let successKeys = successfulAddKeys + successfulRemoveKeys + groupCompletion(.success(uniqueKeys: successKeys)) + } + + case .failure(let error): + groupCompletion(.failure(error: error)) + } + } + } + + public func cacheFromMigration(desktopArticleURL: URL, content: String, completionHandler: @escaping ((Error?) -> Void)) { // articleURL should be desktopURL + + guard let articleDBWriter = dbWriter as? ArticleCacheDBWriter else { + completionHandler(ArticleCacheControllerError.invalidDBWriterType) + return + } + + cacheBundledResourcesIfNeeded(desktopArticleURL: desktopArticleURL) { (cacheBundledError) in + + articleDBWriter.addMobileHtmlURLForMigration(desktopArticleURL: desktopArticleURL, success: { urlRequest in + + self.fileWriter.addMobileHtmlContentForMigration(content: content, urlRequest: urlRequest, success: { + + articleDBWriter.markDownloaded(urlRequest: urlRequest, response: nil) { (result) in + switch result { + case .success: + + if cacheBundledError == nil { + completionHandler(nil) + } else { + completionHandler(cacheBundledError) + } + + case .failure(let error): + completionHandler(error) + } + } + }) { (error) in + completionHandler(error) + } + }) { (error) in + completionHandler(error) + } + } + } + + private func bundledResourcesAreCached() -> Bool { + guard let articleDBWriter = dbWriter as? ArticleCacheDBWriter else { + return false + } + + return articleDBWriter.bundledResourcesAreCached() + } + + private func cacheBundledResourcesIfNeeded(desktopArticleURL: URL, completionHandler: @escaping ((Error?) -> Void)) { // articleURL should be desktopURL + + guard let articleDBWriter = dbWriter as? ArticleCacheDBWriter else { + completionHandler(ArticleCacheControllerError.invalidDBWriterType) + return + } + + if !articleDBWriter.bundledResourcesAreCached() { + articleDBWriter.addBundledResourcesForMigration(desktopArticleURL: desktopArticleURL) { (result) in + + switch result { + case .success(let requests): + + self.fileWriter.addBundledResourcesForMigration(urlRequests: requests, success: { (_) in + + let bulkRequests = requests.map { ArticleCacheDBWriter.BulkMarkDownloadRequest(urlRequest: $0, response: nil) } + articleDBWriter.markDownloaded(requests: bulkRequests) { (result) in + switch result { + case .success: + completionHandler(nil) + case .failure(let error): + completionHandler(error) + } + } + + }) { (error) in + completionHandler(error) + } + case .failure(let error): + completionHandler(error) + } + } + } else { + completionHandler(nil) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/ArticleCacheDBWriter+SyncResources.swift b/Apps/Wikipedia/WMF Framework/ArticleCacheDBWriter+SyncResources.swift new file mode 100644 index 0000000..53323a1 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ArticleCacheDBWriter+SyncResources.swift @@ -0,0 +1,244 @@ +import Foundation + +enum ArticleCacheDBWriterSyncError: Error { + case missingMOC + case cannotFindCacheGroup + case cannotFindCacheItem + case unableToDetermineItemKey + case failureFetchOrCreateCacheItem + case failureToCreateNetworkItem +} + +struct CacheDBWritingSyncSuccessResult { + let addURLRequests: [URLRequest] + let removeItemKeyAndVariants: [CacheController.ItemKeyAndVariant] +} + +extension ArticleCacheDBWriter { + + private struct NetworkItem: Hashable { + let itemKeyAndVariant: CacheController.ItemKeyAndVariant + let url: URL? + let urlRequest: URLRequest? + let cacheItem: CacheItem? + + func hash(into hasher: inout Hasher) { + hasher.combine(itemKeyAndVariant) + } + + static func ==(lhs: NetworkItem, rhs: NetworkItem) -> Bool { + return lhs.itemKeyAndVariant == rhs.itemKeyAndVariant + } + } + + // adds new resources to DB, returns old resources in completion + func syncResources(url: URL, groupKey: CacheController.GroupKey, completion: @escaping (Result) -> Void) { + + let mobileHTMLURL: URL + let mediaListURL: URL + let mobileHTMLRequest: URLRequest + let mediaListRequest: URLRequest + do { + mobileHTMLURL = try articleFetcher.mobileHTMLURL(articleURL: url) + mediaListURL = try articleFetcher.mediaListURL(articleURL: url) + mobileHTMLRequest = try articleFetcher.mobileHTMLRequest(articleURL: url) + mediaListRequest = try articleFetcher.mobileHTMLMediaListRequest(articleURL: url) + } catch let error { + completion(.failure(error)) + return + } + + let mobileHTMLItemKey = articleFetcher.itemKeyForURL(mobileHTMLURL, type: .article) + let mobileHTMLVariant = articleFetcher.variantForURL(mobileHTMLURL, type: .article) + let mediaListItemKey = articleFetcher.itemKeyForURL(mediaListURL, type: .article) + let mediaListVariant = articleFetcher.variantForURL(mediaListURL, type: .article) + + guard let mobileHTMLItemKeyAndVariant = CacheController.ItemKeyAndVariant(itemKey: mobileHTMLItemKey, variant: mobileHTMLVariant), + let mediaListItemKeyAndVariant = CacheController.ItemKeyAndVariant(itemKey: mediaListItemKey, variant: mediaListVariant) else { + completion(.failure(ArticleCacheDBWriterSyncError.failureToCreateNetworkItem)) + return + } + + let mobileHTMLNetworkItem = NetworkItem(itemKeyAndVariant: mobileHTMLItemKeyAndVariant, url: mobileHTMLURL, urlRequest: mobileHTMLRequest, cacheItem: nil) + let mediaListNetworkItem = NetworkItem(itemKeyAndVariant: mediaListItemKeyAndVariant, url: mediaListURL, urlRequest: mediaListRequest, cacheItem: nil) + + fetchImageAndResourceURLsForArticleURL(url, groupKey: groupKey) { [weak self] (result) in + + guard let self = self else { + return + } + + switch result { + case .success(let urls): + + // package offline resource urls into NetworkItems + let offlineResourceItems = urls.offlineResourcesURLs.compactMap { (url) -> NetworkItem? in + guard let itemKey = self.articleFetcher.itemKeyForURL(url, type: .article) else { + return nil + } + + let variant = self.articleFetcher.variantForURL(url, type: .article) + + guard let itemKeyAndVariant = CacheController.ItemKeyAndVariant(itemKey: itemKey, variant: variant), + let urlRequest = self.articleFetcher.urlRequestFromPersistence(with: url, persistType: .article) else { + return nil + } + + return NetworkItem(itemKeyAndVariant: itemKeyAndVariant, url: url, urlRequest: urlRequest, cacheItem: nil) + } + + // begin package media list urls into NetworkItems + let mediaListItems = urls.mediaListURLs.compactMap { (url) -> NetworkItem? in + guard let itemKey = self.articleFetcher.itemKeyForURL(url, type: .image) else { + return nil + } + + let variant = self.articleFetcher.variantForURL(url, type: .image) + + guard let itemKeyAndVariant = CacheController.ItemKeyAndVariant(itemKey: itemKey, variant: variant), + let urlRequest = self.articleFetcher.urlRequestFromPersistence(with: url, persistType: .image) else { + return nil + } + + return NetworkItem(itemKeyAndVariant: itemKeyAndVariant, url: url, urlRequest: urlRequest, cacheItem: nil) + } + + // group into dictionary of the same itemKey for use in filtering out variants + var similarItemsDictionary: [CacheController.ItemKey: [NetworkItem]] = [:] + for item in mediaListItems { + if var existingItems = similarItemsDictionary[item.itemKeyAndVariant.itemKey] { + existingItems.append(item) + similarItemsDictionary[item.itemKeyAndVariant.itemKey] = existingItems + } else { + similarItemsDictionary[item.itemKeyAndVariant.itemKey] = [item] + } + } + + // filter out any variants that image cache controller should not download (i.e. multiple sizes of the same image) + var finalMediaListItems: [NetworkItem] = [] + for item in mediaListItems { + guard let similarItems = similarItemsDictionary[item.itemKeyAndVariant.itemKey] else { + continue + } + + let allVariantItems = similarItems.map { $0.itemKeyAndVariant } + if self.imageController.shouldDownloadVariantForAllVariantItems(variant: item.itemKeyAndVariant.variant, allVariantItems) { + finalMediaListItems.append(item) + } + } + // end package media list urls into NetworkItems + + // begin image info urls into NetworkItems + let imageInfoItems = urls.imageInfoURLs.compactMap { (url) -> NetworkItem? in + guard let itemKey = self.articleFetcher.itemKeyForURL(url, type: .imageInfo) else { + return nil + } + + let variant = self.articleFetcher.variantForURL(url, type: .imageInfo) + guard let itemKeyAndVariant = CacheController.ItemKeyAndVariant(itemKey: itemKey, variant: variant), + let urlRequest = self.articleFetcher.urlRequestFromPersistence(with: url, persistType: .imageInfo) else { + return nil + } + + return NetworkItem(itemKeyAndVariant: itemKeyAndVariant, url: url, urlRequest: urlRequest, cacheItem: nil) + } + + // consolidate list of network items to compare with downloaded cached items + // remove list also contains mobile-html & media-list urls for comparison purposes only, otherwise it will think they should be removed from cache. + let networkItemsForAdd: Set = Set(offlineResourceItems + finalMediaListItems + imageInfoItems) + let networkItemsForRemove: Set = Set([mobileHTMLNetworkItem] + [mediaListNetworkItem] + offlineResourceItems + finalMediaListItems + imageInfoItems) + + self.context.perform { [weak self] in + guard let self = self else { + return + } + guard let group = CacheDBWriterHelper.cacheGroup(with: groupKey, in: self.context) else { + completion(.failure(ArticleCacheDBWriterSyncError.cannotFindCacheGroup)) + return + } + guard let cacheItems = group.cacheItems as? Set else { + completion(.failure(ArticleCacheDBWriterSyncError.cannotFindCacheItem)) + return + } + + let downloadedCacheItems = cacheItems.compactMap { (cacheItem) -> NetworkItem? in + guard let itemKey = cacheItem.key, + let itemKeyandVariant = CacheController.ItemKeyAndVariant(itemKey: itemKey, variant: cacheItem.variant), + cacheItem.isDownloaded == true else { + return nil + } + + return NetworkItem(itemKeyAndVariant: itemKeyandVariant, url: cacheItem.url, urlRequest: nil, cacheItem: cacheItem) + } + + let downloadedCacheItemsSet = Set(downloadedCacheItems) + + // determine final set of new items that need to be cached + let filteredNewItems = networkItemsForAdd.subtracting(downloadedCacheItemsSet) + let filteredNewURLRequests = filteredNewItems.compactMap { $0.urlRequest } + + // determine set of old items that need to be removed + let filteredOldItems = downloadedCacheItemsSet.subtracting(networkItemsForRemove) + + // create list of unique item key and variants (those with 1 cache group) to return to CacheController for file deletion. Otherwise delete from group. + var uniqueOldItemKeyAndVariants: [CacheController.ItemKeyAndVariant] = [] + for filteredOldItem in filteredOldItems { + if let cacheItem = filteredOldItem.cacheItem { + if cacheItem.cacheGroups?.count == 1 { + uniqueOldItemKeyAndVariants.append(filteredOldItem.itemKeyAndVariant) + } else { + group.removeFromCacheItems(cacheItem) + } + } + } + + // cache nonCached urls + self.cacheItems(groupKey: groupKey, items: filteredNewItems) { (result) in + switch result { + case .success: + let result = CacheDBWritingSyncSuccessResult(addURLRequests: filteredNewURLRequests, removeItemKeyAndVariants: uniqueOldItemKeyAndVariants) + completion(.success(result)) + case .failure(let error): + completion(.failure(error)) + } + } + } + + case .failure(let error): + completion(.failure(error)) + return + } + } + } + + private func cacheItems(groupKey: String, items: Set, completion: @escaping ((SaveResult) -> Void)) { + context.perform { + + guard let group = CacheDBWriterHelper.fetchOrCreateCacheGroup(with: groupKey, in: self.context) else { + completion(.failure(ArticleCacheDBWriterError.failureFetchOrCreateCacheGroup)) + return + } + + for item in items { + + guard let url = item.url, + item.urlRequest != nil else { + assertionFailure("These need to be populated at this point. They are only optional to be able to compare cleanly with a set of converted CacheItems to NetworkItems") + continue + } + + let itemKey = item.itemKeyAndVariant.itemKey + let variant = item.itemKeyAndVariant.variant + + guard let item = CacheDBWriterHelper.fetchOrCreateCacheItem(with: url, itemKey: itemKey, variant: variant, in: self.context) else { + completion(.failure(ArticleCacheDBWriterSyncError.failureFetchOrCreateCacheItem)) + return + } + + group.addToCacheItems(item) + } + + CacheDBWriterHelper.save(moc: self.context, completion: completion) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/ArticleCacheResourceDBWriting.swift b/Apps/Wikipedia/WMF Framework/ArticleCacheResourceDBWriting.swift new file mode 100644 index 0000000..212ef5a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ArticleCacheResourceDBWriting.swift @@ -0,0 +1,206 @@ +import Foundation + +struct ImageAndResourceURLs { + let offlineResourcesURLs: [URL] + let mediaListURLs: [URL] + let imageInfoURLs: [URL] +} + +enum ImageAndResourceResult { + case success(ImageAndResourceURLs) + case failure(Error) +} + +typealias ImageAndResourceCompletion = (ImageAndResourceResult) -> Void + +protocol ArticleCacheResourceDBWriting: CacheDBWriting { + func fetchMediaListURLs(request: URLRequest, groupKey: String, completion: @escaping (Result<[ArticleFetcher.MediaListItem], ArticleCacheDBWriterError>) -> Void) + func fetchOfflineResourceURLs(request: URLRequest, groupKey: String, completion: @escaping (Result<[URL], ArticleCacheDBWriterError>) -> Void) + func cacheURLs(groupKey: String, mustHaveURLRequests: [URLRequest], niceToHaveURLRequests: [URLRequest], completion: @escaping ((SaveResult) -> Void)) + var articleFetcher: ArticleFetcher { get } + var imageInfoFetcher: MWKImageInfoFetcher { get } + var context: NSManagedObjectContext { get } +} + +extension ArticleCacheResourceDBWriting { + + func fetchMediaListURLs(request: URLRequest, groupKey: String, completion: @escaping (Result<[ArticleFetcher.MediaListItem], ArticleCacheDBWriterError>) -> Void) { + + guard request.url != nil else { + completion(.failure(.missingListURLInRequest)) + return + } + + let untrackKey = UUID().uuidString + let task = articleFetcher.fetchMediaListURLs(with: request) { [weak self] (result) in + + defer { + self?.untrackTask(untrackKey: untrackKey, from: groupKey) + } + + switch result { + case .success(let items): + completion(.success(items)) + case .failure(let error): + completion(.failure(.failureFetchingMediaList(error))) + } + } + + if let task = task { + trackTask(untrackKey: untrackKey, task: task, to: groupKey) + } + } + + func fetchOfflineResourceURLs(request: URLRequest, groupKey: String, completion: @escaping (Result<[URL], ArticleCacheDBWriterError>) -> Void) { + + guard request.url != nil else { + completion(.failure(.missingListURLInRequest)) + return + } + + let untrackKey = UUID().uuidString + let task = articleFetcher.fetchOfflineResourceURLs(with: request) { [weak self] (result) in + + defer { + self?.untrackTask(untrackKey: untrackKey, from: groupKey) + } + + switch result { + case .success(let urls): + completion(.success(urls)) + case .failure(let error): + completion(.failure(.failureFetchingOfflineResourceList(error))) + } + } + + if let task = task { + trackTask(untrackKey: untrackKey, task: task, to: groupKey) + } + } + + func cacheURLs(groupKey: String, mustHaveURLRequests: [URLRequest], niceToHaveURLRequests: [URLRequest], completion: @escaping ((SaveResult) -> Void)) { + context.perform { + + guard let group = CacheDBWriterHelper.fetchOrCreateCacheGroup(with: groupKey, in: self.context) else { + completion(.failure(ArticleCacheDBWriterError.failureFetchOrCreateCacheGroup)) + return + } + + for urlRequest in mustHaveURLRequests { + + guard let url = urlRequest.url, + let itemKey = self.fetcher.itemKeyForURLRequest(urlRequest) else { + completion(.failure(ArticleCacheDBWriterError.unableToDetermineItemKey)) + return + } + + // note, we purposefully do not set variant here. We need to wait until CacheFileWriter determines if the response varies on language, then set it when we call markDownloaded + guard let item = CacheDBWriterHelper.fetchOrCreateCacheItem(with: url, itemKey: itemKey, variant: nil, in: self.context) else { + completion(.failure(ArticleCacheDBWriterError.failureFetchOrCreateMustHaveCacheItem)) + return + } + + group.addToCacheItems(item) + group.addToMustHaveCacheItems(item) + } + + for urlRequest in niceToHaveURLRequests { + + guard let url = urlRequest.url, + let itemKey = self.fetcher.itemKeyForURLRequest(urlRequest) else { + continue + } + + guard let item = CacheDBWriterHelper.fetchOrCreateCacheItem(with: url, itemKey: itemKey, variant: nil, in: self.context) else { + continue + } + + group.addToCacheItems(item) + } + + CacheDBWriterHelper.save(moc: self.context, completion: completion) + } + } + + func fetchImageAndResourceURLsForArticleURL(_ articleURL: URL, groupKey: CacheController.GroupKey, completion: @escaping ImageAndResourceCompletion) { + var mobileHTMLOfflineResourcesRequest: URLRequest + var mobileHTMLMediaListRequest: URLRequest + do { + mobileHTMLOfflineResourcesRequest = try articleFetcher.mobileHTMLOfflineResourcesRequest(articleURL: articleURL) + mobileHTMLMediaListRequest = try articleFetcher.mobileHTMLMediaListRequest(articleURL: articleURL) + } catch let error { + completion(.failure(error)) + return + } + + var mobileHtmlOfflineResourceURLs: [URL] = [] + var mediaListURLs: [URL] = [] + var imageInfoURLs: [URL] = [] + + var mediaListError: Error? + var mobileHtmlOfflineResourceError: Error? + + let group = DispatchGroup() + + group.enter() + fetchOfflineResourceURLs(request: mobileHTMLOfflineResourcesRequest, groupKey: groupKey) { (result) in + defer { + group.leave() + } + + switch result { + case .success(let urls): + + mobileHtmlOfflineResourceURLs = urls + + case .failure(let error): + mobileHtmlOfflineResourceError = error + } + } + + group.enter() + fetchMediaListURLs(request: mobileHTMLMediaListRequest, groupKey: groupKey) { (result) in + + defer { + group.leave() + } + + switch result { + case .success(let items): + + mediaListURLs = items.map { $0.imageURL } + + let imageTitles = items.map { $0.imageTitle } + let dedupedTitles = Set(imageTitles) + + // add imageInfoFetcher's urls for deduped titles (for captions/licensing info in gallery) + for title in dedupedTitles { + if let imageInfoURL = self.imageInfoFetcher.galleryInfoURL(forImageTitles: [title], fromSiteURL: articleURL) { + imageInfoURLs.append(imageInfoURL) + } + } + + case .failure(let error): + mediaListError = error + } + } + + group.notify(queue: DispatchQueue.global(qos: .default)) { + + if let mediaListError = mediaListError { + let result = ImageAndResourceResult.failure(mediaListError) + completion(result) + return + } + + if let mobileHtmlOfflineResourceError = mobileHtmlOfflineResourceError { + let result = ImageAndResourceResult.failure(mobileHtmlOfflineResourceError) + completion(result) + return + } + + let result = ImageAndResourceURLs(offlineResourcesURLs: mobileHtmlOfflineResourceURLs, mediaListURLs: mediaListURLs, imageInfoURLs: imageInfoURLs) + completion(.success(result)) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/ArticleCollectionViewCell+Themeable.swift b/Apps/Wikipedia/WMF Framework/ArticleCollectionViewCell+Themeable.swift new file mode 100644 index 0000000..7f62bb7 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ArticleCollectionViewCell+Themeable.swift @@ -0,0 +1,32 @@ +extension ArticleCollectionViewCell: Themeable { + open func apply(theme: Theme) { + let selected = isBatchEditing ? theme.colors.batchSelectionBackground : theme.colors.midBackground + setBackgroundColors(theme.colors.paperBackground, selected: selected) + imageView.backgroundColor = theme.colors.midBackground + titleLabel.textColor = theme.colors.primaryText + descriptionLabel.textColor = theme.colors.secondaryText + extractLabel?.textColor = theme.colors.primaryText + imageView.alpha = theme.imageOpacity + statusView.backgroundColor = theme.colors.warning + alertButton.tintColor = alertType == .downloading ? theme.colors.warning : theme.colors.error + alertButton.setTitleColor(alertButton.tintColor, for: .normal) + actionsView.apply(theme: theme) + batchEditSelectView?.apply(theme: theme) + updateSelectedOrHighlighted() + } +} + +extension ArticleRightAlignedImageCollectionViewCell { + open override func apply(theme: Theme) { + super.apply(theme: theme) + bottomSeparator.backgroundColor = theme.colors.baseBackground + topSeparator.backgroundColor = theme.colors.baseBackground + } +} + +extension ArticleFullWidthImageCollectionViewCell { + open override func apply(theme: Theme) { + super.apply(theme: theme) + saveButton.setTitleColor(theme.colors.link, for: .normal) + } +} diff --git a/Apps/Wikipedia/WMF Framework/ArticleSummary.swift b/Apps/Wikipedia/WMF Framework/ArticleSummary.swift new file mode 100644 index 0000000..a761b9e --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ArticleSummary.swift @@ -0,0 +1,95 @@ +import Foundation + +/// Represents the response structure of an article summary from the Page Content Service /page/summary endpoint +@objc(WMFArticleSummary) +public class ArticleSummary: NSObject, Codable { + @objc public class Namespace: NSObject, Codable { + let id: Int? + let text: String? + + @objc public var number: NSNumber? { + guard let id = id else { + return nil + } + return NSNumber(value: id) + } + } + let id: Int64? + let wikidataID: String? + let revision: String? + let timestamp: String? + let index: Int? + @objc let namespace: Namespace? + let title: String? + let displayTitle: String? + let articleDescription: String? + let extract: String? + let extractHTML: String? + let thumbnail: ArticleSummaryImage? + let original: ArticleSummaryImage? + @objc let coordinates: ArticleSummaryCoordinates? + var languageVariantCode: String? + + enum CodingKeys: String, CodingKey { + case id = "pageid" + case revision + case index + case namespace + case title + case timestamp + case displayTitle = "displaytitle" + case articleDescription = "description" + case extract + case extractHTML = "extract_html" + case thumbnail + case original = "originalimage" + case coordinates + case contentURLs = "content_urls" + case wikidataID = "wikibase_item" + } + + let contentURLs: ArticleSummaryContentURLs + + var articleURL: URL? { + guard let urlString = contentURLs.desktop?.page else { + return nil + } + var articleURL = URL(string: urlString) + articleURL?.wmf_languageVariantCode = languageVariantCode + return articleURL + } + + var key: WMFInMemoryURLKey? { + return articleURL?.wmf_inMemoryKey // don't use contentURLs.desktop?.page directly as it needs to be standardized + } +} + +@objc(WMFArticleSummaryImage) +class ArticleSummaryImage: NSObject, Codable { + let source: String + let width: Int + let height: Int + var url: URL? { + return URL(string: source) + } +} + +@objc(WMFArticleSummaryURLs) +class ArticleSummaryURLs: NSObject, Codable { + let page: String? + let revisions: String? + let edit: String? + let talk: String? +} + +@objc(WMFArticleSummaryContentURLs) +class ArticleSummaryContentURLs: NSObject, Codable { + let desktop: ArticleSummaryURLs? + let mobile: ArticleSummaryURLs? +} + +@objc(WMFArticleSummaryCoordinates) +class ArticleSummaryCoordinates: NSObject, Codable { + @objc let lat: Double + @objc let lon: Double +} diff --git a/Apps/Wikipedia/WMF Framework/AsyncOperation.swift b/Apps/Wikipedia/WMF Framework/AsyncOperation.swift new file mode 100644 index 0000000..5d0533c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/AsyncOperation.swift @@ -0,0 +1,122 @@ +import Foundation + +enum AsyncOperationError: Error { + case cancelled +} + +// Adapted from https://gist.github.com/calebd/93fa347397cec5f88233 + +@objc(WMFAsyncOperation) open class AsyncOperation: Operation { + + // MARK: - Operation State + + fileprivate let semaphore = DispatchSemaphore(value: 1) // Ensures `state` is thread-safe + + @objc public enum State: Int { + case ready + case executing + case finished + + var affectedKeyPath: KeyPath { + switch self { + case .ready: + return \.isReady + case .executing: + return \.isExecuting + case .finished: + return \.isFinished + } + } + } + + public var error: Error? + + fileprivate var _state = AsyncOperation.State.ready + + @objc public var state: AsyncOperation.State { + get { + semaphore.wait() + let state = _state + defer { + semaphore.signal() + } + return state + } + set { + willChangeValue(for: \.state) + let affectedKeyPaths = [_state.affectedKeyPath, newValue.affectedKeyPath] + for keyPath in affectedKeyPaths { + willChangeValue(for: keyPath) + } + semaphore.wait() + _state = newValue + semaphore.signal() + didChangeValue(for: \.state) + for keyPath in affectedKeyPaths { + didChangeValue(for: keyPath) + } + } + } + + // MARK: - Operation subclass requirements + + public final override var isReady: Bool { + return state == .ready && super.isReady + } + + public final override var isExecuting: Bool { + return state == .executing + } + + public final override var isFinished: Bool { + return state == .finished + } + + public final override var isAsynchronous: Bool { + return true + } + + open override func start() { + // From the docs for `start`: + // "Your custom implementation must not call super at any time." + + if isCancelled { + finish(with: AsyncOperationError.cancelled) + return + } + + state = .executing + execute() + } + + // MARK: - Custom behavior + + @objc open func finish() { + state = .finished + } + + @objc open func finish(with error: Error) { + self.error = error + state = .finished + } + + /// Subclasses must implement this to perform their work and they must not + /// call `super`. The default implementation of this function throws an + /// exception. + open func execute() { + fatalError("Subclasses must implement `execute`.") + } +} + +@objc(WMFAsyncBlockOperation) open class AsyncBlockOperation: AsyncOperation { + let asyncBlock: (AsyncBlockOperation) -> Void + + @objc init(asyncBlock block: @escaping (AsyncBlockOperation) -> Void) { + asyncBlock = block + } + + final override public func execute() { + asyncBlock(self) + } +} + diff --git a/Apps/Wikipedia/WMF Framework/BackgroundFetcher.swift b/Apps/Wikipedia/WMF Framework/BackgroundFetcher.swift new file mode 100644 index 0000000..8237869 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/BackgroundFetcher.swift @@ -0,0 +1,40 @@ +import Foundation + +@objc(WMFBackgroundFetcher) public protocol BackgroundFetcher: NSObjectProtocol { + func performBackgroundFetch(_ completion: @escaping (UIBackgroundFetchResult) -> Void) +} + +@objc(WMFBackgroundFetcherController) public class BackgroundFetcherController: WorkerController { + var fetchers = [BackgroundFetcher]() + + @objc public func add(_ worker: BackgroundFetcher) { + fetchers.append(worker) + } + + @objc public func performBackgroundFetch(_ completion: @escaping (UIBackgroundFetchResult) -> Void) { + let identifier = UUID().uuidString + delegate?.workerControllerWillStart(self, workWithIdentifier: identifier) + fetchers.asyncMap({ (fetcher, completion) in + fetcher.performBackgroundFetch(completion) + }) { [weak self] (results) in + var combinedResult = UIBackgroundFetchResult.noData + resultLoop: for result in results { + switch result { + case .failed: + combinedResult = .failed + break resultLoop + case .newData: + combinedResult = .newData + default: + break + } + } + + completion(combinedResult) + guard let strongSelf = self else { + return + } + strongSelf.delegate?.workerControllerDidEnd(strongSelf, workWithIdentifier: identifier) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/BatchEditToolbarViewController.swift b/Apps/Wikipedia/WMF Framework/BatchEditToolbarViewController.swift new file mode 100644 index 0000000..3a6ad08 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/BatchEditToolbarViewController.swift @@ -0,0 +1,47 @@ +import UIKit + +public final class BatchEditToolbarViewController: UIViewController { + + @IBOutlet private weak var stackView: UIStackView? + @IBOutlet private weak var bottomConstraint: NSLayoutConstraint! + @IBOutlet private weak var separatorView: UIView? + public var items: [UIButton] = [] + private var theme: Theme = Theme.standard + + public func setItemsEnabled(_ enabled: Bool) { + for item in items { + item.isEnabled = enabled + } + } + + public func remove() { + self.willMove(toParent: nil) + view.removeFromSuperview() + self.removeFromParent() + } + + public override func didMove(toParent parent: UIViewController?) { + if let parent = parent, let safeAreaOwningView = view.safeAreaLayoutGuide.owningView { + bottomConstraint.constant = max(0, parent.view.safeAreaInsets.bottom - safeAreaOwningView.frame.height) + } + } + + public override func viewDidLoad() { + super.viewDidLoad() + for item in items { + stackView?.addArrangedSubview(item) + } + apply(theme: theme) + } +} + +extension BatchEditToolbarViewController: Themeable { + public func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.midBackground + separatorView?.backgroundColor = theme.colors.border + } +} diff --git a/Apps/Wikipedia/WMF Framework/BatchEditToolbarViewController.xib b/Apps/Wikipedia/WMF Framework/BatchEditToolbarViewController.xib new file mode 100644 index 0000000..323ece2 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/BatchEditToolbarViewController.xib @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/WMF Framework/Bundle+IsAppExtension.swift b/Apps/Wikipedia/WMF Framework/Bundle+IsAppExtension.swift new file mode 100644 index 0000000..c244c5a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Bundle+IsAppExtension.swift @@ -0,0 +1,7 @@ +import Foundation + +extension Bundle { + var isAppExtension: Bool { + return bundleURL.pathExtension.caseInsensitiveCompare("appex") == .orderedSame + } +} diff --git a/Apps/Wikipedia/WMF Framework/Bundle.swift b/Apps/Wikipedia/WMF Framework/Bundle.swift new file mode 100644 index 0000000..0596daf --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Bundle.swift @@ -0,0 +1,10 @@ +import Foundation + +extension Bundle { + @objc public static let wmf: Bundle = Bundle(identifier: "org.wikimedia.WMF")! + + @objc(wmf_assetsFolderURL) + public var assetsFolderURL: URL { + return url(forResource: "assets", withExtension: nil)! + } +} diff --git a/Apps/Wikipedia/WMF Framework/CGRect+Layout.swift b/Apps/Wikipedia/WMF Framework/CGRect+Layout.swift new file mode 100644 index 0000000..005245a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CGRect+Layout.swift @@ -0,0 +1,10 @@ +extension CGRect { + // Height required to layout this rect. Returns 0 if rect.height is 0. Returns height plus spacing otherwise. + public func layoutHeight(with spacing: CGFloat) -> CGFloat { + return height > 0 ? height + spacing : 0 + } + + public var center: CGPoint { + return CGPoint(x: midX, y: midY) + } +} diff --git a/Apps/Wikipedia/WMF Framework/Cache.xcdatamodeld/.xccurrentversion b/Apps/Wikipedia/WMF Framework/Cache.xcdatamodeld/.xccurrentversion new file mode 100644 index 0000000..5bbc9de --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Cache.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ + + + + + _XCCurrentVersionName + Cache 2.xcdatamodel + + diff --git a/Apps/Wikipedia/WMF Framework/Cache.xcdatamodeld/Cache 2.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Cache.xcdatamodeld/Cache 2.xcdatamodel/contents new file mode 100644 index 0000000..1b76957 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Cache.xcdatamodeld/Cache 2.xcdatamodel/contents @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/Cache.xcdatamodeld/Cache.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Cache.xcdatamodeld/Cache.xcdatamodel/contents new file mode 100644 index 0000000..598e6df --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Cache.xcdatamodeld/Cache.xcdatamodel/contents @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/CacheController.swift b/Apps/Wikipedia/WMF Framework/CacheController.swift new file mode 100644 index 0000000..f2c0ac6 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheController.swift @@ -0,0 +1,405 @@ +import Foundation + +public enum CacheControllerError: Error { + case unableToCreateBackgroundCacheContext + case atLeastOneItemFailedInFileWriter(Error) + case failureToGenerateItemResult + case atLeastOneItemFailedInSync(Error) +} + +public class CacheController { + + #if TEST + public static var temporaryCacheURL: URL? = nil + #endif + + static let cacheURL: URL = { + + #if TEST + if let temporaryCacheURL = temporaryCacheURL { + return temporaryCacheURL + } + #endif + + var url = FileManager.default.wmf_containerURL().appendingPathComponent("Permanent Cache", isDirectory: true) + + var values = URLResourceValues() + values.isExcludedFromBackup = true + do { + try url.setResourceValues(values) + } catch { + return url + } + + return url + }() + + // todo: Settings hook, logout don't sync hook, etc. + public static var totalCacheSizeInBytes: Int64 { + return FileManager.default.sizeOfDirectory(at: cacheURL) + } + + /// Performs any necessary migrations on the CacheController's internal storage + static func setupCoreDataStack(_ completion: @escaping (NSManagedObjectContext?, CacheControllerError?) -> Void) { + // Expensive file & db operations happen as a part of this migration, so async it to a non-main queue + DispatchQueue.global(qos: .default).async { + // Instantiating the moc will perform the migrations in CacheItemMigrationPolicy + guard let moc = createCacheContext(cacheURL: cacheURL) else { + completion(nil, .unableToCreateBackgroundCacheContext) + return + } + // do a moc.perform in case anything else needs to be run before the context is ready + moc.perform { + DispatchQueue.main.async { + completion(moc, nil) + } + } + } + } + + static func createCacheContext(cacheURL: URL) -> NSManagedObjectContext? { + + // create cacheURL directory + do { + try FileManager.default.createDirectory(at: cacheURL, withIntermediateDirectories: true, attributes: nil) + } catch let error { + assertionFailure("Error creating permanent cache: \(error)") + return nil + } + + // create ManagedObjectModel based on Cache.momd + guard let modelURL = Bundle.wmf.url(forResource: "Cache", withExtension: "momd"), + let model = NSManagedObjectModel(contentsOf: modelURL) else { + assertionFailure("Failure to create managed object model") + return nil + } + + // create persistent store coordinator / persistent store + let dbURL = cacheURL.appendingPathComponent("Cache.sqlite", isDirectory: false) + let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model) + + let options = [ + NSMigratePersistentStoresAutomaticallyOption: NSNumber(booleanLiteral: true), + NSInferMappingModelAutomaticallyOption: NSNumber(booleanLiteral: true) + ] + + do { + try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: dbURL, options: options) + } catch { + do { + try FileManager.default.removeItem(at: dbURL) + } catch { + assertionFailure("Failure to remove old db file") + } + + do { + try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: dbURL, options: options) + } catch { + assertionFailure("Failure to add persistent store to coordinator") + return nil + } + } + + let cacheBackgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + cacheBackgroundContext.persistentStoreCoordinator = persistentStoreCoordinator + + return cacheBackgroundContext + } + + public typealias ItemKey = String + public typealias GroupKey = String + public typealias UniqueKey = String // combo of item key + variant + public typealias IndividualCompletionBlock = (FinalIndividualResult) -> Void + public typealias GroupCompletionBlock = (FinalGroupResult) -> Void + + public struct ItemKeyAndVariant: Hashable { + let itemKey: CacheController.ItemKey + let variant: String? + + init?(itemKey: CacheController.ItemKey?, variant: String?) { + + guard let itemKey = itemKey else { + return nil + } + + self.itemKey = itemKey + self.variant = variant + } + } + + public enum FinalIndividualResult { + case success(uniqueKey: CacheController.UniqueKey) + case failure(error: Error) + } + + public enum FinalGroupResult { + case success(uniqueKeys: [CacheController.UniqueKey]) + case failure(error: Error) + } + + let dbWriter: CacheDBWriting + let fileWriter: CacheFileWriter + let gatekeeper = CacheGatekeeper() + + init(dbWriter: CacheDBWriting, fileWriter: CacheFileWriter) { + self.dbWriter = dbWriter + self.fileWriter = fileWriter + } + + public func add(url: URL, groupKey: GroupKey, individualCompletion: @escaping IndividualCompletionBlock, groupCompletion: @escaping GroupCompletionBlock) { + + if gatekeeper.shouldQueueAddCompletion(groupKey: groupKey) { + gatekeeper.queueAddCompletion(groupKey: groupKey) { + self.add(url: url, groupKey: groupKey, individualCompletion: individualCompletion, groupCompletion: groupCompletion) + return + } + } else { + gatekeeper.addCurrentlyAddingGroupKey(groupKey) + } + + if gatekeeper.numberOfQueuedGroupCompletions(for: groupKey) > 0 { + gatekeeper.queueGroupCompletion(groupKey: groupKey, groupCompletion: groupCompletion) + return + } + + gatekeeper.queueGroupCompletion(groupKey: groupKey, groupCompletion: groupCompletion) + + dbWriter.add(url: url, groupKey: groupKey) { [weak self] (result) in + self?.finishDBAdd(groupKey: groupKey, individualCompletion: individualCompletion, groupCompletion: groupCompletion, result: result) + } + } + + public func cancelTasks(groupKey: String) { + dbWriter.cancelTasks(for: groupKey) + fileWriter.cancelTasks(for: groupKey) + } + + public func cancelAllTasks() { + dbWriter.cancelAllTasks() + fileWriter.cancelAllTasks() + } + + func shouldDownloadVariantForAllVariantItems(variant: String?, _ allVariantItems: [CacheController.ItemKeyAndVariant]) -> Bool { + return dbWriter.shouldDownloadVariantForAllVariantItems(variant: variant, allVariantItems) + } + + func finishDBAdd(groupKey: GroupKey, individualCompletion: @escaping IndividualCompletionBlock, groupCompletion: @escaping GroupCompletionBlock, result: CacheDBWritingResultWithURLRequests) { + + let groupCompleteBlock = { (groupResult: FinalGroupResult) in + self.gatekeeper.runAndRemoveGroupCompletions(groupKey: groupKey, groupResult: groupResult) + self.gatekeeper.removeCurrentlyAddingGroupKey(groupKey) + self.gatekeeper.runAndRemoveQueuedRemoves(groupKey: groupKey) + } + + switch result { + case .success(let urlRequests): + + var successfulKeys: [CacheController.UniqueKey] = [] + var failedKeys: [(CacheController.UniqueKey, Error)] = [] + + let group = DispatchGroup() + for urlRequest in urlRequests { + + guard let uniqueKey = fileWriter.uniqueFileNameForURLRequest(urlRequest), + let url = urlRequest.url else { + continue + } + + group.enter() + + if gatekeeper.numberOfQueuedIndividualCompletions(for: uniqueKey) > 0 { + defer { + group.leave() + } + gatekeeper.queueIndividualCompletion(uniqueKey: uniqueKey, individualCompletion: individualCompletion) + continue + } + + gatekeeper.queueIndividualCompletion(uniqueKey: uniqueKey, individualCompletion: individualCompletion) + + guard dbWriter.shouldDownloadVariant(urlRequest: urlRequest) else { + group.leave() + continue + } + + fileWriter.add(groupKey: groupKey, urlRequest: urlRequest) { [weak self] (result) in + + guard let self = self else { + return + } + + switch result { + case .success(let response, let data): + + self.dbWriter.markDownloaded(urlRequest: urlRequest, response: response) { (result) in + + defer { + group.leave() + } + + let individualResult: FinalIndividualResult + + switch result { + case .success: + successfulKeys.append(uniqueKey) + individualResult = FinalIndividualResult.success(uniqueKey: uniqueKey) + + case .failure(let error): + failedKeys.append((uniqueKey, error)) + individualResult = FinalIndividualResult.failure(error: error) + } + + self.gatekeeper.runAndRemoveIndividualCompletions(uniqueKey: uniqueKey, individualResult: individualResult) + } + + self.finishFileSave(data: data, mimeType: response.mimeType, uniqueKey: uniqueKey, url: url) + + case .failure(let error): + + defer { + group.leave() + } + + failedKeys.append((uniqueKey, error)) + let individualResult = FinalIndividualResult.failure(error: error) + self.gatekeeper.runAndRemoveIndividualCompletions(uniqueKey: uniqueKey, individualResult: individualResult) + } + } + + group.notify(queue: DispatchQueue.global(qos: .userInitiated)) { + let groupResult: FinalGroupResult + if let error = failedKeys.first?.1 { + groupResult = FinalGroupResult.failure(error: CacheControllerError.atLeastOneItemFailedInFileWriter(error)) + } else { + groupResult = FinalGroupResult.success(uniqueKeys: successfulKeys) + } + groupCompleteBlock(groupResult) + } + } + + case .failure(let error): + let groupResult = FinalGroupResult.failure(error: error) + groupCompleteBlock(groupResult) + } + } + + func finishFileSave(data: Data, mimeType: String?, uniqueKey: CacheController.UniqueKey, url: URL) { + // hook to allow subclasses to do any additional work with data + } + + public func remove(groupKey: GroupKey, individualCompletion: @escaping IndividualCompletionBlock, groupCompletion: @escaping GroupCompletionBlock) { + + if gatekeeper.shouldQueueRemoveCompletion(groupKey: groupKey) { + gatekeeper.queueRemoveCompletion(groupKey: groupKey) { + self.remove(groupKey: groupKey, individualCompletion: individualCompletion, groupCompletion: groupCompletion) + return + } + } else { + gatekeeper.addCurrentlyRemovingGroupKey(groupKey) + } + + if gatekeeper.numberOfQueuedGroupCompletions(for: groupKey) > 0 { + gatekeeper.queueGroupCompletion(groupKey: groupKey, groupCompletion: groupCompletion) + return + } + + gatekeeper.queueGroupCompletion(groupKey: groupKey, groupCompletion: groupCompletion) + + cancelTasks(groupKey: groupKey) + + let groupCompleteBlock = { (groupResult: FinalGroupResult) in + self.gatekeeper.runAndRemoveGroupCompletions(groupKey: groupKey, groupResult: groupResult) + self.gatekeeper.removeCurrentlyRemovingGroupKey(groupKey) + self.gatekeeper.runAndRemoveQueuedAdds(groupKey: groupKey) + } + + dbWriter.fetchKeysToRemove(for: groupKey) { [weak self] (result) in + + guard let self = self else { + return + } + + switch result { + case .success(let keys): + + var successfulKeys: [CacheController.UniqueKey] = [] + var failedKeys: [(CacheController.UniqueKey, Error)] = [] + + let group = DispatchGroup() + for key in keys { + + guard let uniqueKey = self.fileWriter.uniqueFileNameForItemKey(key.itemKey, variant: key.variant) else { + continue + } + + group.enter() + + if self.gatekeeper.numberOfQueuedIndividualCompletions(for: uniqueKey) > 0 { + defer { + group.leave() + } + self.gatekeeper.queueIndividualCompletion(uniqueKey: uniqueKey, individualCompletion: individualCompletion) + continue + } + + self.gatekeeper.queueIndividualCompletion(uniqueKey: uniqueKey, individualCompletion: individualCompletion) + + self.fileWriter.remove(itemKey: key.itemKey, variant: key.variant) { (result) in + + switch result { + case .success: + + self.dbWriter.remove(itemAndVariantKey: key) { (result) in + defer { + group.leave() + } + + var individualResult: FinalIndividualResult + switch result { + case .success: + successfulKeys.append(uniqueKey) + individualResult = FinalIndividualResult.success(uniqueKey: uniqueKey) + case .failure(let error): + failedKeys.append((uniqueKey, error)) + individualResult = FinalIndividualResult.failure(error: error) + } + + self.gatekeeper.runAndRemoveIndividualCompletions(uniqueKey: uniqueKey, individualResult: individualResult) + } + + case .failure(let error): + failedKeys.append((uniqueKey, error)) + let individualResult = FinalIndividualResult.failure(error: error) + self.gatekeeper.runAndRemoveIndividualCompletions(uniqueKey: uniqueKey, individualResult: individualResult) + group.leave() + } + } + } + + group.notify(queue: DispatchQueue.global(qos: .userInitiated)) { + if let error = failedKeys.first?.1 { + let groupResult = FinalGroupResult.failure(error: CacheControllerError.atLeastOneItemFailedInFileWriter(error)) + groupCompleteBlock(groupResult) + return + } + self.dbWriter.remove(groupKey: groupKey, completion: { (result) in + var groupResult: FinalGroupResult + switch result { + case .success: + groupResult = FinalGroupResult.success(uniqueKeys: successfulKeys) + + case .failure(let error): + groupResult = FinalGroupResult.failure(error: error) + } + + groupCompleteBlock(groupResult) + }) + } + + case .failure(let error): + let groupResult = FinalGroupResult.failure(error: error) + groupCompleteBlock(groupResult) + } + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/CacheDBWriterHelper.swift b/Apps/Wikipedia/WMF Framework/CacheDBWriterHelper.swift new file mode 100644 index 0000000..eaf566c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheDBWriterHelper.swift @@ -0,0 +1,114 @@ +import Foundation + +final class CacheDBWriterHelper { + static func fetchOrCreateCacheGroup(with groupKey: String, in moc: NSManagedObjectContext) -> CacheGroup? { + return cacheGroup(with: groupKey, in: moc) ?? createCacheGroup(with: groupKey, in: moc) + } + + static func fetchOrCreateCacheItem(with url: URL, itemKey: String, variant: String?, in moc: NSManagedObjectContext) -> CacheItem? { + return cacheItem(with: itemKey, variant: variant, in: moc) ?? createCacheItem(with: url, itemKey: itemKey, variant: variant, in: moc) + } + + static func cacheGroup(with key: String, in moc: NSManagedObjectContext) -> + CacheGroup? { + + let fetchRequest: NSFetchRequest = CacheGroup.fetchRequest() + fetchRequest.predicate = NSPredicate(format: "key == %@", key) + fetchRequest.fetchLimit = 1 + do { + guard let group = try moc.fetch(fetchRequest).first else { + return nil + } + return group + } catch let error { + fatalError(error.localizedDescription) + } + } + + static func createCacheGroup(with groupKey: String, in moc: NSManagedObjectContext) -> CacheGroup? { + + guard let entity = NSEntityDescription.entity(forEntityName: "CacheGroup", in: moc) else { + return nil + } + let group = CacheGroup(entity: entity, insertInto: moc) + group.key = groupKey + return group + } + + static func cacheItem(with itemKey: String, variant: String?, in moc: NSManagedObjectContext) -> CacheItem? { + + let predicate: NSPredicate + if let variant = variant { + predicate = NSPredicate(format: "key == %@ && variant == %@", itemKey, variant) + } else { + predicate = NSPredicate(format: "key == %@", itemKey) + } + + let fetchRequest: NSFetchRequest = CacheItem.fetchRequest() + fetchRequest.predicate = predicate + fetchRequest.fetchLimit = 1 + do { + guard let item = try moc.fetch(fetchRequest).first else { + return nil + } + return item + } catch let error { + fatalError(error.localizedDescription) + } + } + + static func createCacheItem(with url: URL, itemKey: String, variant: String?, in moc: NSManagedObjectContext) -> CacheItem? { + guard let entity = NSEntityDescription.entity(forEntityName: "CacheItem", in: moc) else { + return nil + } + let item = CacheItem(entity: entity, insertInto: moc) + item.key = itemKey + item.variant = variant + item.url = url + item.date = Date() + return item + } + + static func isCached(itemKey: CacheController.ItemKey, variant: String?, in moc: NSManagedObjectContext, completion: @escaping (Bool) -> Void) { + return moc.perform { + let isCached = CacheDBWriterHelper.cacheItem(with: itemKey, variant: variant, in: moc) != nil + completion(isCached) + } + } + + static func allDownloadedVariantItems(itemKey: CacheController.ItemKey, in moc: NSManagedObjectContext) -> [CacheItem] { + + let fetchRequest: NSFetchRequest = CacheItem.fetchRequest() + fetchRequest.predicate = NSPredicate(format: "key == %@ && isDownloaded == YES", itemKey) + do { + return try moc.fetch(fetchRequest) + } catch { + return [] + } + } + + static func allVariantItems(itemKey: CacheController.ItemKey, in moc: NSManagedObjectContext) -> [CacheItem] { + + let fetchRequest: NSFetchRequest = CacheItem.fetchRequest() + fetchRequest.predicate = NSPredicate(format: "key == %@", itemKey) + do { + return try moc.fetch(fetchRequest) + } catch { + return [] + } + } + + static func save(moc: NSManagedObjectContext, completion: (_ result: SaveResult) -> Void) { + guard moc.hasChanges else { + completion(.success) + return + } + do { + try moc.save() + completion(.success) + } catch let error { + assertionFailure("Error saving cache moc: \(error)") + completion(.failure(error)) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/CacheDBWriting.swift b/Apps/Wikipedia/WMF Framework/CacheDBWriting.swift new file mode 100644 index 0000000..4054f6e --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheDBWriting.swift @@ -0,0 +1,126 @@ +import Foundation + +enum SaveResult { + case success + case failure(Error) +} + +enum CacheDBWritingResultWithURLRequests { + case success([URLRequest]) + case failure(Error) +} + +enum CacheDBWritingResultWithItemAndVariantKeys { + case success([CacheController.ItemKeyAndVariant]) + case failure(Error) +} + +enum CacheDBWritingResult { + case success + case failure(Error) +} + +enum CacheDBWritingMarkDownloadedError: Error { + case cannotFindCacheGroup + case cannotFindCacheItem + case unableToDetermineItemKey + case missingMOC +} + +enum CacheDBWritingRemoveError: Error { + case cannotFindCacheGroup + case cannotFindCacheItem + case missingMOC +} + +protocol CacheDBWriting: CacheTaskTracking { + var context: NSManagedObjectContext { get } + + typealias CacheDBWritingCompletionWithURLRequests = (CacheDBWritingResultWithURLRequests) -> Void + typealias CacheDBWritingCompletionWithItemAndVariantKeys = (CacheDBWritingResultWithItemAndVariantKeys) -> Void + + func add(url: URL, groupKey: CacheController.GroupKey, completion: @escaping CacheDBWritingCompletionWithURLRequests) + func add(urls: [URL], groupKey: CacheController.GroupKey, completion: @escaping CacheDBWritingCompletionWithURLRequests) + func shouldDownloadVariant(itemKey: CacheController.ItemKey, variant: String?) -> Bool + func shouldDownloadVariant(urlRequest: URLRequest) -> Bool + func shouldDownloadVariantForAllVariantItems(variant: String?, _ allVariantItems: [CacheController.ItemKeyAndVariant]) -> Bool + var fetcher: CacheFetching { get } + + // default implementations + func remove(itemAndVariantKey: CacheController.ItemKeyAndVariant, completion: @escaping (CacheDBWritingResult) -> Void) + func remove(groupKey: String, completion: @escaping (CacheDBWritingResult) -> Void) + func fetchKeysToRemove(for groupKey: CacheController.GroupKey, completion: @escaping CacheDBWritingCompletionWithItemAndVariantKeys) + func markDownloaded(urlRequest: URLRequest, response: HTTPURLResponse?, completion: @escaping (CacheDBWritingResult) -> Void) +} + +extension CacheDBWriting { + + func fetchKeysToRemove(for groupKey: CacheController.GroupKey, completion: @escaping CacheDBWritingCompletionWithItemAndVariantKeys) { + context.perform { + guard let group = CacheDBWriterHelper.cacheGroup(with: groupKey, in: self.context) else { + completion(.failure(CacheDBWritingMarkDownloadedError.cannotFindCacheGroup)) + return + } + guard let cacheItems = group.cacheItems as? Set else { + completion(.failure(CacheDBWritingMarkDownloadedError.cannotFindCacheItem)) + return + } + + let cacheItemsToRemove = cacheItems.filter({ (cacheItem) -> Bool in + return cacheItem.cacheGroups?.count == 1 + }) + + completion(.success(cacheItemsToRemove.compactMap { CacheController.ItemKeyAndVariant(itemKey: $0.key, variant: $0.variant) })) + } + } + + func remove(itemAndVariantKey: CacheController.ItemKeyAndVariant, completion: @escaping (CacheDBWritingResult) -> Void) { + context.perform { + guard let cacheItem = CacheDBWriterHelper.cacheItem(with: itemAndVariantKey.itemKey, variant: itemAndVariantKey.variant, in: self.context) else { + completion(.failure(CacheDBWritingRemoveError.cannotFindCacheItem)) + return + } + + self.context.delete(cacheItem) + + CacheDBWriterHelper.save(moc: self.context) { (result) in + switch result { + case .success: + completion(.success) + case .failure(let error): + completion(.failure(error)) + } + } + } + } + + func remove(groupKey: CacheController.GroupKey, completion: @escaping (CacheDBWritingResult) -> Void) { + context.perform { + guard let cacheGroup = CacheDBWriterHelper.cacheGroup(with: groupKey, in: self.context) else { + completion(.failure(CacheDBWritingRemoveError.cannotFindCacheItem)) + return + } + + self.context.delete(cacheGroup) + + CacheDBWriterHelper.save(moc: self.context) { (result) in + switch result { + case .success: + completion(.success) + case .failure(let error): + completion(.failure(error)) + } + } + } + } + + func shouldDownloadVariant(urlRequest: URLRequest) -> Bool { + guard let itemKey = fetcher.itemKeyForURLRequest(urlRequest) else { + return false + } + + let variant = fetcher.variantForURLRequest(urlRequest) + + return shouldDownloadVariant(itemKey: itemKey, variant: variant) + } +} diff --git a/Apps/Wikipedia/WMF Framework/CacheFetching.swift b/Apps/Wikipedia/WMF Framework/CacheFetching.swift new file mode 100644 index 0000000..414ad1f --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheFetching.swift @@ -0,0 +1,145 @@ +import Foundation + +public struct CacheFetchingResult { + let data: Data + let response: URLResponse +} + +enum CacheFetchingError: Error { + case missingDataAndURLResponse + case missingURLResponse + case unableToDetermineURLRequest +} + +public protocol CacheFetching { + typealias TemporaryFileURL = URL + typealias MIMEType = String + typealias DownloadCompletion = (Error?, URLRequest?, URLResponse?, TemporaryFileURL?, MIMEType?) -> Void + typealias DataCompletion = (Result) -> Void + + // internally populates urlRequest with cache header fields + func dataForURL(_ url: URL, persistType: Header.PersistItemType, headers: [String: String], completion: @escaping DataCompletion) -> URLSessionTask? + + // assumes urlRequest is already populated with cache header fields + func dataForURLRequest(_ urlRequest: URLRequest, completion: @escaping DataCompletion) -> URLSessionTask? + + // Session Passthroughs + func cachedResponseForURL(_ url: URL, type: Header.PersistItemType) -> CachedURLResponse? + func cachedResponseForURLRequest(_ urlRequest: URLRequest) -> CachedURLResponse? // assumes urlRequest is already populated with the proper cache headers + func uniqueKeyForURL(_ url: URL, type: Header.PersistItemType) -> String? + func cacheResponse(httpUrlResponse: HTTPURLResponse, content: CacheResponseContentType, urlRequest: URLRequest, success: @escaping () -> Void, failure: @escaping (Error) -> Void) + func uniqueFileNameForItemKey(_ itemKey: CacheController.ItemKey, variant: String?) -> String? + func uniqueHeaderFileNameForItemKey(_ itemKey: CacheController.ItemKey, variant: String?) -> String? + func uniqueFileNameForURLRequest(_ urlRequest: URLRequest) -> String? + func itemKeyForURLRequest(_ urlRequest: URLRequest) -> String? + func variantForURLRequest(_ urlRequest: URLRequest) -> String? + + // Bundled migration only - copies files into cache + func writeBundledFiles(mimeType: String, bundledFileURL: URL, urlRequest: URLRequest, completion: @escaping (Result) -> Void) +} + +extension CacheFetching where Self:Fetcher { + + @discardableResult public func dataForURLRequest(_ urlRequest: URLRequest, completion: @escaping DataCompletion) -> URLSessionTask? { + + let task = session.dataTask(with: urlRequest) { (data, urlResponse, error) in + if let error = error { + completion(.failure(error)) + return + } + + guard let unwrappedResponse = urlResponse else { + completion(.failure(CacheFetchingError.missingURLResponse)) + return + } + + if let httpResponse = unwrappedResponse as? HTTPURLResponse, httpResponse.statusCode != 200 { + completion(.failure(RequestError.unexpectedResponse)) + return + } + + if let data = data, + let urlResponse = urlResponse { + let result = CacheFetchingResult(data: data, response: urlResponse) + completion(.success(result)) + } else { + completion(.failure(CacheFetchingError.missingDataAndURLResponse)) + } + } + + task?.resume() + return task + } + + @discardableResult public func dataForURL(_ url: URL, persistType: Header.PersistItemType, headers: [String: String] = [:], completion: @escaping DataCompletion) -> URLSessionTask? { + + guard let urlRequest = session.urlRequestFromPersistence(with: url, persistType: persistType, headers: headers) else { + completion(.failure(CacheFetchingError.unableToDetermineURLRequest)) + return nil + } + + return dataForURLRequest(urlRequest, completion: completion) + } +} + +// MARK: Session Passthroughs + +extension CacheFetching where Self:Fetcher { + + public func cachedResponseForURL(_ url: URL, type: Header.PersistItemType) -> CachedURLResponse? { + return session.cachedResponseForURL(url, type: type) + } + + public func cachedResponseForURLRequest(_ urlRequest: URLRequest) -> CachedURLResponse? { + return session.cachedResponseForURLRequest(urlRequest) + } + + public func uniqueFileNameForURLRequest(_ urlRequest: URLRequest) -> String? { + return session.uniqueFileNameForURLRequest(urlRequest) + } + + public func uniqueKeyForURL(_ url: URL, type: Header.PersistItemType) -> String? { + return session.uniqueKeyForURL(url, type: type) + } + + public func cacheResponse(httpUrlResponse: HTTPURLResponse, content: CacheResponseContentType, urlRequest: URLRequest, success: @escaping () -> Void, failure: @escaping (Error) -> Void) { + + session.cacheResponse(httpUrlResponse: httpUrlResponse, content: content, urlRequest: urlRequest, success: success, failure: failure) + } + + public func writeBundledFiles(mimeType: String, bundledFileURL: URL, urlRequest: URLRequest, completion: @escaping (Result) -> Void) { + session.writeBundledFiles(mimeType: mimeType, bundledFileURL: bundledFileURL, urlRequest: urlRequest, completion: completion) + } + + public func uniqueFileNameForItemKey(_ itemKey: CacheController.ItemKey, variant: String?) -> String? { + return session.uniqueFileNameForItemKey(itemKey, variant: variant) + } + + public func itemKeyForURLRequest(_ urlRequest: URLRequest) -> String? { + return session.itemKeyForURLRequest(urlRequest) + } + + public func variantForURLRequest(_ urlRequest: URLRequest) -> String? { + return session.variantForURLRequest(urlRequest) + } + + public func itemKeyForURL(_ url: URL, type: Header.PersistItemType) -> String? { + return session.itemKeyForURL(url, type: type) + } + + public func variantForURL(_ url: URL, type: Header.PersistItemType) -> String? { + return session.variantForURL(url, type: type) + } + + public func urlRequestFromPersistence(with url: URL, persistType: Header.PersistItemType, cachePolicy: WMFCachePolicy? = nil, headers: [String: String] = [:]) -> URLRequest? { + return session.urlRequestFromPersistence(with: url, persistType: persistType, cachePolicy: cachePolicy, headers: headers) + } + + public func uniqueHeaderFileNameForItemKey(_ itemKey: CacheController.ItemKey, variant: String?) -> String? { + return session.uniqueHeaderFileNameForItemKey(itemKey, variant: variant) + } + + public func isCachedWithURLRequest(_ request: URLRequest, completion: @escaping (Bool) -> Void) { + return session.isCachedWithURLRequest(request, completion: completion) + } +} diff --git a/Apps/Wikipedia/WMF Framework/CacheFileWriter.swift b/Apps/Wikipedia/WMF Framework/CacheFileWriter.swift new file mode 100644 index 0000000..494bbca --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheFileWriter.swift @@ -0,0 +1,246 @@ +import Foundation +import CocoaLumberjackSwift + +enum CacheFileWriterError: Error { + case missingTemporaryFileURL + case missingHeaderItemKey + case missingHTTPResponse + case unableToDetermineSiteURLFromMigration + case unexpectedFetcherTypeForBundledMigration + case unableToDetermineBundledOfflineURLS + case failureToSaveBundledFiles + case unableToPullCachedDataFromNotModified + case missingURLInRequest + case unableToGenerateHTTPURLResponse + case unableToDetermineFileNames +} + +enum CacheFileWriterAddResult { + case success(response: HTTPURLResponse, data: Data) + case failure(Error) +} + +enum CacheFileWriterRemoveResult { + case success + case failure(Error) +} + +final class CacheFileWriter: CacheTaskTracking { + + private let fetcher: CacheFetching + + lazy private var baseCSSFileURL: URL = { + URL(fileURLWithPath: WikipediaAppUtils.assetsPath()) + .appendingPathComponent("pcs-html-converter", isDirectory: true) + .appendingPathComponent("base.css", isDirectory: false) + }() + + lazy private var pcsCSSFileURL: URL = { + URL(fileURLWithPath: WikipediaAppUtils.assetsPath()) + .appendingPathComponent("pcs-html-converter", isDirectory: true) + .appendingPathComponent("pcs.css", isDirectory: false) + }() + + lazy private var pcsJSFileURL: URL = { + URL(fileURLWithPath: WikipediaAppUtils.assetsPath()) + .appendingPathComponent("pcs-html-converter", isDirectory: true) + .appendingPathComponent("pcs.js", isDirectory: false) + }() + + var groupedTasks: [String : [IdentifiedTask]] = [:] + + init(fetcher: CacheFetching) { + self.fetcher = fetcher + + do { + try FileManager.default.createDirectory(at: CacheController.cacheURL, withIntermediateDirectories: true, attributes: nil) + } catch { + DDLogError("Error creating permanent cache: \(error)") + } + } + + func add(groupKey: String, urlRequest: URLRequest, completion: @escaping (CacheFileWriterAddResult) -> Void) { + + let untrackKey = UUID().uuidString + let task = fetcher.dataForURLRequest(urlRequest) { [weak self] (response) in + guard let self = self else { + return + } + + defer { + self.untrackTask(untrackKey: untrackKey, from: groupKey) + } + + switch response { + case .success(let result): + + guard let httpUrlResponse = result.response as? HTTPURLResponse else { + completion(.failure(CacheFileWriterError.missingHTTPResponse)) + return + } + + self.fetcher.cacheResponse(httpUrlResponse: httpUrlResponse, content: .data(result.data), urlRequest: urlRequest, success: { + completion(.success(response: httpUrlResponse, data: result.data)) + }) { (error) in + completion(.failure(error)) + } + + case .failure(let error): + DDLogError("Error downloading data for offline: \(error)\n\(String(describing: response))") + completion(.failure(error)) + return + } + } + + if let task = task { + trackTask(untrackKey: untrackKey, task: task, to: groupKey) + } + } + + func remove(itemKey: String, variant: String?, completion: @escaping (CacheFileWriterRemoveResult) -> Void) { + + guard let fileName = self.fetcher.uniqueFileNameForItemKey(itemKey, variant: variant), + let headerFileName = self.fetcher.uniqueHeaderFileNameForItemKey(itemKey, variant: variant) else { + completion(.failure(CacheFileWriterError.unableToDetermineFileNames)) + return + } + + var responseHeaderRemoveError: Error? = nil + var responseRemoveError: Error? = nil + + // remove response from file system + let responseCachedFileURL = CacheFileWriterHelper.fileURL(for: fileName) + do { + try FileManager.default.removeItem(at: responseCachedFileURL) + } catch let error as NSError { + if !(error.code == NSURLErrorFileDoesNotExist || error.code == NSFileNoSuchFileError) { + responseRemoveError = error + } + } + + // remove response header from file system + let responseHeaderCachedFileURL = CacheFileWriterHelper.fileURL(for: headerFileName) + do { + try FileManager.default.removeItem(at: responseHeaderCachedFileURL) + } catch let error as NSError { + if !(error.code == NSURLErrorFileDoesNotExist || error.code == NSFileNoSuchFileError) { + responseHeaderRemoveError = error + } + } + + if let responseHeaderRemoveError = responseHeaderRemoveError { + completion(.failure(responseHeaderRemoveError)) + return + } + + if let responseRemoveError = responseRemoveError { + completion(.failure(responseRemoveError)) + return + } + + completion(.success) + } + + func uniqueFileNameForItemKey(_ itemKey: CacheController.ItemKey, variant: String?) -> String? { + return fetcher.uniqueFileNameForItemKey(itemKey, variant: variant) + } + + func uniqueFileNameForURLRequest(_ urlRequest: URLRequest) -> String? { + return fetcher.uniqueFileNameForURLRequest(urlRequest) + } + +} + +// MARK: Migration + +extension CacheFileWriter { + + // assumes urlRequest is already populated with the right type header + func addMobileHtmlContentForMigration(content: String, urlRequest: URLRequest, success: @escaping () -> Void, failure: @escaping (Error) -> Void) { + + guard let url = urlRequest.url else { + failure(CacheFileWriterError.missingURLInRequest) + return + } + + // artificially create HTTPURLResponse + guard let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: ["Content-Type": "text/html"]) else { + failure(CacheFileWriterError.unableToGenerateHTTPURLResponse) + return + } + + fetcher.cacheResponse(httpUrlResponse: response, content: .string(content), urlRequest: urlRequest, success: { + success() + }) { (error) in + failure(error) + } + } + + func addBundledResourcesForMigration(urlRequests:[URLRequest], success: @escaping ([URLRequest]) -> Void, failure: @escaping (Error) -> Void) { + + guard let articleFetcher = fetcher as? ArticleFetcher else { + failure(CacheFileWriterError.unexpectedFetcherTypeForBundledMigration) + return + } + + guard let bundledOfflineResources = articleFetcher.bundledOfflineResourceURLs() else { + failure(CacheFileWriterError.unableToDetermineBundledOfflineURLS) + return + } + + var failedURLRequests: [URLRequest] = [] + var succeededURLRequests: [URLRequest] = [] + + for urlRequest in urlRequests { + + guard let urlString = urlRequest.url?.absoluteString else { + continue + } + + switch urlString { + case bundledOfflineResources.baseCSS.absoluteString: + + fetcher.writeBundledFiles(mimeType: "text/css", bundledFileURL: baseCSSFileURL, urlRequest: urlRequest) { (result) in + switch result { + case .success: + succeededURLRequests.append(urlRequest) + case .failure: + failedURLRequests.append(urlRequest) + } + } + + case bundledOfflineResources.pcsCSS.absoluteString: + + fetcher.writeBundledFiles(mimeType: "text/css", bundledFileURL: pcsCSSFileURL, urlRequest: urlRequest) { (result) in + switch result { + case .success: + succeededURLRequests.append(urlRequest) + case .failure: + failedURLRequests.append(urlRequest) + } + } + + case bundledOfflineResources.pcsJS.absoluteString: + + fetcher.writeBundledFiles(mimeType: "application/javascript", bundledFileURL: pcsJSFileURL, urlRequest: urlRequest) { (result) in + switch result { + case .success: + succeededURLRequests.append(urlRequest) + case .failure: + failedURLRequests.append(urlRequest) + } + } + + default: + failedURLRequests.append(urlRequest) + } + } + + if succeededURLRequests.count == 0 { + failure(CacheFileWriterError.failureToSaveBundledFiles) + return + } + + success(succeededURLRequests) + } +} diff --git a/Apps/Wikipedia/WMF Framework/CacheFileWriterHelper.swift b/Apps/Wikipedia/WMF Framework/CacheFileWriterHelper.swift new file mode 100644 index 0000000..50a832d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheFileWriterHelper.swift @@ -0,0 +1,139 @@ +import Foundation + +enum CacheFileWriterHelperError: Error { + case unexpectedHeaderFieldsType +} + +final class CacheFileWriterHelper { + static func fileURL(for key: String) -> URL { + return CacheController.cacheURL.appendingPathComponent(key, isDirectory: false) + } + + static func saveData(data: Data, toNewFileWithKey key: String, completion: @escaping (FileSaveResult) -> Void) { + do { + let newFileURL = self.fileURL(for: key) + try data.write(to: newFileURL) + completion(.success) + } catch let error as NSError { + if error.domain == NSCocoaErrorDomain, error.code == NSFileWriteFileExistsError { + completion(.exists) + } else { + completion(.failure(error)) + } + } catch let error { + completion(.failure(error)) + } + } + + static func copyFile(from fileURL: URL, toNewFileWithKey key: String, completion: @escaping (FileSaveResult) -> Void) { + do { + let newFileURL = self.fileURL(for: key) + try FileManager.default.copyItem(at: fileURL, to: newFileURL) + completion(.success) + } catch let error as NSError { + if error.domain == NSCocoaErrorDomain, error.code == NSFileWriteFileExistsError { + completion(.exists) + } else { + completion(.failure(error)) + } + } catch let error { + completion(.failure(error)) + } + } + + static func saveResponseHeader(httpUrlResponse: HTTPURLResponse, toNewFileName fileName: String, completion: @escaping (FileSaveResult) -> Void) { + + guard let headerFields = httpUrlResponse.allHeaderFields as? [String: String] else { + completion(.failure(CacheFileWriterHelperError.unexpectedHeaderFieldsType)) + return + } + + saveResponseHeader(headerFields: headerFields, toNewFileName: fileName, completion: completion) + } + + static func saveResponseHeader(headerFields: [String: String], toNewFileName fileName: String, completion: (FileSaveResult) -> Void) { + do { + let contentData: Data = try NSKeyedArchiver.archivedData(withRootObject: headerFields, requiringSecureCoding: false) + let newFileURL = self.fileURL(for: fileName) + try contentData.write(to: newFileURL) + completion(.success) + } catch let error as NSError { + if error.domain == NSCocoaErrorDomain, error.code == NSFileWriteFileExistsError { + completion(.exists) + } else { + completion(.failure(error)) + } + } catch let error { + completion(.failure(error)) + } + } + + static func replaceResponseHeaderWithURLResponse(_ httpUrlResponse: HTTPURLResponse, atFileName fileName: String, completion: @escaping (FileSaveResult) -> Void) { + + guard let headerFields = httpUrlResponse.allHeaderFields as? [String: String] else { + completion(.failure(CacheFileWriterHelperError.unexpectedHeaderFieldsType)) + return + } + + replaceResponseHeaderWithHeaderFields(headerFields, atFileName: fileName, completion: completion) + } + + static func replaceResponseHeaderWithHeaderFields(_ headerFields:[String: String], atFileName fileName: String, completion: @escaping (FileSaveResult) -> Void) { + do { + let headerData: Data = try NSKeyedArchiver.archivedData(withRootObject: headerFields, requiringSecureCoding:false) + replaceFileWithData(headerData, fileName: fileName, completion: completion) + } catch let error { + completion(.failure(error)) + } + } + + static func replaceFileWithData(_ data: Data, fileName: String, completion: @escaping (FileSaveResult) -> Void) { + let destinationURL = fileURL(for: fileName) + do { + let temporaryDirectoryURL = try FileManager.default.url(for: .itemReplacementDirectory, + in: .userDomainMask, + appropriateFor: destinationURL, + create: true) + + let temporaryFileName = UUID().uuidString + + let temporaryFileURL = temporaryDirectoryURL.appendingPathComponent(temporaryFileName) + + try data.write(to: temporaryFileURL, + options: .atomic) + + _ = try FileManager.default.replaceItemAt(destinationURL, withItemAt: temporaryFileURL) + + try FileManager.default.removeItem(at: temporaryDirectoryURL) + + completion(.success) + + } catch let error { + completion(.failure(error)) + } + } + + + static func saveContent(_ content: String, toNewFileName fileName: String, completion: @escaping (FileSaveResult) -> Void) { + + do { + let newFileURL = self.fileURL(for: fileName) + try content.write(to: newFileURL, atomically: true, encoding: .utf8) + completion(.success) + } catch let error as NSError { + if error.domain == NSCocoaErrorDomain, error.code == NSFileWriteFileExistsError { + completion(.exists) + } else { + completion(.failure(error)) + } + } catch let error { + completion(.failure(error)) + } + } +} + +enum FileSaveResult { + case exists + case success + case failure(Error) +} diff --git a/Apps/Wikipedia/WMF Framework/CacheGatekeeper.swift b/Apps/Wikipedia/WMF Framework/CacheGatekeeper.swift new file mode 100644 index 0000000..65c9c36 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheGatekeeper.swift @@ -0,0 +1,214 @@ +import Foundation + +final class CacheGatekeeper { + + private let queue = DispatchQueue(label: "org.wikimedia.cache.gatekeeper") + + // Used when adding or removing the same uniqueKey rapidly. Individual completion block is queued here until uniqueKey is determined complete in CacheController. Note this complete can come from another groupKey. Queued completions are then called and cleaned out. + private var individualCompletions: [CacheController.UniqueKey: [CacheController.IndividualCompletionBlock]] = [:] + + // Used when adding or removing the same groupKey rapidly. Completion block is queued here until group is determined complete in CacheController. Queued completions are then called and cleaned out. + private var groupCompletions: [CacheController.GroupKey: [CacheController.GroupCompletionBlock]] = [:] + + // Used when adding THEN removing (or vice versa) the same group key rapidly. Completion blocks are queued here until an add or remove completes. Queued completions are then called and cleaned out. + private var queuedAddsWhileWaitingOnRemoves: [CacheController.GroupKey: [() -> Void]] = [:] + private var queuedRemovesWhileWaitingOnAdds: [CacheController.GroupKey: [() -> Void]] = [:] + private var currentlyAdding: [CacheController.GroupKey] = [] + private var currentlyRemoving: [CacheController.GroupKey] = [] + + func numberOfQueuedGroupCompletions(for groupKey: CacheController.GroupKey) -> Int { + + queue.sync { + return groupCompletions[groupKey]?.count ?? 0 + } + } + + func numberOfQueuedIndividualCompletions(for uniqueKey: CacheController.UniqueKey) -> Int { + + queue.sync { + return individualCompletions[uniqueKey]?.count ?? 0 + } + + } + + func queueGroupCompletion(groupKey: CacheController.GroupKey, groupCompletion: @escaping CacheController.GroupCompletionBlock) { + + queue.async { [weak self] in + + guard let self = self else { + return + } + + var currentCompletions = self.groupCompletions[groupKey] ?? [] + currentCompletions.append(groupCompletion) + + self.groupCompletions[groupKey] = currentCompletions + } + } + + func queueIndividualCompletion(uniqueKey: CacheController.UniqueKey, individualCompletion: @escaping CacheController.IndividualCompletionBlock) { + + queue.async { [weak self] in + + guard let self = self else { + return + } + + var currentCompletions = self.individualCompletions[uniqueKey] ?? [] + currentCompletions.append(individualCompletion) + + self.individualCompletions[uniqueKey] = currentCompletions + } + } + + func runAndRemoveGroupCompletions(groupKey: CacheController.GroupKey, groupResult: CacheController.FinalGroupResult) { + + queue.async { [weak self] in + + guard let self = self else { + return + } + + if let completions = self.groupCompletions[groupKey] { + for completion in completions { + completion(groupResult) + } + } + + self.groupCompletions[groupKey]?.removeAll() + } + } + + func runAndRemoveIndividualCompletions(uniqueKey: CacheController.UniqueKey, individualResult: CacheController.FinalIndividualResult) { + + queue.async { [weak self] in + + guard let self = self else { + return + } + + if let completions = self.individualCompletions[uniqueKey] { + for completion in completions { + completion(individualResult) + } + } + + self.individualCompletions[uniqueKey]?.removeAll() + } + } + + func addCurrentlyAddingGroupKey(_ groupKey: CacheController.GroupKey) { + + queue.async { [weak self] in + self?.currentlyAdding.append(groupKey) + } + } + + func addCurrentlyRemovingGroupKey(_ groupKey: CacheController.GroupKey) { + + queue.async { [weak self] in + self?.currentlyRemoving.append(groupKey) + } + } + + func removeCurrentlyAddingGroupKey(_ groupKey: CacheController.GroupKey) { + + queue.async { [weak self] in + + guard let self = self else { + return + } + + let filteredCurrentlyAdding = self.currentlyAdding.filter { $0 != groupKey } + self.currentlyAdding = filteredCurrentlyAdding + } + } + + func removeCurrentlyRemovingGroupKey(_ groupKey: CacheController.GroupKey) { + + queue.async { [weak self] in + + guard let self = self else { + return + } + + let filteredCurrentlyRemoving = self.currentlyRemoving.filter { $0 != groupKey } + self.currentlyRemoving = filteredCurrentlyRemoving + } + } + + func shouldQueueAddCompletion(groupKey: CacheController.GroupKey) -> Bool { + + queue.sync { + return currentlyRemoving.contains(groupKey) + } + } + + func shouldQueueRemoveCompletion(groupKey: CacheController.GroupKey) -> Bool { + + queue.sync { + return currentlyAdding.contains(groupKey) + } + } + + func queueAddCompletion(groupKey: CacheController.GroupKey, completion: @escaping () -> Void) { + + queue.async { [weak self] in + + guard let self = self else { + return + } + + var currentCompletions = self.queuedAddsWhileWaitingOnRemoves[groupKey] ?? [] + currentCompletions.append(completion) + self.queuedAddsWhileWaitingOnRemoves[groupKey] = currentCompletions + } + } + + func queueRemoveCompletion(groupKey: CacheController.GroupKey, completion: @escaping () -> Void) { + + queue.async { [weak self] in + + guard let self = self else { + return + } + + var currentCompletions = self.queuedRemovesWhileWaitingOnAdds[groupKey] ?? [] + currentCompletions.append(completion) + self.queuedRemovesWhileWaitingOnAdds[groupKey] = currentCompletions + + } + } + + func runAndRemoveQueuedRemoves(groupKey: CacheController.GroupKey) { + + queue.async { [weak self] in + + guard let self = self else { + return + } + + if let completion = self.queuedRemovesWhileWaitingOnAdds[groupKey]?.first { + completion() + } + + self.queuedRemovesWhileWaitingOnAdds[groupKey]?.removeAll() + } + } + + func runAndRemoveQueuedAdds(groupKey: CacheController.GroupKey) { + + queue.async { [weak self] in + + guard let self = self else { + return + } + + if let completion = self.queuedAddsWhileWaitingOnRemoves[groupKey]?.first { + completion() + } + + self.queuedAddsWhileWaitingOnRemoves[groupKey]?.removeAll() + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/CacheGroup+CoreDataClass.swift b/Apps/Wikipedia/WMF Framework/CacheGroup+CoreDataClass.swift new file mode 100644 index 0000000..cf29a50 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheGroup+CoreDataClass.swift @@ -0,0 +1,7 @@ +import Foundation +import CoreData + +@objc(WMFCacheGroup) +public class CacheGroup: NSManagedObject { + +} diff --git a/Apps/Wikipedia/WMF Framework/CacheGroup+CoreDataProperties.swift b/Apps/Wikipedia/WMF Framework/CacheGroup+CoreDataProperties.swift new file mode 100644 index 0000000..21f2e91 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheGroup+CoreDataProperties.swift @@ -0,0 +1,49 @@ +import Foundation +import CoreData + + +extension CacheGroup { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "CacheGroup") + } + + @NSManaged public var key: String? + @NSManaged public var cacheItems: NSSet? + @NSManaged public var mustHaveCacheItems: NSSet? + +} + +// MARK: Generated accessors for cacheItems +extension CacheGroup { + + @objc(addCacheItemsObject:) + @NSManaged public func addToCacheItems(_ value: CacheItem) + + @objc(removeCacheItemsObject:) + @NSManaged public func removeFromCacheItems(_ value: CacheItem) + + @objc(addCacheItems:) + @NSManaged public func addToCacheItems(_ values: NSSet) + + @objc(removeCacheItems:) + @NSManaged public func removeFromCacheItems(_ values: NSSet) + +} + +// MARK: Generated accessors for mustHaveCacheItems +extension CacheGroup { + + @objc(addMustHaveCacheItemsObject:) + @NSManaged public func addToMustHaveCacheItems(_ value: CacheItem) + + @objc(removeMustHaveCacheItemsObject:) + @NSManaged public func removeFromMustHaveCacheItems(_ value: CacheItem) + + @objc(addMustHaveCacheItems:) + @NSManaged public func addToMustHaveCacheItems(_ values: NSSet) + + @objc(removeMustHaveCacheItems:) + @NSManaged public func removeFromMustHaveCacheItems(_ values: NSSet) + +} diff --git a/Apps/Wikipedia/WMF Framework/CacheItem+CoreDataClass.swift b/Apps/Wikipedia/WMF Framework/CacheItem+CoreDataClass.swift new file mode 100644 index 0000000..299c63a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheItem+CoreDataClass.swift @@ -0,0 +1,7 @@ +import Foundation +import CoreData + +@objc(WMFCacheItem) +public class CacheItem: NSManagedObject { + +} diff --git a/Apps/Wikipedia/WMF Framework/CacheItem+CoreDataProperties.swift b/Apps/Wikipedia/WMF Framework/CacheItem+CoreDataProperties.swift new file mode 100644 index 0000000..81e7a1a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheItem+CoreDataProperties.swift @@ -0,0 +1,53 @@ +import Foundation +import CoreData + + +extension CacheItem { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "CacheItem") + } + + @NSManaged public var date: Date? + @NSManaged public var isDownloaded: Bool + @NSManaged public var key: String? + @NSManaged public var url: URL? + @NSManaged public var variant: String? + @NSManaged public var cacheGroups: NSSet? + @NSManaged public var mustHaveCacheGroups: NSSet? + +} + +// MARK: Generated accessors for cacheGroups +extension CacheItem { + + @objc(addCacheGroupsObject:) + @NSManaged public func addToCacheGroups(_ value: CacheGroup) + + @objc(removeCacheGroupsObject:) + @NSManaged public func removeFromCacheGroups(_ value: CacheGroup) + + @objc(addCacheGroups:) + @NSManaged public func addToCacheGroups(_ values: NSSet) + + @objc(removeCacheGroups:) + @NSManaged public func removeFromCacheGroups(_ values: NSSet) + +} + +// MARK: Generated accessors for mustHaveCacheGroups +extension CacheItem { + + @objc(addMustHaveCacheGroupsObject:) + @NSManaged public func addToMustHaveCacheGroups(_ value: CacheGroup) + + @objc(removeMustHaveCacheGroupsObject:) + @NSManaged public func removeFromMustHaveCacheGroups(_ value: CacheGroup) + + @objc(addMustHaveCacheGroups:) + @NSManaged public func addToMustHaveCacheGroups(_ values: NSSet) + + @objc(removeMustHaveCacheGroups:) + @NSManaged public func removeFromMustHaveCacheGroups(_ values: NSSet) + +} diff --git a/Apps/Wikipedia/WMF Framework/CacheItemMappingModel.xcmappingmodel/xcmapping.xml b/Apps/Wikipedia/WMF Framework/CacheItemMappingModel.xcmappingmodel/xcmapping.xml new file mode 100644 index 0000000..7262b78 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheItemMappingModel.xcmappingmodel/xcmapping.xml @@ -0,0 +1,126 @@ + + + + + + 134481920 + F7713431-094A-4464-BECD-22EBEC32A9FB + 114 + + + + NSPersistenceFrameworkVersion + 977 + NSStoreModelVersionHashes + + XDDevAttributeMapping + + 0plcXXRN7XHKl5CcF+fwriFmUpON3ZtcI/AfK748aWc= + + XDDevEntityMapping + + qeN1Ym3TkWN1G6dU9RfX6Kd2ccEvcDVWHpd3LpLgboI= + + XDDevMappingModel + + EqtMzvRnVZWkXwBHu4VeVGy8UyoOe+bi67KC79kphlQ= + + XDDevPropertyMapping + + XN33V44TTGY4JETlMoOB5yyTKxB+u4slvDIinv0rtGA= + + XDDevRelationshipMapping + + akYY9LhehVA/mCb4ATLWuI9XGLcjpm14wWL1oEBtIcs= + + + NSStoreModelVersionHashesDigest + +Hmc2uYZK6og+Pvx5GUJ7oW75UG4V/ksQanTjfTKUnxyGWJRMtB5tIRgVwGsrd7lz/QR57++wbvWsr6nxwyS0A== + NSStoreModelVersionHashesVersion + 3 + NSStoreModelVersionIdentifiers + + + + + + + + + WMF.CacheItemMigrationPolicy + CacheItem + Undefined + 1 + CacheItem + 1 + + + + + + key + + + + date + + + + key + + + + 1 + mustHaveCacheItems + + + + 1 + cacheItems + + + + WMF Framework/Cache.xcdatamodeld/Cache.xcdatamodel + YnBsaXN0MDDUAAEAAgADAAQABQAGAAcAClgkdmVyc2lvblkkYXJjaGl2ZXJUJHRvcFgkb2JqZWN0 +cxIAAYagXxAPTlNLZXllZEFyY2hpdmVy0QAIAAlUcm9vdIABrxEBAQALAAwAGwA3ADgAOQBDAEQARQBgAGEAYgBoAGkAdQCLAIwAjQCOAI8AkACRAJIAkwCUAK0AsAC3AL0AzADbAN4A7QD8AP8AXwEPAR4BIgEmATUBOwE8AUQBUwFUAV0BZQFmAWcBfAF9AYUBhgGHAZMBpwGoAakBqgGrAawBrQGuAa8BvgHNAdwB4AHvAfAB/wIOAh0CLAI4AkoCSwJMAk0CTgJPAlACUQJgAm8CfgKNAo4CnQKsArsCwwLYAtkC4QLiAu4DAgMRAyADLwMzA0IDUQNgA28DfgOKA5wDnQOeA58DoAOhA6IDowAxA7IDwQPCA9ED4AP6A/sEAQQNBCMEMgQ1BDcERgRVBFgEZwR2BHkEiASXBJsEqgS5BLoExgTHBMgEyQTeBN8E5wTzBQcFFgUlBTQFOAVHBVYFZQV0BYMFjwWhBbAFvwXOBd0F7AX7BgoGEgYnBigGMAY8BlAGXwZuBn0GgQaQBp8Grga9BswG2AbqBvkHCAcXByYHNQdEB1MHaAdpB3EHfQeRB6AHrwe+B8IH0QfgB+8H/ggNCBkIKwg6CDsISghZCGgIaQh4CIcIlgirCKwItAjACNQI4wjyCQEJBQkUCSMJMglBCVAJXAluCX0JjAmbCaoJqwm6CckJ2AnZCdwJ5Qn0CgMKEgoVChkKHQohCikKLAowCjFVJG51bGzXAA0ADgAPABAAEQASABMAFAAVABYAFwAYABcAGl8QD194ZF9yb290UGFja2FnZVYkY2xhc3NcX3hkX2NvbW1lbnRzXxAQX3hkX21vZGVsTWFuYWdlcl8QFV9jb25maWd1cmF0aW9uc0J5TmFtZV1feGRfbW9kZWxOYW1lXxAXX21vZGVsVmVyc2lvbklkZW50aWZpZXKAAoEBAID9gACA/oAAgP/eABwAHQAeAB8AIAAhACIADgAjACQAJQAmACcAKAApACoAKwAJACkAFwAvADAAMQAyADMAKQApABdfEBxYREJ1Y2tldEZvckNsYXNzZXN3YXNFbmNvZGVkXxAaWERCdWNrZXRGb3JQYWNrYWdlc3N0b3JhZ2VfEBxYREJ1Y2tldEZvckludGVyZmFjZXNzdG9yYWdlXxAPX3hkX293bmluZ01vZGVsXxAdWERCdWNrZXRGb3JQYWNrYWdlc3dhc0VuY29kZWRWX293bmVyXxAbWERCdWNrZXRGb3JEYXRhVHlwZXNzdG9yYWdlW192aXNpYmlsaXR5XxAZWERCdWNrZXRGb3JDbGFzc2Vzc3RvcmFnZVVfbmFtZV8QH1hEQnVja2V0Rm9ySW50ZXJmYWNlc3dhc0VuY29kZWRfEB5YREJ1Y2tldEZvckRhdGFUeXBlc3dhc0VuY29kZWRfEBBfdW5pcXVlRWxlbWVudElEgASA+4D5gAGABIAAgPqA/BAAgAWAA4AEgASAAFBTWUVT0wA6ADsADgA8AD8AQldOUy5rZXlzWk5TLm9iamVjdHOiAD0APoAGgAeiAEAAQYAIgHqAJlpDYWNoZUdyb3VwWUNhY2hlSXRlbd8QEABGAEcASABJACEASgBLACMATABNAA4AJQBOAE8AKABQAFEAUgApACkAFABWAFcAMQApAFEAWgA9AFEAXQBeAF9fECFYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc29yZGVyZWRfECFYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc3N0b3JhZ2VfECBYREJ1Y2tldEZvclN0ZXJlb3R5cGVzd2FzRW5jb2RlZF8QJFhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzd2FzRW5jb2RlZF8QHVhEQnVja2V0Rm9yU3RlcmVvdHlwZXNzdG9yYWdlXxAkWERCdWNrZXRGb3JHZW5lcmFsaXphdGlvbnNkdXBsaWNhdGVzXxAkWERCdWNrZXRGb3JHZW5lcmFsaXphdGlvbnN3YXNFbmNvZGVkXxAdWERCdWNrZXRGb3JTdGVyZW90eXBlc29yZGVyZWRfECFYREJ1Y2tldEZvckdlbmVyYWxpemF0aW9uc29yZGVyZWRfECFYREJ1Y2tldEZvckdlbmVyYWxpemF0aW9uc3N0b3JhZ2VbX2lzQWJzdHJhY3SACoAugASABIACgAuA8oAEgAqA9IAGgAqA+IAJCBK7EpffV29yZGVyZWTTADoAOwAOAGMAZQBCoQBkgAyhAGaADYAmXlhEX1BTdGVyZW90eXBl2QAhACUAagAOACgAawAjAFAAbABAAGQAUQBwABcAKQAxAF8AdF8QIVhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzb3JkZXJlZF8QJFhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzd2FzRW5jb2RlZF8QIVhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzc3RvcmFnZYAIgAyACoAtgACABAiADtMAOgA7AA4AdgCAAEKpAHcAeAB5AHoAewB8AH0AfgB/gA+AEIARgBKAE4AUgBWAFoAXqQCBAIIAgwCEAIUAhgCHAIgAiYAYgByAHYAfgCCAIoAkgCeAK4AmXxATWERQTUNvbXBvdW5kSW5kZXhlc18QEFhEX1BTS19lbGVtZW50SURfEBlYRFBNVW5pcXVlbmVzc0NvbnN0cmFpbnRzXxAaWERfUFNLX3ZlcnNpb25IYXNoTW9kaWZpZXJfEBlYRF9QU0tfZmV0Y2hSZXF1ZXN0c0FycmF5XxARWERfUFNLX2lzQWJzdHJhY3RfEA9YRF9QU0tfdXNlckluZm9fEBNYRF9QU0tfY2xhc3NNYXBwaW5nXxAWWERfUFNLX2VudGl0eUNsYXNzTmFtZd8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXAKAAFwBmAF8AXwBfADEAXwCnAHcAXwBfABcAX1VfdHlwZVhfZGVmYXVsdFxfYXNzb2NpYXRpb25bX2lzUmVhZE9ubHlZX2lzU3RhdGljWV9pc1VuaXF1ZVpfaXNEZXJpdmVkWl9pc09yZGVyZWRcX2lzQ29tcG9zaXRlV19pc0xlYWaAAIAZgACADQgICAiAG4APCAiAAAjSADsADgCuAK+ggBrSALEAsgCzALRaJGNsYXNzbmFtZVgkY2xhc3Nlc15OU011dGFibGVBcnJheaMAswC1ALZXTlNBcnJheVhOU09iamVjdNIAsQCyALgAuV8QEFhEVU1MUHJvcGVydHlJbXCkALoAuwC8ALZfEBBYRFVNTFByb3BlcnR5SW1wXxAUWERVTUxOYW1lZEVsZW1lbnRJbXBfEA9YRFVNTEVsZW1lbnRJbXDfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcAZgBfAF8AXwAxAF8ApwB4AF8AXwAXAF+AAIAAgACADQgICAiAG4AQCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwDOABcAZgBfAF8AXwAxAF8ApwB5AF8AXwAXAF+AAIAegACADQgICAiAG4ARCAiAAAjSADsADgDcAK+ggBrfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcAZgBfAF8AXwAxAF8ApwB6AF8AXwAXAF+AAIAAgACADQgICAiAG4ASCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwDvABcAZgBfAF8AXwAxAF8ApwB7AF8AXwAXAF+AAIAhgACADQgICAiAG4ATCAiAAAjSADsADgD9AK+ggBrfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcAZgBfAF8AXwAxAF8ApwB8AF8AXwAXAF+AAIAjgACADQgICAiAG4AUCAiAAAgI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBEQAXAGYAXwBfAF8AMQBfAKcAfQBfAF8AFwBfgACAJYAAgA0ICAgIgBuAFQgIgAAI0wA6ADsADgEfASAAQqCggCbSALEAsgEjASRfEBNOU011dGFibGVEaWN0aW9uYXJ5owEjASUAtlxOU0RpY3Rpb25hcnnfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEoABcAZgBfAF8AXwAxAF8ApwB+AF8AXwAXAF+AAIAogACADQgICAiAG4AWCAiAAAjWACUADgAoAFAAIQAjATYBNwAXAF8AFwAxgCmAKoAACIAAXxAUWERHZW5lcmljUmVjb3JkQ2xhc3PSALEAsgE9AT5dWERVTUxDbGFzc0ltcKYBPwFAAUEBQgFDALZdWERVTUxDbGFzc0ltcF8QElhEVU1MQ2xhc3NpZmllckltcF8QEVhEVU1MTmFtZXNwYWNlSW1wXxAUWERVTUxOYW1lZEVsZW1lbnRJbXBfEA9YRFVNTEVsZW1lbnRJbXDfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwFGABcAZgBfAF8AXwAxAF8ApwB/AF8AXwAXAF+AAIAsgACADQgICAiAG4AXCAiAAAhdV01GQ2FjaGVHcm91cNIAsQCyAVUBVl8QElhEVU1MU3RlcmVvdHlwZUltcKcBVwFYAVkBWgFbAVwAtl8QElhEVU1MU3RlcmVvdHlwZUltcF1YRFVNTENsYXNzSW1wXxASWERVTUxDbGFzc2lmaWVySW1wXxARWERVTUxOYW1lc3BhY2VJbXBfEBRYRFVNTE5hbWVkRWxlbWVudEltcF8QD1hEVU1MRWxlbWVudEltcNMAOgA7AA4BXgFhAEKiAV8BYIAvgDCiAWIBY4AxgFyAJlNrZXlaY2FjaGVJdGVtc98QEgCVAJYAlwFoACEAmQCaAWkAIwCYAWoAmwAOACUAnACdACgAngAXABcAFwApAEAAXwBfAXIAMQBfAFEAXwF2AV8AXwBfAXoAX18QIFhEQnVja2V0Rm9yU3RlcmVvdHlwZXN3YXNFbmNvZGVkXxAdWERCdWNrZXRGb3JTdGVyZW90eXBlc3N0b3JhZ2VfEB1YREJ1Y2tldEZvclN0ZXJlb3R5cGVzb3JkZXJlZIAAgACAAIAEgAgICIAzCIAKCIBbgC8ICIAyCBI3UY6E0wA6ADsADgF+AYEAQqIBfwGAgDSANaIBggGDgDaASoAmXxASWERfUFByb3BTdGVyZW90eXBlXxASWERfUEF0dF9TdGVyZW90eXBl2QAhACUBiAAOACgBiQAjAFABigFiAX8AUQBwABcAKQAxAF8Bkl8QIVhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzb3JkZXJlZF8QJFhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzd2FzRW5jb2RlZF8QIVhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzc3RvcmFnZYAxgDSACoAtgACABAiAN9MAOgA7AA4BlAGdAEKoAZUBlgGXAZgBmQGaAZsBnIA4gDmAOoA7gDyAPYA+gD+oAZ4BnwGgAaEBogGjAaQBpYBAgEGAQoBEgEaAR4BIgEmAJl8QG1hEX1BQU0tfaXNTdG9yZWRJblRydXRoRmlsZV8QG1hEX1BQU0tfdmVyc2lvbkhhc2hNb2RpZmllcl8QEFhEX1BQU0tfdXNlckluZm9fEBFYRF9QUFNLX2lzSW5kZXhlZF8QElhEX1BQU0tfaXNPcHRpb25hbF8QGlhEX1BQU0tfaXNTcG90bGlnaHRJbmRleGVkXxARWERfUFBTS19lbGVtZW50SURfEBNYRF9QUFNLX2lzVHJhbnNpZW503xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXAYIAXwBfAF8AMQBfAKcBlQBfAF8AFwBfgACAI4AAgDYICAgIgBuAOAgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXAYIAXwBfAF8AMQBfAKcBlgBfAF8AFwBfgACAAIAAgDYICAgIgBuAOQgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBzwAXAYIAXwBfAF8AMQBfAKcBlwBfAF8AFwBfgACAQ4AAgDYICAgIgBuAOggIgAAI0wA6ADsADgHdAd4AQqCggCbfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwHiABcBggBfAF8AXwAxAF8ApwGYAF8AXwAXAF+AAIBFgACANggICAiAG4A7CAiAAAgJ3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXAYIAXwBfAF8AMQBfAKcBmQBfAF8AFwBfgACAI4AAgDYICAgIgBuAPAgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXAYIAXwBfAF8AMQBfAKcBmgBfAF8AFwBfgACAI4AAgDYICAgIgBuAPQgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXAYIAXwBfAF8AMQBfAKcBmwBfAF8AFwBfgACAAIAAgDYICAgIgBuAPggIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXAYIAXwBfAF8AMQBfAKcBnABfAF8AFwBfgACAI4AAgDYICAgIgBuAPwgIgAAI2QAhACUCLQAOACgCLgAjAFACLwFiAYAAUQBwABcAKQAxAF8CN18QIVhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzb3JkZXJlZF8QJFhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzd2FzRW5jb2RlZF8QIVhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzc3RvcmFnZYAxgDWACoAtgACABAiAS9MAOgA7AA4COQJBAEKnAjoCOwI8Aj0CPgI/AkCATIBNgE6AT4BQgFGAUqcCQgJDAkQCRQJGAkcCSIBTgFSAVYBWgFiAWYBagCZfEB1YRF9QQXR0S19kZWZhdWx0VmFsdWVBc1N0cmluZ18QKFhEX1BBdHRLX2FsbG93c0V4dGVybmFsQmluYXJ5RGF0YVN0b3JhZ2VfEBdYRF9QQXR0S19taW5WYWx1ZVN0cmluZ18QFlhEX1BBdHRLX2F0dHJpYnV0ZVR5cGVfEBdYRF9QQXR0S19tYXhWYWx1ZVN0cmluZ18QHVhEX1BBdHRLX3ZhbHVlVHJhbnNmb3JtZXJOYW1lXxAgWERfUEF0dEtfcmVndWxhckV4cHJlc3Npb25TdHJpbmffEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcBgwBfAF8AXwAxAF8ApwI6AF8AXwAXAF+AAIAAgACASggICAiAG4BMCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcBgwBfAF8AXwAxAF8ApwI7AF8AXwAXAF+AAIAjgACASggICAiAG4BNCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcBgwBfAF8AXwAxAF8ApwI8AF8AXwAXAF+AAIAAgACASggICAiAG4BOCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwKAABcBgwBfAF8AXwAxAF8ApwI9AF8AXwAXAF+AAIBXgACASggICAiAG4BPCAiAAAgRArzfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcBgwBfAF8AXwAxAF8ApwI+AF8AXwAXAF+AAIAAgACASggICAiAG4BQCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcBgwBfAF8AXwAxAF8ApwI/AF8AXwAXAF+AAIAAgACASggICAiAG4BRCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcBgwBfAF8AXwAxAF8ApwJAAF8AXwAXAF+AAIAAgACASggICAiAG4BSCAiAAAjSALEAsgK8Ar1dWERQTUF0dHJpYnV0ZaYCvgK/AsACwQLCALZdWERQTUF0dHJpYnV0ZVxYRFBNUHJvcGVydHlfEBBYRFVNTFByb3BlcnR5SW1wXxAUWERVTUxOYW1lZEVsZW1lbnRJbXBfEA9YRFVNTEVsZW1lbnRJbXDfEBIAlQCWAJcCxAAhAJkAmgLFACMAmALGAJsADgAlAJwAnQAoAJ4AFwAXABcAKQBAAF8AXwLOADEAXwBRAF8C0gFgAF8AXwLWAF9fECBYREJ1Y2tldEZvclN0ZXJlb3R5cGVzd2FzRW5jb2RlZF8QHVhEQnVja2V0Rm9yU3RlcmVvdHlwZXNzdG9yYWdlXxAdWERCdWNrZXRGb3JTdGVyZW90eXBlc29yZGVyZWSAAIAAgACABIAICAiAXgiACgiAqYAwCAiAXQgSMJfJmNMAOgA7AA4C2gLdAEKiAX8C3IA0gF+iAt4C34BggGuAJl8QEFhEX1BSX1N0ZXJlb3R5cGXZACEAJQLjAA4AKALkACMAUALlAWMBfwBRAHAAFwApADEAXwLtXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNvcmRlcmVkXxAkWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXN3YXNFbmNvZGVkXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNzdG9yYWdlgFyANIAKgC2AAIAECIBh0wA6ADsADgLvAvgAQqgBlQGWAZcBmAGZAZoBmwGcgDiAOYA6gDuAPIA9gD6AP6gC+QL6AvsC/AL9Av4C/wMAgGKAY4BkgGaAZ4BogGmAaoAm3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXAt4AXwBfAF8AMQBfAKcBlQBfAF8AFwBfgACAI4AAgGAICAgIgBuAOAgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXAt4AXwBfAF8AMQBfAKcBlgBfAF8AFwBfgACAAIAAgGAICAgIgBuAOQgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcDIgAXAt4AXwBfAF8AMQBfAKcBlwBfAF8AFwBfgACAZYAAgGAICAgIgBuAOggIgAAI0wA6ADsADgMwAzEAQqCggCbfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcC3gBfAF8AXwAxAF8ApwGYAF8AXwAXAF+AAIAjgACAYAgICAiAG4A7CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwHiABcC3gBfAF8AXwAxAF8ApwGZAF8AXwAXAF+AAIBFgACAYAgICAiAG4A8CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcC3gBfAF8AXwAxAF8ApwGaAF8AXwAXAF+AAIAjgACAYAgICAiAG4A9CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcC3gBfAF8AXwAxAF8ApwGbAF8AXwAXAF+AAIAAgACAYAgICAiAG4A+CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcC3gBfAF8AXwAxAF8ApwGcAF8AXwAXAF+AAIAjgACAYAgICAiAG4A/CAiAAAjZACEAJQN/AA4AKAOAACMAUAOBAWMC3ABRAHAAFwApADEAXwOJXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNvcmRlcmVkXxAkWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXN3YXNFbmNvZGVkXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNzdG9yYWdlgFyAX4AKgC2AAIAECIBs0wA6ADsADgOLA5MAQqcDjAONA44DjwOQA5EDkoBtgG6Ab4BwgHGAcoBzpwOUA5UDlgOXA5gDmQOagHSAdoB4gHmA9YD2gPeAJl8QD1hEX1BSS19taW5Db3VudF8QEVhEX1BSS19kZWxldGVSdWxlXxAPWERfUFJLX21heENvdW50XxASWERfUFJLX2Rlc3RpbmF0aW9uXxAPWERfUFJLX2lzVG9NYW55XlhEX1BSS19vcmRlcmVkXxAaWERfUFJLX2ludmVyc2VSZWxhdGlvbnNoaXDfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwOlABcC3wBfAF8AXwAxAF8ApwOMAF8AXwAXAF+AAIB1gACAawgICAiAG4BtCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwO0ABcC3wBfAF8AXwAxAF8ApwONAF8AXwAXAF+AAIB3gACAawgICAiAG4BuCAiAAAgQAd8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXA6UAFwLfAF8AXwBfADEAXwCnA44AXwBfABcAX4AAgHWAAIBrCAgICIAbgG8ICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXAEEAFwLfAF8AXwBfADEAXwCnA48AXwBfABcAX4AAgHqAAIBrCAgICIAbgHAICIAACN8QEAPhA+ID4wPkACED5QPmACMD5wPoAA4AJQPpA+oAKABQAFED7AApACkAFAPwAFcAMQApAFEAWgA+AFED9wP4AF9fECFYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc29yZGVyZWRfECFYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc3N0b3JhZ2VfECBYREJ1Y2tldEZvclN0ZXJlb3R5cGVzd2FzRW5jb2RlZF8QJFhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzd2FzRW5jb2RlZF8QHVhEQnVja2V0Rm9yU3RlcmVvdHlwZXNzdG9yYWdlXxAkWERCdWNrZXRGb3JHZW5lcmFsaXphdGlvbnNkdXBsaWNhdGVzXxAkWERCdWNrZXRGb3JHZW5lcmFsaXphdGlvbnN3YXNFbmNvZGVkXxAdWERCdWNrZXRGb3JTdGVyZW90eXBlc29yZGVyZWRfECFYREJ1Y2tldEZvckdlbmVyYWxpemF0aW9uc29yZGVyZWRfECFYREJ1Y2tldEZvckdlbmVyYWxpemF0aW9uc3N0b3JhZ2WACoCOgASABIACgHyA8oAEgAqA9IAHgAqA84B7CBK8COwB0wA6ADsADgP8A/4AQqEAZIAMoQP/gH2AJtkAIQAlBAIADgAoBAMAIwBQBAQAQQBkAFEAcAAXACkAMQBfBAxfECFYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc29yZGVyZWRfECRYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc3dhc0VuY29kZWRfECFYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc3N0b3JhZ2WAeoAMgAqALYAAgAQIgH7TADoAOwAOBA4EGABCqQB3AHgAeQB6AHsAfAB9AH4Af4APgBCAEYASgBOAFIAVgBaAF6kEGQQaBBsEHAQdBB4EHwQgBCGAf4CCgIOAhYCGgIiAiYCLgIyAJt8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXBCUAFwP/AF8AXwBfADEAXwCnAHcAXwBfABcAX4AAgICAAIB9CAgICIAbgA8ICIAACNIAOwAOBDMENKCAgdIAsQCyALUENqIAtQC23xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXA/8AXwBfAF8AMQBfAKcAeABfAF8AFwBfgACAAIAAgH0ICAgIgBuAEAgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcESAAXA/8AXwBfAF8AMQBfAKcAeQBfAF8AFwBfgACAhIAAgH0ICAgIgBuAEQgIgAAI0gA7AA4EVgCvoIAa3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXA/8AXwBfAF8AMQBfAKcAegBfAF8AFwBfgACAAIAAgH0ICAgIgBuAEggIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcEaQAXA/8AXwBfAF8AMQBfAKcAewBfAF8AFwBfgACAh4AAgH0ICAgIgBuAEwgIgAAI0gA7AA4EdwCvoIAa3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXA/8AXwBfAF8AMQBfAKcAfABfAF8AFwBfgACAI4AAgH0ICAgIgBuAFAgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcEigAXA/8AXwBfAF8AMQBfAKcAfQBfAF8AFwBfgACAioAAgH0ICAgIgBuAFQgIgAAI0wA6ADsADgSYBJkAQqCggCbfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEoABcD/wBfAF8AXwAxAF8ApwB+AF8AXwAXAF+AAIAogACAfQgICAiAG4AWCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwSsABcD/wBfAF8AXwAxAF8ApwB/AF8AXwAXAF+AAICNgACAfQgICAiAG4AXCAiAAAhcV01GQ2FjaGVJdGVt0wA6ADsADgS7BMAAQqQEvAFfBL4Ev4CPgC+AkICRpATBBMIEwwTEgJKAqoDBgNqAJltjYWNoZUdyb3Vwc1d2YXJpYW50VGRhdGXfEBIAlQCWAJcEygAhAJkAmgTLACMAmATMAJsADgAlAJwAnQAoAJ4AFwAXABcAKQBBAF8AXwTUADEAXwBRAF8C0gS8AF8AXwTcAF9fECBYREJ1Y2tldEZvclN0ZXJlb3R5cGVzd2FzRW5jb2RlZF8QHVhEQnVja2V0Rm9yU3RlcmVvdHlwZXNzdG9yYWdlXxAdWERCdWNrZXRGb3JTdGVyZW90eXBlc29yZGVyZWSAAIAAgACABIB6CAiAlAiACgiAqYCPCAiAkwgTAAAAASAF6nzTADoAOwAOBOAE4wBCogF/AtyANIBfogTkBOWAlYCggCbZACEAJQToAA4AKATpACMAUATqBMEBfwBRAHAAFwApADEAXwTyXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNvcmRlcmVkXxAkWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXN3YXNFbmNvZGVkXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNzdG9yYWdlgJKANIAKgC2AAIAECICW0wA6ADsADgT0BP0AQqgBlQGWAZcBmAGZAZoBmwGcgDiAOYA6gDuAPIA9gD6AP6gE/gT/BQAFAQUCBQMFBAUFgJeAmICZgJuAnICdgJ6An4Am3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXBOQAXwBfAF8AMQBfAKcBlQBfAF8AFwBfgACAI4AAgJUICAgIgBuAOAgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXBOQAXwBfAF8AMQBfAKcBlgBfAF8AFwBfgACAAIAAgJUICAgIgBuAOQgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcFJwAXBOQAXwBfAF8AMQBfAKcBlwBfAF8AFwBfgACAmoAAgJUICAgIgBuAOggIgAAI0wA6ADsADgU1BTYAQqCggCbfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcE5ABfAF8AXwAxAF8ApwGYAF8AXwAXAF+AAIAjgACAlQgICAiAG4A7CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwHiABcE5ABfAF8AXwAxAF8ApwGZAF8AXwAXAF+AAIBFgACAlQgICAiAG4A8CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcE5ABfAF8AXwAxAF8ApwGaAF8AXwAXAF+AAIAjgACAlQgICAiAG4A9CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcE5ABfAF8AXwAxAF8ApwGbAF8AXwAXAF+AAIAAgACAlQgICAiAG4A+CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcE5ABfAF8AXwAxAF8ApwGcAF8AXwAXAF+AAIAjgACAlQgICAiAG4A/CAiAAAjZACEAJQWEAA4AKAWFACMAUAWGBMEC3ABRAHAAFwApADEAXwWOXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNvcmRlcmVkXxAkWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXN3YXNFbmNvZGVkXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNzdG9yYWdlgJKAX4AKgC2AAIAECICh0wA6ADsADgWQBZgAQqcDjAONA44DjwOQA5EDkoBtgG6Ab4BwgHGAcoBzpwWZBZoFmwWcBZ0FngWfgKKAo4CkgKWApoCngKiAJt8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXA6UAFwTlAF8AXwBfADEAXwCnA4wAXwBfABcAX4AAgHWAAICgCAgICIAbgG0ICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXA7QAFwTlAF8AXwBfADEAXwCnA40AXwBfABcAX4AAgHeAAICgCAgICIAbgG4ICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXA6UAFwTlAF8AXwBfADEAXwCnA44AXwBfABcAX4AAgHWAAICgCAgICIAbgG8ICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXAEAAFwTlAF8AXwBfADEAXwCnA48AXwBfABcAX4AAgAiAAICgCAgICIAbgHAICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXAeIAFwTlAF8AXwBfADEAXwCnA5AAXwBfABcAX4AAgEWAAICgCAgICIAbgHEICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXAQEAFwTlAF8AXwBfADEAXwCnA5EAXwBfABcAX4AAgCOAAICgCAgICIAbgHIICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXAWMAFwTlAF8AXwBfADEAXwCnA5IAXwBfABcAX4AAgFyAAICgCAgICIAbgHMICIAACNIAsQCyBgsGDF8QEFhEUE1SZWxhdGlvbnNoaXCmBg0GDgYPBhAGEQC2XxAQWERQTVJlbGF0aW9uc2hpcFxYRFBNUHJvcGVydHlfEBBYRFVNTFByb3BlcnR5SW1wXxAUWERVTUxOYW1lZEVsZW1lbnRJbXBfEA9YRFVNTEVsZW1lbnRJbXDfEBIAlQCWAJcGEwAhAJkAmgYUACMAmAYVAJsADgAlAJwAnQAoAJ4AFwAXABcAKQBBAF8AXwYdADEAXwBRAF8BdgFfAF8AXwYlAF9fECBYREJ1Y2tldEZvclN0ZXJlb3R5cGVzd2FzRW5jb2RlZF8QHVhEQnVja2V0Rm9yU3RlcmVvdHlwZXNzdG9yYWdlXxAdWERCdWNrZXRGb3JTdGVyZW90eXBlc29yZGVyZWSAAIAAgACABIB6CAiArAiACgiAW4AvCAiAqwgSb+YZoNMAOgA7AA4GKQYsAEKiAX8BgIA0gDWiBi0GLoCtgLiAJtkAIQAlBjEADgAoBjIAIwBQBjMEwgF/AFEAcAAXACkAMQBfBjtfECFYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc29yZGVyZWRfECRYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc3dhc0VuY29kZWRfECFYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc3N0b3JhZ2WAqoA0gAqALYAAgAQIgK7TADoAOwAOBj0GRgBCqAGVAZYBlwGYAZkBmgGbAZyAOIA5gDqAO4A8gD2APoA/qAZHBkgGSQZKBksGTAZNBk6Ar4CwgLGAs4C0gLWAtoC3gCbfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcGLQBfAF8AXwAxAF8ApwGVAF8AXwAXAF+AAIAjgACArQgICAiAG4A4CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcGLQBfAF8AXwAxAF8ApwGWAF8AXwAXAF+AAIAAgACArQgICAiAG4A5CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwZwABcGLQBfAF8AXwAxAF8ApwGXAF8AXwAXAF+AAICygACArQgICAiAG4A6CAiAAAjTADoAOwAOBn4GfwBCoKCAJt8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXAQEAFwYtAF8AXwBfADEAXwCnAZgAXwBfABcAX4AAgCOAAICtCAgICIAbgDsICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXAQEAFwYtAF8AXwBfADEAXwCnAZkAXwBfABcAX4AAgCOAAICtCAgICIAbgDwICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXAQEAFwYtAF8AXwBfADEAXwCnAZoAXwBfABcAX4AAgCOAAICtCAgICIAbgD0ICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXABcAFwYtAF8AXwBfADEAXwCnAZsAXwBfABcAX4AAgACAAICtCAgICIAbgD4ICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXAQEAFwYtAF8AXwBfADEAXwCnAZwAXwBfABcAX4AAgCOAAICtCAgICIAbgD8ICIAACNkAIQAlBs0ADgAoBs4AIwBQBs8EwgGAAFEAcAAXACkAMQBfBtdfECFYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc29yZGVyZWRfECRYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc3dhc0VuY29kZWRfECFYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc3N0b3JhZ2WAqoA1gAqALYAAgAQIgLnTADoAOwAOBtkG4QBCpwI6AjsCPAI9Aj4CPwJAgEyATYBOgE+AUIBRgFKnBuIG4wbkBuUG5gbnBuiAuoC7gLyAvYC+gL+AwIAm3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXBi4AXwBfAF8AMQBfAKcCOgBfAF8AFwBfgACAAIAAgLgICAgIgBuATAgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXBi4AXwBfAF8AMQBfAKcCOwBfAF8AFwBfgACAI4AAgLgICAgIgBuATQgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXBi4AXwBfAF8AMQBfAKcCPABfAF8AFwBfgACAAIAAgLgICAgIgBuATggIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcCgAAXBi4AXwBfAF8AMQBfAKcCPQBfAF8AFwBfgACAV4AAgLgICAgIgBuATwgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXBi4AXwBfAF8AMQBfAKcCPgBfAF8AFwBfgACAAIAAgLgICAgIgBuAUAgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXBi4AXwBfAF8AMQBfAKcCPwBfAF8AFwBfgACAAIAAgLgICAgIgBuAUQgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXBi4AXwBfAF8AMQBfAKcCQABfAF8AFwBfgACAAIAAgLgICAgIgBuAUggIgAAI3xASAJUAlgCXB1QAIQCZAJoHVQAjAJgHVgCbAA4AJQCcAJ0AKACeABcAFwAXACkAQQBfAF8HXgAxAF8AUQBfAXYEvgBfAF8HZgBfXxAgWERCdWNrZXRGb3JTdGVyZW90eXBlc3dhc0VuY29kZWRfEB1YREJ1Y2tldEZvclN0ZXJlb3R5cGVzc3RvcmFnZV8QHVhEQnVja2V0Rm9yU3RlcmVvdHlwZXNvcmRlcmVkgACAAIAAgASAeggIgMMIgAoIgFuAkAgIgMIIEi7w8OvTADoAOwAOB2oHbQBCogF/AYCANIA1ogduB2+AxIDPgCbZACEAJQdyAA4AKAdzACMAUAd0BMMBfwBRAHAAFwApADEAXwd8XxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNvcmRlcmVkXxAkWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXN3YXNFbmNvZGVkXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNzdG9yYWdlgMGANIAKgC2AAIAECIDF0wA6ADsADgd+B4cAQqgBlQGWAZcBmAGZAZoBmwGcgDiAOYA6gDuAPIA9gD6AP6gHiAeJB4oHiweMB40HjgePgMaAx4DIgMqAy4DMgM2AzoAm3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXB24AXwBfAF8AMQBfAKcBlQBfAF8AFwBfgACAI4AAgMQICAgIgBuAOAgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXB24AXwBfAF8AMQBfAKcBlgBfAF8AFwBfgACAAIAAgMQICAgIgBuAOQgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcHsQAXB24AXwBfAF8AMQBfAKcBlwBfAF8AFwBfgACAyYAAgMQICAgIgBuAOggIgAAI0wA6ADsADge/B8AAQqCggCbfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcHbgBfAF8AXwAxAF8ApwGYAF8AXwAXAF+AAIAjgACAxAgICAiAG4A7CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcHbgBfAF8AXwAxAF8ApwGZAF8AXwAXAF+AAIAjgACAxAgICAiAG4A8CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcHbgBfAF8AXwAxAF8ApwGaAF8AXwAXAF+AAIAjgACAxAgICAiAG4A9CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcHbgBfAF8AXwAxAF8ApwGbAF8AXwAXAF+AAIAAgACAxAgICAiAG4A+CAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcHbgBfAF8AXwAxAF8ApwGcAF8AXwAXAF+AAIAjgACAxAgICAiAG4A/CAiAAAjZACEAJQgOAA4AKAgPACMAUAgQBMMBgABRAHAAFwApADEAXwgYXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNvcmRlcmVkXxAkWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXN3YXNFbmNvZGVkXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNzdG9yYWdlgMGANYAKgC2AAIAECIDQ0wA6ADsADggaCCIAQqcCOgI7AjwCPQI+Aj8CQIBMgE2AToBPgFCAUYBSpwgjCCQIJQgmCCcIKAgpgNGA04DUgNWA14DYgNmAJt8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXCC0AFwdvAF8AXwBfADEAXwCnAjoAXwBfABcAX4AAgNKAAIDPCAgICIAbgEwICIAACFEw3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXB28AXwBfAF8AMQBfAKcCOwBfAF8AFwBfgACAI4AAgM8ICAgIgBuATQgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXB28AXwBfAF8AMQBfAKcCPABfAF8AFwBfgACAAIAAgM8ICAgIgBuATggIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcIWwAXB28AXwBfAF8AMQBfAKcCPQBfAF8AFwBfgACA1oAAgM8ICAgIgBuATwgIgAAIEQEs3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXB28AXwBfAF8AMQBfAKcCPgBfAF8AFwBfgACAAIAAgM8ICAgIgBuAUAgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXB28AXwBfAF8AMQBfAKcCPwBfAF8AFwBfgACAAIAAgM8ICAgIgBuAUQgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXB28AXwBfAF8AMQBfAKcCQABfAF8AFwBfgACAAIAAgM8ICAgIgBuAUggIgAAI3xASAJUAlgCXCJcAIQCZAJoImAAjAJgImQCbAA4AJQCcAJ0AKACeABcAFwAXACkAQQBfAF8IoQAxAF8AUQBfAXYEvwBfAF8IqQBfXxAgWERCdWNrZXRGb3JTdGVyZW90eXBlc3dhc0VuY29kZWRfEB1YREJ1Y2tldEZvclN0ZXJlb3R5cGVzc3RvcmFnZV8QHVhEQnVja2V0Rm9yU3RlcmVvdHlwZXNvcmRlcmVkgACAAIAAgASAeggIgNwIgAoIgFuAkQgIgNsIEwAAAAEcBXCL0wA6ADsADgitCLAAQqIBfwGAgDSANaIIsQiygN2A6IAm2QAhACUItQAOACgItgAjAFAItwTEAX8AUQBwABcAKQAxAF8Iv18QIVhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzb3JkZXJlZF8QJFhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzd2FzRW5jb2RlZF8QIVhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzc3RvcmFnZYDagDSACoAtgACABAiA3tMAOgA7AA4IwQjKAEKoAZUBlgGXAZgBmQGaAZsBnIA4gDmAOoA7gDyAPYA+gD+oCMsIzAjNCM4IzwjQCNEI0oDfgOCA4YDjgOSA5YDmgOeAJt8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXAQEAFwixAF8AXwBfADEAXwCnAZUAXwBfABcAX4AAgCOAAIDdCAgICIAbgDgICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXABcAFwixAF8AXwBfADEAXwCnAZYAXwBfABcAX4AAgACAAIDdCAgICIAbgDkICIAACN8QDwCVAJYAlwAhAJgAmQCaACMAmwAOACUAnACdACgAngAXCPQAFwixAF8AXwBfADEAXwCnAZcAXwBfABcAX4AAgOKAAIDdCAgICIAbgDoICIAACNMAOgA7AA4JAgkDAEKgoIAm3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcB4gAXCLEAXwBfAF8AMQBfAKcBmABfAF8AFwBfgACARYAAgN0ICAgIgBuAOwgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXCLEAXwBfAF8AMQBfAKcBmQBfAF8AFwBfgACAI4AAgN0ICAgIgBuAPAgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXCLEAXwBfAF8AMQBfAKcBmgBfAF8AFwBfgACAI4AAgN0ICAgIgBuAPQgIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAFwAXCLEAXwBfAF8AMQBfAKcBmwBfAF8AFwBfgACAAIAAgN0ICAgIgBuAPggIgAAI3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcBAQAXCLEAXwBfAF8AMQBfAKcBnABfAF8AFwBfgACAI4AAgN0ICAgIgBuAPwgIgAAI2QAhACUJUQAOACgJUgAjAFAJUwTEAYAAUQBwABcAKQAxAF8JW18QIVhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzb3JkZXJlZF8QJFhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzd2FzRW5jb2RlZF8QIVhEQnVja2V0Rm9yT3duZWRBdHRyaWJ1dGVzc3RvcmFnZYDagDWACoAtgACABAiA6dMAOgA7AA4JXQllAEKnAjoCOwI8Aj0CPgI/AkCATIBNgE6AT4BQgFGAUqcJZglnCWgJaQlqCWsJbIDqgOuA7IDtgO+A8IDxgCbfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcIsgBfAF8AXwAxAF8ApwI6AF8AXwAXAF+AAIAAgACA6AgICAiAG4BMCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcIsgBfAF8AXwAxAF8ApwI7AF8AXwAXAF+AAIAjgACA6AgICAiAG4BNCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcIsgBfAF8AXwAxAF8ApwI8AF8AXwAXAF+AAIAAgACA6AgICAiAG4BOCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwmdABcIsgBfAF8AXwAxAF8ApwI9AF8AXwAXAF+AAIDugACA6AgICAiAG4BPCAiAAAgRA4TfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcIsgBfAF8AXwAxAF8ApwI+AF8AXwAXAF+AAIAAgACA6AgICAiAG4BQCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcIsgBfAF8AXwAxAF8ApwI/AF8AXwAXAF+AAIAAgACA6AgICAiAG4BRCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwAXABcIsgBfAF8AXwAxAF8ApwJAAF8AXwAXAF+AAIAAgACA6AgICAiAG4BSCAiAAAhaZHVwbGljYXRlc9IAOwAOCdoAr6CAGtIAsQCyCd0J3lpYRFBNRW50aXR5pwnfCeAJ4QniCeMJ5AC2WlhEUE1FbnRpdHldWERVTUxDbGFzc0ltcF8QElhEVU1MQ2xhc3NpZmllckltcF8QEVhEVU1MTmFtZXNwYWNlSW1wXxAUWERVTUxOYW1lZEVsZW1lbnRJbXBfEA9YRFVNTEVsZW1lbnRJbXDfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwHiABcC3wBfAF8AXwAxAF8ApwOQAF8AXwAXAF+AAIBFgACAawgICAiAG4BxCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwEBABcC3wBfAF8AXwAxAF8ApwORAF8AXwAXAF+AAIAjgACAawgICAiAG4ByCAiAAAjfEA8AlQCWAJcAIQCYAJkAmgAjAJsADgAlAJwAnQAoAJ4AFwTBABcC3wBfAF8AXwAxAF8ApwOSAF8AXwAXAF+AAICSgACAawgICAiAG4BzCAiAAAjSADsADgoTAK+ggBrTADoAOwAOChYKFwBCoKCAJtMAOgA7AA4KGgobAEKgoIAm0wA6ADsADgoeCh8AQqCggCbSALEAsgoiCiNeWERNb2RlbFBhY2thZ2WmCiQKJQomCicKKAC2XlhETW9kZWxQYWNrYWdlXxAPWERVTUxQYWNrYWdlSW1wXxARWERVTUxOYW1lc3BhY2VJbXBfEBRYRFVNTE5hbWVkRWxlbWVudEltcF8QD1hEVU1MRWxlbWVudEltcNIAOwAOCioAr6CAGtMAOgA7AA4KLQouAEKgoIAmUNIAsQCyCjIKM1lYRFBNTW9kZWyjCjIKNAC2V1hETW9kZWwACAAZACIALAAxADoAPwBRAFYAWwBdAmMCaQKGApgCnwKsAr8C1wLlAv8DAQMEAwYDCAMKAwwDDgNHA2YDgwOiA7QD1APbA/kEBQQhBCcESQRqBH0EfwSBBIMEhQSHBIkEiwSNBI8EkQSTBJUElwSZBJoEngSrBLMEvgTDBMUExwTMBM4E0ATSBN0E5wUqBU4FcgWVBbwF3AYDBioGSgZuBpIGngagBqIGpAamBqgGqgasBq4GsAayBrQGtga4BroGuwbABsgG1QbYBtoG3QbfBuEG8AcVBzkHYAeEB4YHiAeKB4wHjgeQB5EHkwegB7MHtQe3B7kHuwe9B78HwQfDB8UH2AfaB9wH3gfgB+IH5AfmB+gH6gfsCAIIFQgxCE4Iagh+CJAIpgi/CP4JBAkNCRoJJgkwCToJRQlQCV0JZQlnCWkJawltCW4JbwlwCXEJcwl1CXYJdwl5CXoJgwmECYYJjwmaCaMJsgm5CcEJygnTCeYJ7woCChkKKwpqCmwKbgpwCnIKcwp0CnUKdgp4CnoKewp8Cn4Kfwq+CsAKwgrECsYKxwrICskKygrMCs4KzwrQCtIK0wrcCt0K3wseCyALIgskCyYLJwsoCykLKgssCy4LLwswCzILMwtyC3QLdgt4C3oLewt8C30LfguAC4ILgwuEC4YLhwuQC5ELkwvSC9QL1gvYC9oL2wvcC90L3gvgC+IL4wvkC+YL5wvoDCcMKQwrDC0MLwwwDDEMMgwzDDUMNww4DDkMOww8DEkMSgxLDE0MVgxsDHMMgAy/DMEMwwzFDMcMyAzJDMoMywzNDM8M0AzRDNMM1AztDO8M8QzzDPQM9g0NDRYNJA0xDT8NVA1oDX8NkQ3QDdIN1A3WDdgN2Q3aDdsN3A3eDeAN4Q3iDeQN5Q3zDfwOEQ4gDjUOQw5YDmwOgw6VDqIOpw6pDqsOsA6yDrQOtg66DsUPEA8zD1MPcw91D3cPeQ97D30Pfg9/D4EPgg+ED4UPhw+JD4oPiw+ND44Pkw+gD6UPpw+pD64PsA+yD7QPyQ/eEAMQJxBOEHIQdBB2EHgQehB8EH4QfxCBEI4QnxChEKMQpRCnEKkQqxCtEK8QwBDCEMQQxhDIEMoQzBDOENAQ0hDwEQ4RIRE1EUoRZxF7EZER0BHSEdQR1hHYEdkR2hHbEdwR3hHgEeER4hHkEeUSJBImEigSKhIsEi0SLhIvEjASMhI0EjUSNhI4EjkSeBJ6EnwSfhKAEoESghKDEoQShhKIEokSihKMEo0SmhKbEpwSnhLdEt8S4RLjEuUS5hLnEugS6RLrEu0S7hLvEvES8hLzEzITNBM2EzgTOhM7EzwTPRM+E0ATQhNDE0QTRhNHE4YTiBOKE4wTjhOPE5ATkROSE5QTlhOXE5gTmhObE9oT3BPeE+AT4hPjE+QT5RPmE+gT6hPrE+wT7hPvFC4UMBQyFDQUNhQ3FDgUORQ6FDwUPhQ/FEAUQhRDFGgUjBSzFNcU2RTbFN0U3xThFOMU5BTmFPMVAhUEFQYVCBUKFQwVDhUQFR8VIRUjFSUVJxUpFSsVLRUvFU8VehWUFa0VxxXnFgoWSRZLFk0WTxZRFlIWUxZUFlUWVxZZFloWWxZdFl4WnRafFqEWoxalFqYWpxaoFqkWqxatFq4WrxaxFrIW8RbzFvUW9xb5FvoW+xb8Fv0W/xcBFwIXAxcFFwYXRRdHF0kXSxdNF04XTxdQF1EXUxdVF1YXVxdZF1oXXRecF54XoBeiF6QXpRemF6cXqBeqF6wXrReuF7AXsRfwF/IX9Bf2F/gX+Rf6F/sX/Bf+GAAYARgCGAQYBRhEGEYYSBhKGEwYTRhOGE8YUBhSGFQYVRhWGFgYWRhiGHAYfRiLGJgYqxjCGNQZHxlCGWIZghmEGYYZiBmKGYwZjRmOGZAZkRmTGZQZlhmYGZkZmhmcGZ0ZohmvGbQZthm4Gb0ZvxnBGcMZ1hn7Gh8aRhpqGmwabhpwGnIadBp2GncaeRqGGpcamRqbGp0anxqhGqMapRqnGrgauhq8Gr4awBrCGsQaxhrIGsobCRsLGw0bDxsRGxIbExsUGxUbFxsZGxobGxsdGx4bXRtfG2EbYxtlG2YbZxtoG2kbaxttG24bbxtxG3IbsRuzG7Ubtxu5G7obuxu8G70bvxvBG8IbwxvFG8Yb0xvUG9Ub1xwWHBgcGhwcHB4cHxwgHCEcIhwkHCYcJxwoHCocKxxqHGwcbhxwHHIccxx0HHUcdhx4HHocexx8HH4cfxy+HMAcwhzEHMYcxxzIHMkcyhzMHM4czxzQHNIc0x0SHRQdFh0YHRodGx0cHR0dHh0gHSIdIx0kHSYdJx1mHWgdah1sHW4dbx1wHXEdch10HXYddx14HXodex2gHcQd6x4PHhEeEx4VHhceGR4bHhweHh4rHjoePB4+HkAeQh5EHkYeSB5XHlkeWx5dHl8eYR5jHmUeZx55Ho0enx60HsYe1R7yHzEfMx81HzcfOR86HzsfPB89Hz8fQR9CH0MfRR9GH4Ufhx+JH4sfjR+OH48fkB+RH5MflR+WH5cfmR+aH5wf2x/dH98f4R/jH+Qf5R/mH+cf6R/rH+wf7R/vH/AgLyAxIDMgNSA3IDggOSA6IDsgPSA/IEAgQSBDIEQghyCrIM8g8iEZITkhYCGHIachyyHvIfEh8yH1Ifch+SH7If0h/yIBIgMiBSIHIgkiCyIMIhEiHiIhIiMiJiIoIioiTyJzIpoiviLAIsIixCLGIsgiyiLLIs0i2iLtIu8i8SLzIvUi9yL5Ivsi/SL/IxIjFCMWIxgjGiMcIx4jICMiIyQjJiNlI2cjaSNrI20jbiNvI3AjcSNzI3UjdiN3I3kjeiODI4QjhiOPI5Qj0yPVI9cj2SPbI9wj3SPeI98j4SPjI+Qj5SPnI+gkJyQpJCskLSQvJDAkMSQyJDMkNSQ3JDgkOSQ7JDwkRSRGJEgkhySJJIskjSSPJJAkkSSSJJMklSSXJJgkmSSbJJwk2yTdJN8k4STjJOQk5STmJOck6STrJOwk7STvJPAk+ST6JPwlOyU9JT8lQSVDJUQlRSVGJUclSSVLJUwlTSVPJVAljyWRJZMllSWXJZglmSWaJZslnSWfJaAloSWjJaQlsSWyJbMltSX0JfYl+CX6Jfwl/SX+Jf8mACYCJgQmBSYGJggmCSZIJkomTCZOJlAmUSZSJlMmVCZWJlgmWSZaJlwmXSZqJncmgCaCJoQmhiaIJpEmkyaVJpcmmSabJqcmrya0Jv8nIidCJ2InZCdmJ2gnaidsJ20nbidwJ3Encyd0J3YneCd5J3onfCd9J4YnkyeYJ5onnCehJ6MnpSenJ8wn8CgXKDsoPSg/KEEoQyhFKEcoSChKKFcoaChqKGwobihwKHIodCh2KHgoiSiLKI0ojyiRKJMolSiXKJkomyjaKNwo3ijgKOIo4yjkKOUo5ijoKOoo6yjsKO4o7ykuKTApMik0KTYpNyk4KTkpOik8KT4pPylAKUIpQymCKYQphimIKYopiymMKY0pjimQKZIpkymUKZYplymkKaUppimoKecp6SnrKe0p7ynwKfEp8inzKfUp9yn4Kfkp+yn8KjsqPSo/KkEqQypEKkUqRipHKkkqSypMKk0qTypQKo8qkSqTKpUqlyqYKpkqmiqbKp0qnyqgKqEqoyqkKuMq5SrnKukq6yrsKu0q7irvKvEq8yr0KvUq9yr4KzcrOSs7Kz0rPytAK0ErQitDK0UrRytIK0krSytMK3ErlSu8K+Ar4ivkK+Yr6CvqK+wr7SvvK/wsCywNLA8sESwTLBUsFywZLCgsKiwsLC4sMCwyLDQsNiw4LHcseSx7LH0sfyyALIEsgiyDLIUshyyILIksiyyMLMsszSzPLNEs0yzULNUs1izXLNks2yzcLN0s3yzgLR8tIS0jLSUtJy0oLSktKi0rLS0tLy0wLTEtMy00LXMtdS13LXktey18LX0tfi1/LYEtgy2ELYUthy2ILcctyS3LLc0tzy3QLdEt0i3TLdUt1y3YLdkt2y3cLhsuHS4fLiEuIy4kLiUuJi4nLikuKy4sLi0uLy4wLm8ucS5zLnUudy54Lnkuei57Ln0ufy6ALoEugy6ELo0uoC6tLsAuzS7gLvcvCS9UL3cvly+3L7kvuy+9L78vwS/CL8MvxS/GL8gvyS/LL80vzi/PL9Ev0i/XL+Qv6S/rL+0v8i/0L/Yv+DAdMEEwaDCMMI4wkDCSMJQwljCYMJkwmzCoMLkwuzC9ML8wwTDDMMUwxzDJMNow3DDeMOAw4jDkMOYw6DDqMOwxKzEtMS8xMTEzMTQxNTE2MTcxOTE7MTwxPTE/MUAxfzGBMYMxhTGHMYgxiTGKMYsxjTGPMZAxkTGTMZQx0zHVMdcx2THbMdwx3THeMd8x4THjMeQx5THnMegx9TH2Mfcx+TI4MjoyPDI+MkAyQTJCMkMyRDJGMkgySTJKMkwyTTKMMo4ykDKSMpQylTKWMpcymDKaMpwynTKeMqAyoTLgMuIy5DLmMugy6TLqMusy7DLuMvAy8TLyMvQy9TM0MzYzODM6MzwzPTM+Mz8zQDNCM0QzRTNGM0gzSTOIM4ozjDOOM5AzkTOSM5MzlDOWM5gzmTOaM5wznTPCM+Y0DTQxNDM0NTQ3NDk0OzQ9ND40QDRNNFw0XjRgNGI0ZDRmNGg0ajR5NHs0fTR/NIE0gzSFNIc0iTTINMo0zDTONNA00TTSNNM01DTWNNg02TTaNNw03TUcNR41IDUiNSQ1JTUmNSc1KDUqNSw1LTUuNTA1MTVwNXI1dDV2NXg1eTV6NXs1fDV+NYA1gTWCNYQ1hTXENcY1yDXKNcw1zTXONc810DXSNdQ11TXWNdg12TYYNho2HDYeNiA2ITYiNiM2JDYmNig2KTYqNiw2LTZsNm42cDZyNnQ2dTZ2Nnc2eDZ6Nnw2fTZ+NoA2gTbANsI2xDbGNsg2yTbKNss2zDbONtA20TbSNtQ21TcgN0M3YzeDN4U3hzeJN4s3jTeON483kTeSN5Q3lTeXN5k3mjebN503njejN7A3tTe3N7k3vjfAN8I3xDfpOA04NDhYOFo4XDheOGA4YjhkOGU4Zzh0OIU4hziJOIs4jTiPOJE4kziVOKY4qDiqOKw4rjiwOLI4tDi2OLg49zj5OPs4/Tj/OQA5ATkCOQM5BTkHOQg5CTkLOQw5SzlNOU85UTlTOVQ5VTlWOVc5WTlbOVw5XTlfOWA5nzmhOaM5pTmnOag5qTmqOas5rTmvObA5sTmzObQ5wTnCOcM5xToEOgY6CDoKOgw6DToOOg86EDoSOhQ6FToWOhg6GTpYOlo6XDpeOmA6YTpiOmM6ZDpmOmg6aTpqOmw6bTqsOq46sDqyOrQ6tTq2Orc6uDq6Orw6vTq+OsA6wTsAOwI7BDsGOwg7CTsKOws7DDsOOxA7ETsSOxQ7FTtUO1Y7WDtaO1w7XTteO187YDtiO2Q7ZTtmO2g7aTuOO7I72Tv9O/88ATwDPAU8BzwJPAo8DDwZPCg8KjwsPC48MDwyPDQ8NjxFPEc8STxLPE08TzxRPFM8VTyUPJY8mDyaPJw8nTyePJ88oDyiPKQ8pTymPKg8qTyrPOo87DzuPPA88jzzPPQ89Tz2PPg8+jz7PPw8/jz/PT49QD1CPUQ9Rj1HPUg9ST1KPUw9Tj1PPVA9Uj1TPZI9lD2WPZg9mj2bPZw9nT2ePaA9oj2jPaQ9pj2nPao96T3rPe097z3xPfI98z30PfU99z35Pfo9+z39Pf4+PT4/PkE+Qz5FPkY+Rz5IPkk+Sz5NPk4+Tz5RPlI+kT6TPpU+lz6ZPpo+mz6cPp0+nz6hPqI+oz6lPqY+8T8UPzQ/VD9WP1g/Wj9cP14/Xz9gP2I/Yz9lP2Y/aD9qP2s/bD9uP28/eD+FP4o/jD+OP5M/lT+XP5k/vj/iQAlALUAvQDFAM0A1QDdAOUA6QDxASUBaQFxAXkBgQGJAZEBmQGhAakB7QH1Af0CBQINAhUCHQIlAi0CNQMxAzkDQQNJA1EDVQNZA10DYQNpA3EDdQN5A4EDhQSBBIkEkQSZBKEEpQSpBK0EsQS5BMEExQTJBNEE1QXRBdkF4QXpBfEF9QX5Bf0GAQYJBhEGFQYZBiEGJQZZBl0GYQZpB2UHbQd1B30HhQeJB40HkQeVB50HpQepB60HtQe5CLUIvQjFCM0I1QjZCN0I4QjlCO0I9Qj5CP0JBQkJCgUKDQoVCh0KJQopCi0KMQo1Cj0KRQpJCk0KVQpZC1ULXQtlC20LdQt5C30LgQuFC40LlQuZC50LpQupDKUMrQy1DL0MxQzJDM0M0QzVDN0M5QzpDO0M9Qz5DY0OHQ65D0kPUQ9ZD2EPaQ9xD3kPfQ+FD7kP9Q/9EAUQDRAVEB0QJRAtEGkQcRB5EIEQiRCREJkQoRCpEaURrRG1Eb0RxRHJEc0R0RHVEd0R5RHpEe0R9RH5EvUS/RMFEw0TFRMZEx0TIRMlEy0TNRM5Ez0TRRNJFEUUTRRVFF0UZRRpFG0UcRR1FH0UhRSJFI0UlRSZFZUVnRWlFa0VtRW5Fb0VwRXFFc0V1RXZFd0V5RXpFfUW8Rb5FwEXCRcRFxUXGRcdFyEXKRcxFzUXORdBF0UYQRhJGFEYWRhhGGUYaRhtGHEYeRiBGIUYiRiRGJUZkRmZGaEZqRmxGbUZuRm9GcEZyRnRGdUZ2RnhGeUaERo1GjkaQRplGpEazRr5GzEbhRvVHDEceR11HX0dhR2NHZUdmR2dHaEdpR2tHbUduR29HcUdyR7FHs0e1R7dHuUe6R7tHvEe9R79HwUfCR8NHxUfGSAVIB0gJSAtIDUgOSA9IEEgRSBNIFUgWSBdIGUgaSCNIJEgmSDNINEg1SDdIREhFSEZISEhVSFZIV0hZSGJIcUh+SI1In0izSMpI3EjlSOZI6Ej1SPZI90j5SPpJA0kNSRQAAAAAAAACAgAAAAAAAAo1AAAAAAAAAAAAAAAAAABJHA== + + WMF Framework/Cache.xcdatamodeld/Cache 2.xcdatamodel + YnBsaXN0MDDUAAEAAgADAAQABQAGAAcAClgkdmVyc2lvblkkYXJjaGl2ZXJUJHRvcFgkb2JqZWN0 +cxIAAYagXxAPTlNLZXllZEFyY2hpdmVy0QAIAAlUcm9vdIABrxEBYQALAAwAGwA3ADgAOQBDAEQARQBgAGEAYgBoAGkAdQCLAIwAjQCOAI8AkACRAJIAkwCUAK0AsAC3AL0AzADbAN4A7QD8AP8AXwEPAR4BIgEmATUBOwE8AUQBUwFUAV0BZwFoAWkBagF/AYABiAGJAYoBlgGqAasBrAGtAa4BrwGwAbEBsgHBAdAB3wHjAfIB8wICAhECIAIvAjsCTQJOAk8CUAJRAlICUwJUAmMCcgKBApACkQKgAq8CvgLGAtsC3ALkAuUC8QMFAxQDIwMyAzYDRQNUA2MDcgOBA40DnwOgA6EDogOjA6QDpQOmADEDtQPEA8UD1APjA/0D/gQEBBAEJgQ1BDgERwRWBFkEaAR3BHoEiQSYBJwEqwS6BLsEzQTOBM8E0ATRBNIE0wToBOkE8QT9BREFIAUvBT4FQgVRBWAFbwV+BY0FmQWrBboFyQXYBecF6AX3BgYGFQYqBisGMwY/BlMGYgZxBoAGhAaTBqIGsQbABs8G2wbtBvwHCwcaBykHOAdHB1YHXgdzB3QHfAeIB5wHqwe6B8kHzQfcB+sH+ggJCBgIJAg2CEUIRghVCGQIcwh0CIMIkgihCLYItwi/CMsI3wjuCP0JDAkQCR8JLgk9CUwJWwlnCXkJiAmXCaYJtQnECdMJ4gn3CfgKAAoMCiAKLwo+Ck0KUQpgCm8KfgqNCpwKqAq6CskK2ArnCvYLBQsUCyMLOAs5C0ELTQthC3ALfwuOC5ILoQuwC78LzgvdC+kL+wwKDBkMKAw3DEYMVQxkDHkMegyCDI4MogyxDMAMzwzTDOIM8Q0ADQ8NHg0qDTwNSw1aDWkNeA15DYgNlw2mDbsNvA3EDdAN5A3zDgIOEQ4VDiQOMw5CDlEOYA5sDn4OjQ6cDqsOug7JDtgO5w7oDusO9A8DDxIPIQ8kDygPLA8wDzgPOw8/D0BVJG51bGzXAA0ADgAPABAAEQASABMAFAAVABYAFwAYABcAGl8QD194ZF9yb290UGFja2FnZVYkY2xhc3NcX3hkX2NvbW1lbnRzXxAQX3hkX21vZGVsTWFuYWdlcl8QFV9jb25maWd1cmF0aW9uc0J5TmFtZV1feGRfbW9kZWxOYW1lXxAXX21vZGVsVmVyc2lvbklkZW50aWZpZXKAAoEBYIEBXYAAgQFegACBAV/eABwAHQAeAB8AIAAhACIADgAjACQAJQAmACcAKAApACoAKwAJACkAFwAvADAAMQAyADMAKQApABdfEBxYREJ1Y2tldEZvckNsYXNzZXN3YXNFbmNvZGVkXxAaWERCdWNrZXRGb3JQYWNrYWdlc3N0b3JhZ2VfEBxYREJ1Y2tldEZvckludGVyZmFjZXNzdG9yYWdlXxAPX3hkX293bmluZ01vZGVsXxAdWERCdWNrZXRGb3JQYWNrYWdlc3dhc0VuY29kZWRWX293bmVyXxAbWERCdWNrZXRGb3JEYXRhVHlwZXNzdG9yYWdlW192aXNpYmlsaXR5XxAZWERCdWNrZXRGb3JDbGFzc2Vzc3RvcmFnZVVfbmFtZV8QH1hEQnVja2V0Rm9ySW50ZXJmYWNlc3dhc0VuY29kZWRfEB5YREJ1Y2tldEZvckRhdGFUeXBlc3dhc0VuY29kZWRfEBBfdW5pcXVlRWxlbWVudElEgASBAVuBAVmAAYAEgACBAVqBAVwQAIAFgAOABIAEgABQU1lFU9MAOgA7AA4APAA/AEJXTlMua2V5c1pOUy5vYmplY3RzogA9AD6ABoAHogBAAEGACIB7gCZaQ2FjaGVHcm91cFlDYWNoZUl0ZW3fEBAARgBHAEgASQAhAEoASwAjAEwATQAOACUATgBPACgAUABRAFIAKQApABQAVgBXADEAKQBRAFoAPQBRAF0AXgBfXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNvcmRlcmVkXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNzdG9yYWdlXxAgWERCdWNrZXRGb3JTdGVyZW90eXBlc3dhc0VuY29kZWRfECRYREJ1Y2tldEZvck93bmVkQXR0cmlidXRlc3dhc0VuY29kZWRfEB1YREJ1Y2tldEZvclN0ZXJlb3R5cGVzc3RvcmFnZV8QJFhEQnVja2V0Rm9yR2VuZXJhbGl6YXRpb25zZHVwbGljYXRlc18QJFhEQnVja2V0Rm9yR2VuZXJhbGl6YXRpb25zd2FzRW5jb2RlZF8QHVhEQnVja2V0Rm9yU3RlcmVvdHlwZXNvcmRlcmVkXxAhWERCdWNrZXRGb3JHZW5lcmFsaXphdGlvbnNvcmRlcmVkXxAhWERCdWNrZXRGb3JHZW5lcmFsaXphdGlvbnNzdG9yYWdlW19pc0Fic3RyYWN0gAqALoAEgASAAoALgQFSgASACoEBVIAGgAqBAViACQgTAAAAAQcEm29Xb3JkZXJlZNMAOgA7AA4AYwBlAEKhAGSADKEAZoANgCZeWERfUFN0ZXJlb3R5cGXZACEAJQBqAA4AKABrACMAUABsAEAAZABRAHAAFwApADEAXwB0XxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNvcmRlcmVkXxAkWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXN3YXNFbmNvZGVkXxAhWERCdWNrZXRGb3JPd25lZEF0dHJpYnV0ZXNzdG9yYWdlgAiADIAKgC2AAIAECIAO0wA6ADsADgB2AIAAQqkAdwB4AHkAegB7AHwAfQB+AH+AD4AQgBGAEoATgBSAFYAWgBepAIEAggCDAIQAhQCGAIcAiACJgBiAHIAdgB+AIIAigCSAJ4ArgCZfEBNYRFBNQ29tcG91bmRJbmRleGVzXxAQWERfUFNLX2VsZW1lbnRJRF8QGVhEUE1VbmlxdWVuZXNzQ29uc3RyYWludHNfEBpYRF9QU0tfdmVyc2lvbkhhc2hNb2RpZmllcl8QGVhEX1BTS19mZXRjaFJlcXVlc3RzQXJyYXlfEBFYRF9QU0tfaXNBYnN0cmFjdF8QD1hEX1BTS191c2VySW5mb18QE1hEX1BTS19jbGFzc01hcHBpbmdfEBZYRF9QU0tfZW50aXR5Q2xhc3NOYW1l3xAPAJUAlgCXACEAmACZAJoAIwCbAA4AJQCcAJ0AKACeABcAoAAXAGYAXwBfAF8AMQBfAKcAdwBfAF8AFwBfVV90eXBlWF9kZWZhdWx0XF9hc3NvY2lhdGlvbltfaXNSZWFkT25seVlfaXNTdGF0aWNZX2lzVW5pcXVlWl9pc0Rlcml2ZWRaX2lzT3JkZXJlZFxfaXNDb21wb3NpdGVXX2lzTGVhZoAAgBmAAIANCAgICIAbgA8ICIAACNIAOwAOAK4Ar6CAGtIAsQCyALMAtFokY2xhc3NuYW1lWCRjbGFzc2VzXk5TTXV0YWJsZUFycmF5owCzALUAtldOU0FycmF5WE5TT2JqZWN00gCxALIAuAC5XxAQWERVTUxQcm9wZXJ0eUltcKQAugC7ALwAtl8QEFhEVU1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qTypRKlMqVSpXKlgqWSpaKlsqXSpfKmAqYSpjKmQqoyqlKqcqqSqrKqwqrSquKq8qsSqzKrQqtSq3KrgqxSrGKscqySsIKworDCsOKxArESsSKxMrFCsWKxgrGSsaKxwrHStcK14rYCtiK2QrZStmK2craCtqK2wrbStuK3ArcSuwK7IrtCu2K7gruSu6K7srvCu+K8ArwSvCK8QrxSwELAYsCCwKLAwsDSwOLA8sECwSLBQsFSwWLBgsGSxYLFosXCxeLGAsYSxiLGMsZCxmLGgsaSxqLGwsbSySLLYs3S0BLQMtBS0HLQktCy0NLQ4tEC0dLSwtLi0wLTItNC02LTgtOi1JLUstTS1PLVEtUy1VLVctWS2YLZotnC2eLaAtoS2iLaMtpC2mLagtqS2qLawtrS3sLe4t8C3yLfQt9S32Lfct+C36Lfwt/S3+LgAuAS5ALkIuRC5GLkguSS5KLksuTC5OLlAuUS5SLlQuVS6ULpYumC6aLpwunS6eLp8uoC6iLqQupS6mLqguqS6sLusu7S7vLvEu8y70LvUu9i73Lvku+y78Lv0u/y8ALz8vQS9DL0UvRy9IL0kvSi9LL00vTy9QL1EvUy9UL5MvlS+XL5kvmy+cL50vni+fL6Evoy+kL6Uvpy+oL/MwFjA2MFYwWDBaMFwwXjBgMGEwYjBkMGUwZzBoMGowbDBtMG4wcDBxMHowhzCMMI4wkDCVMJcwmTCbMMAw5DELMS8xMTEzMTUxNzE5MTsxPDE+MUsxXDFeMWAxYjFkMWYxaDFqMWwxfTF/MYExgzGFMYcxiTGLMY0xjzHOMdAx0jHUMdYx1zHYMdkx2jHcMd4x3zHgMeIx4zIiMiQyJjIoMioyKzIsMi0yLjIwMjIyMzI0MjYyNzJ2MngyejJ8Mn4yfzKAMoEygjKEMoYyhzKIMooyizKYMpkymjKcMtsy3TLfMuEy4zLkMuUy5jLnMuky6zLsMu0y7zLwMy8zMTMzMzUzNzM4MzkzOjM7Mz0zPzNAM0EzQzNEM4MzhTOHM4kzizOMM40zjjOPM5EzkzOUM5UzlzOYM9cz2TPbM90z3zPgM+Ez4jPjM+Uz5zPoM+kz6zPsNCs0LTQvNDE0MzQ0NDU0NjQ3NDk0OzQ8ND00PzRANGU0iTSwNNQ01jTYNNo03DTeNOA04TTjNPA0/zUBNQM1BTUHNQk1CzUNNRw1HjUgNSI1JDUmNSg1KjUsNWs1bTVvNXE1czV0NXU1djV3NXk1ezV8NX01fzWANb81wTXDNcU1xzXINck1yjXLNc01zzXQNdE10zXUNhM2FTYXNhk2GzYcNh02HjYfNiE2IzYkNiU2JzYoNmc2aTZrNm02bzZwNnE2cjZzNnU2dzZ4Nnk2ezZ8Nrs2vTa/NsE2wzbENsU2xjbHNsk2yzbMNs02zzbQNw83ETcTNxU3FzcYNxk3GjcbNx03HzcgNyE3IzckN2M3ZTdnN2k3azdsN203bjdvN3E3czd0N3U3dzd4N4E3lDehN7Q3wTfUN+s3/ThIOGs4izirOK04rzixOLM4tTi2OLc4uTi6OLw4vTi/OME4wjjDOMU4xjjLONg43TjfOOE45jjoOOo47DkROTU5XDmAOYI5hDmGOYg5ijmMOY05jzmcOa05rzmxObM5tTm3Obk5uzm9Oc450DnSOdQ51jnYOdo53DneOeA6HzohOiM6JTonOig6KToqOis6LTovOjA6MTozOjQ6czp1Onc6eTp7Onw6fTp+On86gTqDOoQ6hTqHOog6xzrJOss6zTrPOtA60TrSOtM61TrXOtg62TrbOtw66TrqOus67TssOy47MDsyOzQ7NTs2Ozc7ODs6Ozw7PTs+O0A7QTuAO4I7hDuGO4g7iTuKO4s7jDuOO5A7kTuSO5Q7lTvUO9Y72DvaO9w73TveO9874DviO+Q75TvmO+g76TwoPCo8LDwuPDA8MTwyPDM8NDw2PDg8OTw6PDw8PTx8PH48gDyCPIQ8hTyGPIc8iDyKPIw8jTyOPJA8kTy2PNo9AT0lPSc9KT0rPS09Lz0xPTI9ND1BPVA9Uj1UPVY9WD1aPVw9Xj1tPW89cT1zPXU9dz15PXs9fT28Pb49wD3CPcQ9xT3GPcc9yD3KPcw9zT3OPdA90T3UPhM+FT4XPhk+Gz4cPh0+Hj4fPiE+Iz4kPiU+Jz4oPmc+aT5rPm0+bz5wPnE+cj5zPnU+dz54Pnk+ez58Prs+vT6/PsE+wz7EPsU+xj7HPsk+yz7MPs0+zz7QPtM/Ej8UPxY/GD8aPxs/HD8dPx4/ID8iPyM/JD8mPyc/Zj9oP2o/bD9uP28/cD9xP3I/dD92P3c/eD96P3s/uj+8P74/wD/CP8M/xD/FP8Y/yD/KP8s/zD/OP89AGkA9QF1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WWFZaVlxWXlZhVmJWY1ZkVmVWZ1ZpVmpWa1ZtVm5WrVavVrFWs1a2VrdWuFa5VrpWvFa+Vr9WwFbCVsNXAlcEVwZXCFcLVwxXDVcOVw9XEVcTVxRXFVcXVxhXV1dZV1tXXVdgV2FXYldjV2RXZldoV2lXaldsV21XuFfbV/tYG1gdWB9YIVgjWCVYJlgnWCpYK1gtWC5YMFgyWDNYNFg3WDhYPVhKWE9YUVhTWFhYW1heWGBYhVipWNBY9Fj3WPlY+1j9WP9ZAVkCWQVZElkjWSVZJ1kpWStZLVkvWTFZM1lEWUdZSllNWVBZU1lWWVlZXFleWZ1Zn1mhWaNZplmnWahZqVmqWaxZrlmvWbBZslmzWfJZ9Fn2WfhZ+1n8Wf1Z/ln/WgFaA1oEWgVaB1oIWkdaSVpMWk5aUVpSWlNaVFpVWldaWVpaWltaXVpeWmtabFptWm9arlqwWrJatFq3WrhauVq6WrtavVq/WsBawVrDWsRbA1sFWwdbCVsMWw1bDlsPWxBbElsUWxVbFlsYWxlbWFtaW1xbXlthW2JbY1tkW2VbZ1tpW2pba1ttW25brVuvW7Fbs1u2W7dbuFu5W7pbvFu+W79bwFvCW8NcAlwEXAZcCFwLXAxcDVwOXA9cEVwTXBRcFVwXXBhcPVxhXIhcrFyvXLFcs1y1XLdcuVy6XL1cylzZXNtc3VzfXOFc41zlXOdc9lz5XPxc/10CXQVdCF0LXQ1dTF1OXVBdUl1VXVZdV11YXVldW11dXV5dX11hXWJdoV2jXaVdp12qXatdrF2tXa5dsF2yXbNdtF22Xbdd9l34Xfpd/F3/XgBeAV4CXgNeBV4HXgheCV4LXgxeS15NXlBeUl5VXlZeV15YXlleW15dXl5eX15hXmJeZV6kXqZeqF6qXq1erl6vXrBesV6zXrVetl63Xrleul75Xvte/V7/XwJfA18EXwVfBl8IXwpfC18MXw5fD19OX1BfUl9UX1dfWF9ZX1pfW19dX19fYF9hX2NfZF+vX9Jf8mASYBRgFmAYYBpgHGAdYB5gIWAiYCRgJWAnYClgKmArYC5gL2A0YEFgRmBIYEpgT2BSYFVgV2B8YKBgx2DrYO5g8GDyYPRg9mD4YPlg/GEJYRphHGEeYSBhImEkYSZhKGEqYTthPmFBYURhR2FKYU1hUGFTYVVhlGGWYZhhmmGdYZ5hn2GgYaFho2GlYaZhp2GpYaph6WHrYe1h72HyYfNh9GH1YfZh+GH6Yfth/GH+Yf9iPmJAYkNiRWJIYkliSmJLYkxiTmJQYlFiUmJUYlViYmJjYmRiZmKlYqdiqWKrYq5ir2KwYrFismK0YrZit2K4Yrpiu2L6Yvxi/mMAYwNjBGMFYwZjB2MJYwtjDGMNYw9jEGNPY1FjU2NVY1hjWWNaY1tjXGNeY2BjYWNiY2RjZWOkY6ZjqGOqY61jrmOvY7BjsWOzY7VjtmO3Y7ljumP5Y/tj/WP/ZAJkA2QEZAVkBmQIZApkC2QMZA5kD2Q0ZFhkf2SjZKZkqGSqZKxkrmSwZLFktGTBZNBk0mTUZNZk2GTaZNxk3mTtZPBk82T2ZPlk/GT/ZQJlBGVDZUVlR2VJZUxlTWVOZU9lUGVSZVRlVWVWZVhlWWWYZZplnGWeZaFlomWjZaRlpWWnZallqmWrZa1lrmXtZe9l8WXzZfZl92X4Zfll+mX8Zf5l/2YAZgJmA2ZCZkRmRmZIZktmTGZNZk5mT2ZRZlNmVGZVZldmWGaXZplmm2adZqBmoWaiZqNmpGamZqhmqWaqZqxmrWbsZu5m8GbyZvVm9mb3Zvhm+Wb7Zv1m/mb/ZwFnAmdBZ0NnRWdHZ0pnS2dMZ01nTmdQZ1JnU2dUZ1ZnV2diZ2tnbGduZ3dngmeRZ5xnqme/Z9Nn6mf8aDtoPWg/aEFoQ2hEaEVoRmhHaEloS2hMaE1oT2hQaI9okWiTaJVol2iYaJlommibaJ1on2igaKFoo2ikaONo5WjnaOlo62jsaO1o7mjvaPFo82j0aPVo92j4aQFpAmkEaRFpEmkTaRVpImkjaSRpJmkzaTRpNWk3aUBpT2lcaWtpfWmRaahpumnDacRpxmnTadRp1WnXadhp4WnrafIAAAAAAAACAgAAAAAAAA9EAAAAAAAAAAAAAAAAAABp+g== + + + + + isDownloaded + + + + 1 + mustHaveCacheGroups + + + + url + + + + variant + + + + 1 + cacheGroups + + + + + CacheGroup + Undefined + 2 + CacheGroup + 1 + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/CacheItemMigrationPolicy.swift b/Apps/Wikipedia/WMF Framework/CacheItemMigrationPolicy.swift new file mode 100644 index 0000000..415b099 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheItemMigrationPolicy.swift @@ -0,0 +1,62 @@ +import Foundation + +enum CacheItemMigrationPolicyError: Error { + case unrecognizedSourceAttributeTypes +} + +class CacheItemMigrationPolicy: NSEntityMigrationPolicy { + + private let fetcher = ImageFetcher() + + override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws { + + if sInstance.entity.name == "CacheItem" { + + guard let key = sInstance.primitiveValue(forKey: "key") as? String, + let variant = sInstance.primitiveValue(forKey: "variant") as? Int64, + let date = sInstance.primitiveValue(forKey: "date") as? Date else { + throw CacheItemMigrationPolicyError.unrecognizedSourceAttributeTypes + } + + let destinationItem = NSEntityDescription.insertNewObject(forEntityName: "CacheItem", into: manager.destinationContext) + + destinationItem.setValue(key, forKey: "key") + + let newVariant = String(variant) + destinationItem.setValue(newVariant, forKey: "variant") + destinationItem.setValue(date, forKey: "date") + var isDownloaded = false + autoreleasepool { () -> Void in + guard + let fileName = fetcher.uniqueFileNameForItemKey(key, variant: newVariant), + let headerFileName = fetcher.uniqueHeaderFileNameForItemKey(key, variant: newVariant) else { + return + } + let fileURL = CacheFileWriterHelper.fileURL(for: fileName) + let filePath = fileURL.path + // artifically create and save image response header + var headers: [String: String] = [:] + headers["Content-Type"] = FileManager.default.getValueForExtendedFileAttributeNamed(WMFExtendedFileAttributeNameMIMEType, forFileAtPath: filePath) + let values = try? fileURL.resourceValues(forKeys: [URLResourceKey.fileSizeKey]) + if let fileSize = values?.fileSize { + headers["Content-Length"] = String(fileSize) + } + guard !headers.isEmpty else { + return + } + CacheFileWriterHelper.saveResponseHeader(headerFields: headers, toNewFileName: headerFileName) { (result) in + switch result { + case .success, .exists: + isDownloaded = true + case .failure: + break + } + } + } + destinationItem.setValue(isDownloaded, forKey: "isDownloaded") + destinationItem.setValue(nil, forKey: "url") + manager.associate(sourceInstance: sInstance, withDestinationInstance: destinationItem, for: mapping) + + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/CacheTaskTracking.swift b/Apps/Wikipedia/WMF Framework/CacheTaskTracking.swift new file mode 100644 index 0000000..99b0b29 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CacheTaskTracking.swift @@ -0,0 +1,54 @@ +import Foundation + +struct IdentifiedTask { + let untrackKey: String + let task: URLSessionTask +} + +// TODO: less of a sledgehammer here +let CacheTaskTrackingSemaphore = DispatchSemaphore(value: 1) + +protocol CacheTaskTracking: AnyObject { + var groupedTasks: [String: [IdentifiedTask]] { get set } + func cancelAllTasks() + func cancelTasks(for groupKey: String) + func untrackTask(untrackKey: String, from groupKey: String) + func trackTask(untrackKey: String, task: URLSessionTask, to groupKey: String) +} + +extension CacheTaskTracking { + func cancelTasks(for groupKey: String) { + CacheTaskTrackingSemaphore.wait() + if let identifiedTasks = groupedTasks[groupKey] { + for identifiedTask in identifiedTasks { + identifiedTask.task.cancel() + } + } + CacheTaskTrackingSemaphore.signal() + } + + func untrackTask(untrackKey: String, from groupKey: String) { + CacheTaskTrackingSemaphore.wait() + if let identifiedTasks = groupedTasks[groupKey] { + groupedTasks[groupKey] = identifiedTasks.filter { $0.untrackKey == untrackKey } + } + CacheTaskTrackingSemaphore.signal() + } + + func trackTask(untrackKey: String, task: URLSessionTask, to groupKey: String) { + CacheTaskTrackingSemaphore.wait() + let identifiedTask = IdentifiedTask(untrackKey: untrackKey, task: task) + groupedTasks[groupKey]?.append(identifiedTask) + CacheTaskTrackingSemaphore.signal() + } + + func cancelAllTasks() { + CacheTaskTrackingSemaphore.wait() + for group in groupedTasks { + for identifiedTask in group.value { + identifiedTask.task.cancel() + } + } + CacheTaskTrackingSemaphore.signal() + } +} diff --git a/Apps/Wikipedia/WMF Framework/CharacterSet+LinkParsing.swift b/Apps/Wikipedia/WMF Framework/CharacterSet+LinkParsing.swift new file mode 100644 index 0000000..cd83fb8 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CharacterSet+LinkParsing.swift @@ -0,0 +1,20 @@ +extension CharacterSet { + public static var encodeURIComponentAllowed: CharacterSet { + return NSCharacterSet.wmf_encodeURIComponentAllowed() + } + + public static var relativePathAndFragmentAllowed: CharacterSet { + return NSCharacterSet.wmf_relativePathAndFragmentAllowed() + } + + // RFC 3986 reserved + unreserved characters + percent (%) + public static var rfc3986Allowed: CharacterSet { + return CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=%") + } + + static let urlQueryComponentAllowed: CharacterSet = { + var characterSet = CharacterSet.urlQueryAllowed + characterSet.remove(charactersIn: "+&=") + return characterSet + }() +} diff --git a/Apps/Wikipedia/WMF Framework/Collection+AsyncMap.swift b/Apps/Wikipedia/WMF Framework/Collection+AsyncMap.swift new file mode 100644 index 0000000..445d2a9 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Collection+AsyncMap.swift @@ -0,0 +1,81 @@ +import Foundation + +public extension Sequence { + func asyncMapToDictionary( block: (Element, @escaping (K?, V?) -> Void) -> Void, queue: DispatchQueue = DispatchQueue.global(qos: .default), completion: @escaping ([K: V]) -> Void) { + let group = DispatchGroup() + let semaphore = DispatchSemaphore(value: 1) + var results = [K: V](minimumCapacity: underestimatedCount) + for object in self { + group.enter() + block(object, { (key, value) in + defer { + group.leave() + } + guard let key = key, let value = value else { + return + } + semaphore.wait() + results[key] = value + semaphore.signal() + }) + } + group.notify(queue: queue) { + completion(results) + } + } +} + +public extension Collection { + func asyncMap(_ block: (Element, @escaping (R) -> Void) -> Void, completion: @escaping ([R]) -> Void) { + let group = DispatchGroup() + let semaphore = DispatchSemaphore(value: 1) + var results = [R?](repeating: nil, count: count) + for (index, object) in self.enumerated() { + group.enter() + block(object, { result in + semaphore.wait() + results[index] = result + semaphore.signal() + group.leave() + }) + } + group.notify(queue: DispatchQueue.global(qos: .default)) { + completion(results as! [R]) + } + } + + func asyncCompactMap(_ block: (Element, @escaping (R?) -> Void) -> Void, completion: @escaping ([R]) -> Void) { + let group = DispatchGroup() + let semaphore = DispatchSemaphore(value: 1) + var results = [R?](repeating: nil, count: count) + for (index, object) in self.enumerated() { + group.enter() + block(object, { result in + guard let result = result else { + group.leave() + return + } + semaphore.wait() + results[index] = result + semaphore.signal() + group.leave() + }) + } + group.notify(queue: DispatchQueue.global(qos: .default)) { + completion(results.compactMap {$0}) + } + } + + func asyncForEach(_ block: (Element, @escaping () -> Void) -> Void, completion: @escaping () -> Void) { + let group = DispatchGroup() + for object in self { + group.enter() + block(object, { + group.leave() + }) + } + group.notify(queue: DispatchQueue.global(qos: .default)) { + completion() + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/CollectionViewCellActionsView.swift b/Apps/Wikipedia/WMF Framework/CollectionViewCellActionsView.swift new file mode 100644 index 0000000..321e79b --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CollectionViewCellActionsView.swift @@ -0,0 +1,189 @@ +import Foundation + +public class Action: UIAccessibilityCustomAction { + let icon: UIImage? + let confirmationIcon: UIImage? + public let type: ActionType + public let indexPath: IndexPath + + public init(accessibilityTitle: String, icon: UIImage?, confirmationIcon: UIImage?, type: ActionType, indexPath: IndexPath, target: Any?, selector: Selector) { + self.icon = icon + self.confirmationIcon = confirmationIcon + self.type = type + self.indexPath = indexPath + super.init(name: accessibilityTitle, target: target, selector: selector) + } +} + +@objc public protocol ActionDelegate: NSObjectProtocol { + @objc func availableActions(at indexPath: IndexPath) -> [Action] + @objc func didPerformAction(_ action: Action) -> Bool + @objc func willPerformAction(_ action: Action) -> Bool + @objc optional func didPerformBatchEditToolbarAction(_ action: BatchEditToolbarAction, completion: @escaping (Bool) -> Void) + @objc optional var availableBatchEditToolbarActions: [BatchEditToolbarAction] { get } +} + +public enum ActionType { + case delete, save, unsave, share + + private struct Icon { + static let delete = UIImage(named: "swipe-action-delete", in: Bundle.wmf, compatibleWith: nil) + static let save = UIImage(named: "swipe-action-save", in: Bundle.wmf, compatibleWith: nil) + static let unsave = UIImage(named: "swipe-action-unsave", in: Bundle.wmf, compatibleWith: nil) + static let share = UIImage(named: "swipe-action-share", in: Bundle.wmf, compatibleWith: nil) + } + + public func action(with target: Any?, indexPath: IndexPath) -> Action { + switch self { + case .delete: + return Action(accessibilityTitle: CommonStrings.deleteActionTitle, icon: Icon.delete, confirmationIcon: nil, type: .delete, indexPath: indexPath, target: target, selector: #selector(ActionDelegate.willPerformAction(_:))) + case .save: + return Action(accessibilityTitle: CommonStrings.saveTitle, icon: Icon.save, confirmationIcon: Icon.unsave, type: .save, indexPath: indexPath, target: target, selector: #selector(ActionDelegate.willPerformAction(_:))) + case .unsave: + return Action(accessibilityTitle: CommonStrings.accessibilitySavedTitle, icon: Icon.unsave, confirmationIcon: Icon.save, type: .unsave, indexPath: indexPath, target: target, selector: #selector(ActionDelegate.willPerformAction(_:))) + case .share: + return Action(accessibilityTitle: CommonStrings.shareActionTitle, icon: Icon.share, confirmationIcon: nil, type: .share, indexPath: indexPath, target: target, selector: #selector(ActionDelegate.willPerformAction(_:))) + } + } +} + +public class ActionsView: SizeThatFitsView, Themeable { + fileprivate let minButtonWidth: CGFloat = 60 + var maximumWidth: CGFloat = 0 + var buttonWidth: CGFloat = 0 + var buttons: [UIButton] = [] + var needsSubviews = true + + public var theme = Theme.standard + + internal var actions: [Action] = [] { + didSet { + activatedIndex = NSNotFound + needsSubviews = true + } + } + + fileprivate var activatedIndex = NSNotFound + func expand(_ action: Action) { + guard let index = actions.firstIndex(of: action) else { + return + } + bringSubviewToFront(buttons[index]) + activatedIndex = index + setNeedsLayout() + } + + public override var frame: CGRect { + didSet { + setNeedsLayout() + } + } + + public override var bounds: CGRect { + didSet { + setNeedsLayout() + } + } + + public override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let superSize = super.sizeThatFits(size, apply: apply) + if apply { + if size.width > 0 && needsSubviews { + createSubviews(for: actions) + needsSubviews = false + } + let isRTL = effectiveUserInterfaceLayoutDirection == .rightToLeft + if activatedIndex == NSNotFound { + let numberOfButtons = CGFloat(subviews.count) + let buttonDelta = min(size.width, maximumWidth) / numberOfButtons + var x: CGFloat = isRTL ? size.width - buttonWidth : 0 + for button in buttons { + button.frame = CGRect(x: x, y: 0, width: buttonWidth, height: size.height) + if isRTL { + x -= buttonDelta + } else { + x += buttonDelta + } + } + } else { + var x: CGFloat = isRTL ? size.width : 0 - (buttonWidth * CGFloat(buttons.count - 1)) + for button in buttons { + button.clipsToBounds = true + if button.tag == activatedIndex { + button.frame = CGRect(origin: .zero, size: CGSize(width: size.width, height: size.height)) + } else { + button.frame = CGRect(x: x, y: 0, width: buttonWidth, height: size.height) + x += buttonWidth + } + } + } + } + let width = superSize.width == UIView.noIntrinsicMetric ? maximumWidth : superSize.width + let height = superSize.height == UIView.noIntrinsicMetric ? 50 : superSize.height + return CGSize(width: width, height: height) + } + + func createSubviews(for actions: [Action]) { + for view in subviews { + view.removeFromSuperview() + } + buttons = [] + + var maxButtonWidth: CGFloat = 0 + + for (index, action) in actions.enumerated() { + let button = UIButton(type: .custom) + button.setImage(action.icon, for: .normal) + button.titleLabel?.numberOfLines = 1 + button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 14, bottom: 0, right: 14) + button.tag = index + switch action.type { + case .delete: + button.backgroundColor = theme.colors.destructive + case .share: + button.backgroundColor = theme.colors.secondaryAction + case .save: + button.backgroundColor = theme.colors.link + case .unsave: + button.backgroundColor = theme.colors.link + } + button.imageView?.tintColor = .white + button.addTarget(self, action: #selector(willPerformAction(_:)), for: .touchUpInside) + maxButtonWidth = max(maxButtonWidth, button.intrinsicContentSize.width) + insertSubview(button, at: 0) + buttons.append(button) + } + + backgroundColor = buttons.last?.backgroundColor + buttonWidth = max(minButtonWidth, maxButtonWidth) + maximumWidth = buttonWidth * CGFloat(subviews.count) + setNeedsLayout() + } + + public weak var delegate: ActionDelegate? + + private var activeSender: UIButton? + + @objc func willPerformAction(_ sender: UIButton) { + activeSender = sender + let action = actions[sender.tag] + _ = delegate?.willPerformAction(action) + } + + func updateConfirmationImage(for action: Action, completion: @escaping () -> Bool) -> Bool { + if let image = action.confirmationIcon { + activeSender?.setImage(image, for: .normal) + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(200)) { + _ = completion() + } + } else { + return completion() + } + return true + } + + public func apply(theme: Theme) { + self.theme = theme + backgroundColor = theme.colors.baseBackground + } +} diff --git a/Apps/Wikipedia/WMF Framework/CollectionViewEditController.swift b/Apps/Wikipedia/WMF Framework/CollectionViewEditController.swift new file mode 100644 index 0000000..dfa02ec --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CollectionViewEditController.swift @@ -0,0 +1,652 @@ +import Foundation + +public enum CollectionViewCellSwipeType { + case primary, secondary, none +} + +enum CollectionViewCellState { + case idle, open +} + +// wrapper around UIBarButtonItem that lets us access systemItem after button creation +public class SystemBarButton: UIBarButtonItem { + var systemItem: UIBarButtonItem.SystemItem? + public convenience init(with barButtonSystemItem: UIBarButtonItem.SystemItem, target: Any?, action: Selector?) { + self.init(barButtonSystemItem: barButtonSystemItem, target: target, action: action) + self.systemItem = barButtonSystemItem + } +} + +public protocol CollectionViewEditControllerNavigationDelegate: AnyObject { + func didChangeEditingState(from oldEditingState: EditingState, to newEditingState: EditingState, rightBarButton: UIBarButtonItem?, leftBarButton: UIBarButtonItem?) // same implementation for 2/3 + func didSetBatchEditToolbarHidden(_ batchEditToolbarViewController: BatchEditToolbarViewController, isHidden: Bool, with items: [UIButton]) // has default implementation + func newEditingState(for currentEditingState: EditingState, fromEditBarButtonWithSystemItem systemItem: UIBarButtonItem.SystemItem) -> EditingState + func emptyStateDidChange(_ empty: Bool) + var currentTheme: Theme { get } +} + +public class CollectionViewEditController: NSObject, UIGestureRecognizerDelegate, ActionDelegate { + + let collectionView: UICollectionView + + struct SwipeInfo { + let translation: CGFloat + let velocity: CGFloat + let state: SwipeState + } + var swipeInfoByIndexPath: [IndexPath: SwipeInfo] = [:] + var configuredCellsByIndexPath: [IndexPath: SwipeableCell] = [:] + + var activeCell: SwipeableCell? { + guard let indexPath = activeIndexPath else { + return nil + } + return collectionView.cellForItem(at: indexPath) as? SwipeableCell + } + + public var isActive: Bool { + return activeIndexPath != nil + } + + var activeIndexPath: IndexPath? { + didSet { + if activeIndexPath != nil { + editingState = .swiping + } else { + editingState = isCollectionViewEmpty ? .empty : .none + } + } + } + + var isRTL: Bool = false + var initialSwipeTranslation: CGFloat = 0 + let maxExtension: CGFloat = 10 + + let panGestureRecognizer: UIPanGestureRecognizer + let longPressGestureRecognizer: UILongPressGestureRecognizer + + public init(collectionView: UICollectionView) { + self.collectionView = collectionView + panGestureRecognizer = UIPanGestureRecognizer() + longPressGestureRecognizer = UILongPressGestureRecognizer() + super.init() + panGestureRecognizer.addTarget(self, action: #selector(handlePanGesture)) + longPressGestureRecognizer.addTarget(self, action: #selector(handleLongPressGesture)) + if let gestureRecognizers = self.collectionView.gestureRecognizers { + var otherGestureRecognizer: UIGestureRecognizer + for gestureRecognizer in gestureRecognizers { + otherGestureRecognizer = gestureRecognizer is UIPanGestureRecognizer ? panGestureRecognizer : longPressGestureRecognizer + gestureRecognizer.require(toFail: otherGestureRecognizer) + } + + } + + panGestureRecognizer.delegate = self + self.collectionView.addGestureRecognizer(panGestureRecognizer) + + longPressGestureRecognizer.delegate = self + longPressGestureRecognizer.minimumPressDuration = 0.05 + longPressGestureRecognizer.require(toFail: panGestureRecognizer) + self.collectionView.addGestureRecognizer(longPressGestureRecognizer) + + NotificationCenter.default.addObserver(self, selector: #selector(close), name: UIApplication.willResignActiveNotification, object: nil) + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + public func swipeTranslationForItem(at indexPath: IndexPath) -> CGFloat? { + return swipeInfoByIndexPath[indexPath]?.translation + } + + public func configureSwipeableCell(_ cell: UICollectionViewCell, forItemAt indexPath: IndexPath, layoutOnly: Bool) { + guard + !layoutOnly, + let cell = cell as? SwipeableCell, + cell.isSwipeEnabled else { + return + } + cell.actions = availableActions(at: indexPath) + configuredCellsByIndexPath[indexPath] = cell + guard let info = swipeInfoByIndexPath[indexPath] else { + return + } + cell.swipeState = info.state + cell.actionsView.delegate = self + cell.swipeTranslation = info.translation + } + + public func deconfigureSwipeableCell(_ cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + configuredCellsByIndexPath.removeValue(forKey: indexPath) + } + + public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + if gestureRecognizer === panGestureRecognizer { + return panGestureRecognizerShouldBegin(panGestureRecognizer) + } + + if gestureRecognizer === longPressGestureRecognizer { + return longPressGestureRecognizerShouldBegin(longPressGestureRecognizer) + } + + return false + } + + public weak var delegate: ActionDelegate? + + public func didPerformAction(_ action: Action) -> Bool { + if let cell = activeCell { + return cell.actionsView.updateConfirmationImage(for: action) { + self.delegatePerformingAction(action) + } + } + return self.delegatePerformingAction(action) + } + + private func delegatePerformingAction(_ action: Action) -> Bool { + guard action.indexPath == activeIndexPath else { + return self.delegate?.didPerformAction(action) ?? false + } + let activatedAction = action.type == .delete ? action : nil + closeActionPane(with: activatedAction) { (finished) in + _ = self.delegate?.didPerformAction(action) + } + return true + } + + public func willPerformAction(_ action: Action) -> Bool { + return delegate?.willPerformAction(action) ?? didPerformAction(action) + } + + public func availableActions(at indexPath: IndexPath) -> [Action] { + return delegate?.availableActions(at: indexPath) ?? [] + } + + func panGestureRecognizerShouldBegin(_ gestureRecognizer: UIPanGestureRecognizer) -> Bool { + var shouldBegin = false + defer { + if !shouldBegin { + closeActionPane() + } + } + guard delegate != nil else { + return shouldBegin + } + + let position = gestureRecognizer.location(in: collectionView) + + guard let indexPath = collectionView.indexPathForItem(at: position) else { + return shouldBegin + } + + let velocity = gestureRecognizer.velocity(in: collectionView) + + // Begin only if there's enough x velocity. + if abs(velocity.y) >= abs(velocity.x) { + return shouldBegin + } + + defer { + if let indexPath = activeIndexPath { + initialSwipeTranslation = swipeInfoByIndexPath[indexPath]?.translation ?? 0 + } + } + + isRTL = collectionView.effectiveUserInterfaceLayoutDirection == .rightToLeft + let isOpenSwipe = isRTL ? velocity.x > 0 : velocity.x < 0 + + if !isOpenSwipe { // only allow closing swipes on active cells + shouldBegin = indexPath == activeIndexPath + return shouldBegin + } + + if activeIndexPath != nil && activeIndexPath != indexPath { + closeActionPane() + } + + guard activeIndexPath == nil else { + shouldBegin = true + return shouldBegin + } + + activeIndexPath = indexPath + + guard let cell = activeCell, !cell.actions.isEmpty && cell.isSwipeEnabled else { + activeIndexPath = nil + return shouldBegin + } + + shouldBegin = true + return shouldBegin + } + + func longPressGestureRecognizerShouldBegin(_ gestureRecognizer: UILongPressGestureRecognizer) -> Bool { + guard let cell = activeCell else { + return false + } + + // Don't allow the cancel gesture to recognize if any of the touches are within the actions view. + let numberOfTouches = gestureRecognizer.numberOfTouches + + for touchIndex in 0.. Bool { + + if gestureRecognizer is UILongPressGestureRecognizer { + return true + } + + if gestureRecognizer is UIPanGestureRecognizer { + return otherGestureRecognizer is UILongPressGestureRecognizer + } + + return false + } + + private lazy var batchEditToolbarViewController: BatchEditToolbarViewController = { + let batchEditToolbarViewController = BatchEditToolbarViewController(nibName: "BatchEditToolbarViewController", bundle: Bundle.wmf) + batchEditToolbarViewController.items = self.batchEditToolbarItems + return batchEditToolbarViewController + }() + + public var batchEditToolbarView: UIView { + return self.batchEditToolbarViewController.view + } + + @objc func handlePanGesture(_ sender: UIPanGestureRecognizer) { + guard let indexPath = activeIndexPath, let cell = activeCell, cell.isSwipeEnabled else { + return + } + cell.actionsView.delegate = self + let deltaX = sender.translation(in: collectionView).x + let velocityX = sender.velocity(in: collectionView).x + var swipeTranslation = deltaX + initialSwipeTranslation + let normalizedSwipeTranslation = isRTL ? swipeTranslation : -swipeTranslation + let normalizedMaxSwipeTranslation = abs(cell.swipeTranslationWhenOpen) + switch sender.state { + case .began: + cell.swipeState = .swiping + fallthrough + case .changed: + if normalizedSwipeTranslation < 0 { + let normalizedSqrt = maxExtension * log(abs(normalizedSwipeTranslation)) + swipeTranslation = isRTL ? 0 - normalizedSqrt : normalizedSqrt + } + if normalizedSwipeTranslation > normalizedMaxSwipeTranslation { + let maxWidth = normalizedMaxSwipeTranslation + let delta = normalizedSwipeTranslation - maxWidth + swipeTranslation = isRTL ? maxWidth + (maxExtension * log(delta)) : 0 - maxWidth - (maxExtension * log(delta)) + } + cell.swipeTranslation = swipeTranslation + swipeInfoByIndexPath[indexPath] = SwipeInfo(translation: swipeTranslation, velocity: velocityX, state: .swiping) + case .cancelled: + fallthrough + case .failed: + fallthrough + case .ended: + let isOpen: Bool + let velocityAdjustment = 0.3 * velocityX + if isRTL { + isOpen = swipeTranslation + velocityAdjustment > 0.5 * cell.swipeTranslationWhenOpen + } else { + isOpen = swipeTranslation + velocityAdjustment < 0.5 * cell.swipeTranslationWhenOpen + } + if isOpen { + openActionPane() + } else { + closeActionPane() + } + fallthrough + default: + break + } + } + + @objc func handleLongPressGesture(_ sender: UILongPressGestureRecognizer) { + guard activeIndexPath != nil else { + return + } + + switch sender.state { + case .ended: + closeActionPane() + default: + break + } + } + + var areSwipeActionsDisabled: Bool = false { + didSet { + longPressGestureRecognizer.isEnabled = !areSwipeActionsDisabled + panGestureRecognizer.isEnabled = !areSwipeActionsDisabled + } + } + + // MARK: - States + + func openActionPane(_ completion: @escaping (Bool) -> Void = {_ in }) { + collectionView.allowsSelection = false + guard let cell = activeCell, let indexPath = activeIndexPath else { + completion(false) + return + } + let targetTranslation = cell.swipeTranslationWhenOpen + let velocity = swipeInfoByIndexPath[indexPath]?.velocity ?? 0 + swipeInfoByIndexPath[indexPath] = SwipeInfo(translation: targetTranslation, velocity: velocity, state: .open) + cell.swipeState = .open + animateActionPane(of: cell, to: targetTranslation, with: velocity, completion: completion) + } + + func closeActionPane(with expandedAction: Action? = nil, _ completion: @escaping (Bool) -> Void = {_ in }) { + collectionView.allowsSelection = true + guard let cell = activeCell, let indexPath = activeIndexPath else { + completion(false) + return + } + activeIndexPath = nil + let velocity = swipeInfoByIndexPath[indexPath]?.velocity ?? 0 + swipeInfoByIndexPath[indexPath] = nil + if let expandedAction = expandedAction { + let translation = isRTL ? cell.bounds.width : 0 - cell.bounds.width + animateActionPane(of: cell, to: translation, with: velocity, expandedAction: expandedAction, completion: { (finished) in + // don't set isSwiping to false so that the expanded action stays visible through the fade + completion(finished) + }) + } else { + animateActionPane(of: cell, to: 0, with: velocity, completion: { (finished: Bool) in + cell.swipeState = self.activeIndexPath == indexPath ? .swiping : .closed + completion(finished) + }) + } + } + + func animateActionPane(of cell: SwipeableCell, to targetTranslation: CGFloat, with swipeVelocity: CGFloat, expandedAction: Action? = nil, completion: @escaping (Bool) -> Void = {_ in }) { + if let action = expandedAction { + UIView.animate(withDuration: 0.3, delay: 0, options: [.allowUserInteraction, .beginFromCurrentState], animations: { + cell.actionsView.expand(action) + cell.swipeTranslation = targetTranslation + cell.layoutIfNeeded() + }, completion: completion) + return + } + let initialSwipeTranslation = cell.swipeTranslation + let animationTranslation = targetTranslation - initialSwipeTranslation + let animationDuration: TimeInterval = 0.3 + let distanceInOneSecond = animationTranslation / CGFloat(animationDuration) + let unitSpeed = distanceInOneSecond == 0 ? 0 : swipeVelocity / distanceInOneSecond + UIView.animate(withDuration: animationDuration, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: unitSpeed, options: [.allowUserInteraction, .beginFromCurrentState], animations: { + cell.swipeTranslation = targetTranslation + cell.layoutIfNeeded() + }, completion: completion) + } + + // MARK: - Batch editing + + public var isShowingDefaultCellOnly: Bool = false { + didSet { + guard oldValue != isShowingDefaultCellOnly else { + return + } + editingState = isCollectionViewEmpty || isShowingDefaultCellOnly ? .empty : .none + } + } + + public weak var navigationDelegate: CollectionViewEditControllerNavigationDelegate? { + willSet { + batchEditToolbarViewController.remove() + } + didSet { + guard oldValue !== navigationDelegate else { + return + } + if navigationDelegate == nil { + editingState = .unknown + } else { + editingState = isCollectionViewEmpty || isShowingDefaultCellOnly ? .empty : .none + } + } + } + + private var editableCells: [BatchEditableCell] { + guard let editableCells = collectionView.visibleCells as? [BatchEditableCell] else { + return [] + } + return editableCells + } + + public var isBatchEditing: Bool { + return editingState == .open + } + + private var editingState: EditingState = .unknown { + didSet { + guard editingState != oldValue else { + return + } + editingStateDidChange(from: oldValue, to: editingState) + } + } + + private func editingStateDidChange(from oldValue: EditingState, to newValue: EditingState) { + + let rightBarButtonSystemItem: UIBarButtonItem.SystemItem? + let leftBarButtonSystemItem: UIBarButtonItem.SystemItem? + var isRightBarButtonEnabled = !(isCollectionViewEmpty || isShowingDefaultCellOnly) || shouldShowEditButtonsForEmptyState + + switch newValue { + case .editing: + areSwipeActionsDisabled = true + leftBarButtonSystemItem = .cancel + rightBarButtonSystemItem = .done + isRightBarButtonEnabled = true + if oldValue == .open { + transformBatchEditPane(for: editingState) + } + case .swiping: + leftBarButtonSystemItem = nil + rightBarButtonSystemItem = .edit + case .open: + leftBarButtonSystemItem = nil + rightBarButtonSystemItem = .cancel + transformBatchEditPane(for: editingState) + case .closed: + leftBarButtonSystemItem = nil + rightBarButtonSystemItem = .edit + transformBatchEditPane(for: editingState) + case .empty: + leftBarButtonSystemItem = nil + rightBarButtonSystemItem = shouldShowEditButtonsForEmptyState ? .edit : nil + isBatchEditToolbarHidden = true + default: + leftBarButtonSystemItem = nil + rightBarButtonSystemItem = .edit + } + + var rightButton: SystemBarButton? + var leftButton: SystemBarButton? + + if let barButtonSystemItem = rightBarButtonSystemItem { + rightButton = SystemBarButton(with: barButtonSystemItem, target: self, action: #selector(barButtonPressed(_:))) + } + + if let barButtonSystemItem = leftBarButtonSystemItem { + leftButton = SystemBarButton(with: barButtonSystemItem, target: self, action: #selector(barButtonPressed(_:))) + } + + leftButton?.tag = editingState.tag + rightButton?.tag = editingState.tag + rightButton?.isEnabled = isRightBarButtonEnabled + + let font = rightBarButtonSystemItem != .edit ? UIFont.wmf_font(.semiboldBody) : UIFont.wmf_font(.body) + let attributes = [NSAttributedString.Key.font: font] + rightButton?.setTitleTextAttributes(attributes, for: .normal) + leftButton?.setTitleTextAttributes(attributes, for: .normal) + + navigationDelegate?.didChangeEditingState(from: oldValue, to: editingState, rightBarButton: rightButton, leftBarButton: leftButton) + } + + private func transformBatchEditPane(for state: EditingState, animated: Bool = true) { + guard !isCollectionViewEmpty else { + return + } + let willOpen = state == .open + areSwipeActionsDisabled = willOpen + collectionView.allowsMultipleSelection = willOpen + isBatchEditToolbarHidden = !willOpen + for cell in editableCells { + guard cell.isBatchEditable else { + continue + } + if animated { + // ensure layout is in the start anim state + cell.isBatchEditing = !willOpen + cell.layoutIfNeeded() + UIView.animate(withDuration: 0.3, delay: 0.1, options: [.allowUserInteraction, .beginFromCurrentState, .curveEaseInOut], animations: { + cell.isBatchEditing = willOpen + cell.layoutIfNeeded() + }) + } else { + cell.isBatchEditing = willOpen + cell.layoutIfNeeded() + } + if let themeableCell = cell as? Themeable, let navigationDelegate = navigationDelegate { + themeableCell.apply(theme: navigationDelegate.currentTheme) + } + } + if !willOpen { + selectedIndexPaths.forEach({ collectionView.deselectItem(at: $0, animated: true) }) + batchEditToolbarViewController.setItemsEnabled(false) + } + } + + @objc public func close() { + guard editingState == .open || editingState == .swiping else { + return + } + if editingState == .swiping { + editingState = .none + } else { + editingState = .closed + } + closeActionPane() + } + + private func emptyStateDidChange() { + if isCollectionViewEmpty || isShowingDefaultCellOnly { + editingState = .empty + } else { + editingState = .none + } + navigationDelegate?.emptyStateDidChange(isCollectionViewEmpty) + } + + public var isCollectionViewEmpty: Bool = true { + didSet { + guard oldValue != isCollectionViewEmpty else { + return + } + emptyStateDidChange() + } + } + + public var shouldShowEditButtonsForEmptyState: Bool = false + + @objc private func barButtonPressed(_ sender: SystemBarButton) { + guard let navigationDelegate = navigationDelegate else { + assertionFailure("Unable to set new editing state - navigationDelegate is nil") + return + } + guard let systemItem = sender.systemItem else { + assertionFailure("Unable to set new editing state - systemItem is nil") + return + } + let currentEditingState = editingState + if currentEditingState == .swiping { + closeActionPane() + } + editingState = navigationDelegate.newEditingState(for: currentEditingState, fromEditBarButtonWithSystemItem: systemItem) + } + + public func changeEditingState(to newEditingState: EditingState) { + editingState = newEditingState + } + + public var isTextEditing: Bool = false { + didSet { + editingState = isTextEditing ? .editing : .done + } + } + + public var isClosed: Bool { + let isClosed = editingState != .open + if !isClosed { + batchEditToolbarViewController.setItemsEnabled(!selectedIndexPaths.isEmpty) + } + return isClosed + } + + public func transformBatchEditPaneOnScroll() { + transformBatchEditPane(for: editingState, animated: false) + } + + private var selectedIndexPaths: [IndexPath] { + return collectionView.indexPathsForSelectedItems ?? [] + } + + private var isBatchEditToolbarHidden: Bool = true { + didSet { + self.navigationDelegate?.didSetBatchEditToolbarHidden(batchEditToolbarViewController, isHidden: self.isBatchEditToolbarHidden, with: self.batchEditToolbarItems) + } + } + + private var batchEditToolbarActions: [BatchEditToolbarAction] { + guard let delegate = delegate, let actions = delegate.availableBatchEditToolbarActions else { + return [] + } + return actions + } + + @objc public func didPerformBatchEditToolbarAction(with sender: UIBarButtonItem) { + guard let delegate = delegate else { + assertionFailure("delegate should be set by now") + editingState = .closed + return + } + guard let didPerformBatchEditToolbarAction = delegate.didPerformBatchEditToolbarAction else { + assertionFailure("delegate should implement didPerformBatchEditToolbarAction") + editingState = .closed + return + } + let action = batchEditToolbarActions[sender.tag] + didPerformBatchEditToolbarAction(action) { finished in + if finished { + self.editingState = .closed + } + } + } + + private lazy var batchEditToolbarItems: [UIButton] = { + + var buttons: [UIButton] = [] + + for (index, action) in batchEditToolbarActions.enumerated() { + let button = UIButton(type: .system) + button.addTarget(self, action: #selector(didPerformBatchEditToolbarAction(with:)), for: .touchUpInside) + button.tag = index + button.setTitle(action.title, for: UIControl.State.normal) + buttons.append(button) + button.isEnabled = false + } + + return buttons + }() + +} diff --git a/Apps/Wikipedia/WMF Framework/ColumarCollectionViewLayoutSection.swift b/Apps/Wikipedia/WMF Framework/ColumarCollectionViewLayoutSection.swift new file mode 100644 index 0000000..e796156 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ColumarCollectionViewLayoutSection.swift @@ -0,0 +1,221 @@ +class ColumnarCollectionViewLayoutSection { + private class ColumnarCollectionViewLayoutColumn { + var frame: CGRect + init(frame: CGRect) { + self.frame = frame + } + + func addItem(_ attributes: ColumnarCollectionViewLayoutAttributes) { + frame.size.height += attributes.frame.height + } + + var widthForNextItem: CGFloat { + return frame.width + } + + var originForNextItem: CGPoint { + return CGPoint(x: frame.minX, y: frame.maxY) + } + + func addSpace(_ space: CGFloat) { + frame.size.height += space + } + } + + let sectionIndex: Int + var frame: CGRect = .zero + let metrics: ColumnarCollectionViewLayoutMetrics + var headers: [ColumnarCollectionViewLayoutAttributes] = [] + var items: [ColumnarCollectionViewLayoutAttributes] = [] + var footers: [ColumnarCollectionViewLayoutAttributes] = [] + private let columns: [ColumnarCollectionViewLayoutColumn] + private var columnIndexByItemIndex: [Int: Int] = [:] + private var shortestColumnIndex: Int = 0 + + init(sectionIndex: Int, frame: CGRect, metrics: ColumnarCollectionViewLayoutMetrics, countOfItems: Int) { + let countOfColumns = metrics.countOfColumns + let columnSpacing = metrics.interColumnSpacing + let columnWidth: CGFloat = floor((frame.size.width - (columnSpacing * CGFloat(countOfColumns - 1))) / CGFloat(countOfColumns)) + var columns: [ColumnarCollectionViewLayoutColumn] = [] + columns.reserveCapacity(countOfColumns) + var x: CGFloat = frame.origin.x + for _ in 0.. ColumnarCollectionViewLayoutColumn? { + guard let columnIndex = columnIndexByItemIndex[itemIndex] else { + return nil + } + return columns[columnIndex] + } + + private var columnForNextItem: ColumnarCollectionViewLayoutColumn { + let itemIndex = items.count + let columnIndex = shortestColumnIndex + columnIndexByItemIndex[itemIndex] = columnIndex + return columns[columnIndex] + } + + var widthForNextItem: CGFloat { + return columnForNextItem.widthForNextItem + } + + var widthForNextSupplementaryView: CGFloat { + return frame.size.width + } + + var originForNextSupplementaryView: CGPoint { + return CGPoint(x: frame.minX, y: frame.minY) + } + + var originForNextItem: CGPoint { + return columnForNextItem.originForNextItem + } + + var widthForSupplementaryViews: CGFloat { + return frame.width + } + + func addHeader(_ attributes: ColumnarCollectionViewLayoutAttributes) { + headers.append(attributes) + frame.size.height += attributes.frame.size.height + for column in columns { + column.addSpace(attributes.frame.size.height) + } + } + + func addItem(_ attributes: ColumnarCollectionViewLayoutAttributes) { + let column = columnForNextItem + if metrics.interItemSpacing > 0 { + column.addSpace(metrics.interItemSpacing) + } + column.addItem(attributes) + items.append(attributes) + if column.frame.height > frame.height { + frame.size.height = column.frame.height + } + updateShortestColumnIndex() + } + + func updateShortestColumnIndex() { + guard columns.count > 1 else { + return + } + var minHeight: CGFloat = CGFloat.greatestFiniteMagnitude + for (index, column) in columns.enumerated() { + guard column.frame.height < minHeight else { + continue + } + minHeight = column.frame.height + shortestColumnIndex = index + } + } + + func addFooter(_ attributes: ColumnarCollectionViewLayoutAttributes) { + footers.append(attributes) + frame.size.height += attributes.frame.size.height + } + + func updateAttributes(at index: Int, in array: [ColumnarCollectionViewLayoutAttributes], with attributes: ColumnarCollectionViewLayoutAttributes) -> CGFloat { + guard array.indices.contains(index) else { + return 0 + } + let oldAttributes = array[index] + let newFrame = CGRect(origin: oldAttributes.frame.origin, size: attributes.frame.size) + let deltaY = newFrame.height - oldAttributes.frame.height + guard !deltaY.isEqual(to: 0) else { + return 0 + } + oldAttributes.frame = newFrame + return deltaY + } + + + func translateAttributesBy(_ deltaY: CGFloat, at index: Int, in array: [ColumnarCollectionViewLayoutAttributes]) -> [IndexPath] { + guard !deltaY.isEqual(to: 0), array.indices.contains(index) else { + return [] + } + var invalidatedIndexPaths: [IndexPath] = [] + for (index, attributes) in array[index.. ColumnarCollectionViewLayoutSectionInvalidationResults { + let index = originalAttributes.indexPath.item + switch originalAttributes.representedElementCategory { + + case UICollectionView.ElementCategory.decorationView: + return ColumnarCollectionViewLayoutSectionInvalidationResults.empty + case UICollectionView.ElementCategory.supplementaryView: + switch originalAttributes.representedElementKind { + case UICollectionView.elementKindSectionHeader: + let deltaY = updateAttributes(at: index, in: headers, with: attributes) + frame.size.height += deltaY + var invalidatedHeaderIndexPaths = translateAttributesBy(deltaY, at: index + 1, in: headers) + invalidatedHeaderIndexPaths.append(originalAttributes.indexPath) + let invalidatedItemIndexPaths = translateAttributesBy(deltaY, at: 0, in: items) + let invalidatedFooterIndexPaths = translateAttributesBy(deltaY, at: 0, in: footers) + return ColumnarCollectionViewLayoutSectionInvalidationResults(invalidatedHeaderIndexPaths: invalidatedHeaderIndexPaths, invalidatedItemIndexPaths: invalidatedItemIndexPaths, invalidatedFooterIndexPaths: invalidatedFooterIndexPaths) + case UICollectionView.elementKindSectionFooter: + let deltaY = updateAttributes(at: index, in: footers, with: attributes) + frame.size.height += deltaY + var invalidatedFooterIndexPaths = translateAttributesBy(deltaY, at: index + 1, in: footers) + invalidatedFooterIndexPaths.append(originalAttributes.indexPath) + return ColumnarCollectionViewLayoutSectionInvalidationResults(invalidatedHeaderIndexPaths: [], invalidatedItemIndexPaths: [], invalidatedFooterIndexPaths: invalidatedFooterIndexPaths) + default: + return ColumnarCollectionViewLayoutSectionInvalidationResults.empty + } + default: + var invalidatedItemIndexPaths: [IndexPath] = [originalAttributes.indexPath] + let deltaY = updateAttributes(at: index, in: items, with: attributes) + guard + let columnIndex = columnIndexByItemIndex[index] + else { + return ColumnarCollectionViewLayoutSectionInvalidationResults.empty + } + + let column = columns[columnIndex] + + column.frame.size.height += deltaY + if column.frame.height > frame.height { + frame.size.height = column.frame.height + } + + let nextIndex = index + 1 + if items.indices.contains(nextIndex) { + for affectedIndex in nextIndex.. ColumnarCollectionViewLayoutSectionInvalidationResults { + let invalidatedHeaderIndexPaths = translateAttributesBy(deltaY, at: 0, in: headers) + let invalidatedItemIndexPaths = translateAttributesBy(deltaY, at: 0, in: items) + let invalidatedFooterIndexPaths = translateAttributesBy(deltaY, at: 0, in: footers) + frame.origin.y += deltaY + return ColumnarCollectionViewLayoutSectionInvalidationResults(invalidatedHeaderIndexPaths: invalidatedHeaderIndexPaths, invalidatedItemIndexPaths: invalidatedItemIndexPaths, invalidatedFooterIndexPaths: invalidatedFooterIndexPaths) + } +} diff --git a/Apps/Wikipedia/WMF Framework/ColumnarCollectionViewLayout.swift b/Apps/Wikipedia/WMF Framework/ColumnarCollectionViewLayout.swift new file mode 100644 index 0000000..4ee1b51 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ColumnarCollectionViewLayout.swift @@ -0,0 +1,318 @@ +public protocol ColumnarCollectionViewLayoutDelegate { + func collectionView(_ collectionView: UICollectionView, estimatedHeightForItemAt indexPath: IndexPath, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate + func collectionView(_ collectionView: UICollectionView, estimatedHeightForHeaderInSection section: Int, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate + func collectionView(_ collectionView: UICollectionView, estimatedHeightForFooterInSection section: Int, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate + func collectionView(_ collectionView: UICollectionView, shouldShowFooterForSection section: Int) -> Bool + func metrics(with boundsSize: CGSize, readableWidth: CGFloat, layoutMargins: UIEdgeInsets) -> ColumnarCollectionViewLayoutMetrics +} + +public class ColumnarCollectionViewLayout: UICollectionViewLayout { + var info: ColumnarCollectionViewLayoutInfo? { + didSet { + oldInfo = oldValue + } + } + var oldInfo: ColumnarCollectionViewLayoutInfo? + var metrics: ColumnarCollectionViewLayoutMetrics? + var isLayoutValid: Bool = false + let defaultColumnWidth: CGFloat = 315 + let maxColumnWidth: CGFloat = 740 + public var slideInNewContentFromTheTop: Bool = false + public var animateItems: Bool = false + + override public class var layoutAttributesClass: Swift.AnyClass { + return ColumnarCollectionViewLayoutAttributes.self + } + + override public class var invalidationContextClass: Swift.AnyClass { + return ColumnarCollectionViewLayoutInvalidationContext.self + } + + private var delegate: ColumnarCollectionViewLayoutDelegate? { + return collectionView?.delegate as? ColumnarCollectionViewLayoutDelegate + } + + override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { + guard let sections = info?.sections else { + return [] + } + var attributes: [UICollectionViewLayoutAttributes] = [] + for section in sections { + guard rect.intersects(section.frame) else { + continue + } + for item in section.headers { + guard rect.intersects(item.frame) else { + continue + } + attributes.append(item) + } + for item in section.items { + guard rect.intersects(item.frame) else { + continue + } + attributes.append(item) + } + for item in section.footers { + guard rect.intersects(item.frame) else { + continue + } + attributes.append(item) + } + } + return attributes + } + + override public func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { + return info?.layoutAttributesForItem(at: indexPath) + } + + public override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { + return nil + } + + public override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { + return info?.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath) + } + + public var itemLayoutMargins: UIEdgeInsets { + guard let metrics = metrics else { + return .zero + } + return metrics.itemLayoutMargins + } + + override public var collectionViewContentSize: CGSize { + guard let info = info else { + return .zero + } + return info.contentSize + } + + public func layoutHeight(forWidth width: CGFloat) -> CGFloat { + guard let collectionView = collectionView, let delegate = delegate, width >= 1 else { + return 0 + } + let oldMetrics = metrics + let newInfo = ColumnarCollectionViewLayoutInfo() + let newMetrics = delegate.metrics(with: CGSize(width: width, height: 100), readableWidth: width, layoutMargins: .zero) + metrics = newMetrics // needs to be set so that layout margins can be queried. probably not the best solution. + newInfo.layout(with: newMetrics, delegate: delegate, collectionView: collectionView, invalidationContext: nil) + metrics = oldMetrics + return newInfo.contentSize.height + } + + override public func prepare() { + defer { + super.prepare() + } + + guard let collectionView = collectionView else { + return + } + + let size = collectionView.bounds.size + guard size.width > 0 && size.height > 0 else { + return + } + + let readableWidth: CGFloat = collectionView.readableContentGuide.layoutFrame.size.width + + if let metrics = metrics, !metrics.readableWidth.isEqual(to: readableWidth) { + isLayoutValid = false + } + + guard let delegate = delegate, !isLayoutValid else { + return + } + + let delegateMetrics = delegate.metrics(with: size, readableWidth: readableWidth, layoutMargins: collectionView.layoutMargins) + metrics = delegateMetrics + let newInfo = ColumnarCollectionViewLayoutInfo() + newInfo.layout(with: delegateMetrics, delegate: delegate, collectionView: collectionView, invalidationContext: nil) + info = newInfo + isLayoutValid = true + } + + // MARK: - Invalidation + + override public func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) { + defer { + super.invalidateLayout(with: context) + } + + guard let context = context as? ColumnarCollectionViewLayoutInvalidationContext else { + return + } + + guard context.invalidateEverything || context.invalidateDataSourceCounts || context.boundsDidChange else { + return + } + + isLayoutValid = false + } + + override public func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { + guard let metrics = metrics else { + return true + } + return !newBounds.size.width.isEqual(to: metrics.boundsSize.width) + } + + override public func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext { + let superContext = super.invalidationContext(forBoundsChange: newBounds) + let context = superContext as? ColumnarCollectionViewLayoutInvalidationContext ?? ColumnarCollectionViewLayoutInvalidationContext() + context.boundsDidChange = true + return context + } + + override public func shouldInvalidateLayout(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> Bool { + return !preferredAttributes.frame.equalTo(originalAttributes.frame) + } + + override public func invalidationContext(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutInvalidationContext { + let superContext = super.invalidationContext(forPreferredLayoutAttributes: preferredAttributes, withOriginalAttributes: originalAttributes) + let context = superContext as? ColumnarCollectionViewLayoutInvalidationContext ?? ColumnarCollectionViewLayoutInvalidationContext() + context.preferredLayoutAttributes = preferredAttributes + context.originalLayoutAttributes = originalAttributes + if let delegate = delegate, let metrics = metrics, let info = info, let collectionView = collectionView { + info.update(with: metrics, invalidationContext: context, delegate: delegate, collectionView: collectionView) + } + return context + } + + // MARK: - Animation + + var maxNewSection: Int = -1 + var newSectionDeltaY: CGFloat = 0 + var appearingIndexPaths: Set = [] + var disappearingIndexPaths: Set = [] + override public func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) { + super.prepare(forCollectionViewUpdates: updateItems) + guard animateItems, let info = info else { + appearingIndexPaths.removeAll(keepingCapacity: true) + disappearingIndexPaths.removeAll(keepingCapacity: true) + maxNewSection = -1 + newSectionDeltaY = 0 + return + } + + if slideInNewContentFromTheTop { + var maxSection = -1 + for updateItem in updateItems { + guard let after = updateItem.indexPathAfterUpdate, after.item == NSNotFound, updateItem.indexPathBeforeUpdate == nil else { + continue + } + let section: Int = after.section + guard section == maxSection + 1 else { + continue + } + maxSection = section + } + guard maxSection > -1 && maxSection < info.sections.count else { + maxNewSection = -1 + return + } + maxNewSection = maxSection + let sectionFrame = info.sections[maxSection].frame + newSectionDeltaY = 0 - sectionFrame.maxY + appearingIndexPaths.removeAll(keepingCapacity: true) + disappearingIndexPaths.removeAll(keepingCapacity: true) + } else { + appearingIndexPaths.removeAll(keepingCapacity: true) + disappearingIndexPaths.removeAll(keepingCapacity: true) + newSectionDeltaY = 0 + maxNewSection = -1 + for updateItem in updateItems { + if let after = updateItem.indexPathAfterUpdate, updateItem.indexPathBeforeUpdate == nil { + appearingIndexPaths.insert(after) + } else if let before = updateItem.indexPathBeforeUpdate, updateItem.indexPathAfterUpdate == nil { + disappearingIndexPaths.insert(before) + } + } + } + } + + private func adjustAttributesIfNecessary(_ attributes: UICollectionViewLayoutAttributes, forItemOrElementAppearingAtIndexPath indexPath: IndexPath) { + guard indexPath.section <= maxNewSection else { + guard animateItems, appearingIndexPaths.contains(indexPath) else { + return + } + attributes.zIndex = -1 + attributes.alpha = 0 + return + } + attributes.frame.origin.y += newSectionDeltaY + attributes.alpha = 1 + } + + public override func initialLayoutAttributesForAppearingSupplementaryElement(ofKind elementKind: String, at elementIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? { + guard let attributes = super.initialLayoutAttributesForAppearingSupplementaryElement(ofKind: elementKind, at: elementIndexPath) else { + return nil + } + adjustAttributesIfNecessary(attributes, forItemOrElementAppearingAtIndexPath: elementIndexPath) + return attributes + } + + public override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? { + guard let attributes = super.initialLayoutAttributesForAppearingItem(at: itemIndexPath) else { + return nil + } + adjustAttributesIfNecessary(attributes, forItemOrElementAppearingAtIndexPath: itemIndexPath) + return attributes + } + + public override func initialLayoutAttributesForAppearingDecorationElement(ofKind elementKind: String, at decorationIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? { + return super.initialLayoutAttributesForAppearingDecorationElement(ofKind: elementKind, at: decorationIndexPath) + } + + public override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? { + guard let attributes = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath) else { + return nil + } + guard animateItems, disappearingIndexPaths.contains(itemIndexPath) else { + return attributes + } + attributes.zIndex = -1 + attributes.alpha = 0 + return attributes + } + + // MARK: Scroll View + + public var currentSection: Int? + + public override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint { + var superTarget = super.targetContentOffset(forProposedContentOffset: proposedContentOffset) + if let currentSection = currentSection, + let oldInfo = oldInfo, + let info = info, + oldInfo.sections.indices.contains(currentSection), + info.sections.indices.contains(currentSection) { + let oldY = oldInfo.sections[currentSection].frame.origin.y + let newY = info.sections[currentSection].frame.origin.y + let deltaY = newY - oldY + superTarget.y += deltaY + } + return superTarget + } +} + +extension ColumnarCollectionViewLayout: NSCopying { + public func copy(with zone: NSZone? = nil) -> Any { + let newLayout = ColumnarCollectionViewLayout() + newLayout.info = info + newLayout.oldInfo = oldInfo + newLayout.metrics = metrics + newLayout.isLayoutValid = isLayoutValid + newLayout.slideInNewContentFromTheTop = slideInNewContentFromTheTop + newLayout.animateItems = animateItems + + newLayout.maxNewSection = maxNewSection + newLayout.newSectionDeltaY = newSectionDeltaY + newLayout.appearingIndexPaths = appearingIndexPaths + newLayout.disappearingIndexPaths = disappearingIndexPaths + + return newLayout + } +} diff --git a/Apps/Wikipedia/WMF Framework/ColumnarCollectionViewLayoutInfo.swift b/Apps/Wikipedia/WMF Framework/ColumnarCollectionViewLayoutInfo.swift new file mode 100644 index 0000000..ae3082c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ColumnarCollectionViewLayoutInfo.swift @@ -0,0 +1,175 @@ +struct ColumnarCollectionViewLayoutSectionInvalidationResults { + let invalidatedHeaderIndexPaths: [IndexPath] + let invalidatedItemIndexPaths: [IndexPath] + let invalidatedFooterIndexPaths: [IndexPath] + + static let empty: ColumnarCollectionViewLayoutSectionInvalidationResults = ColumnarCollectionViewLayoutSectionInvalidationResults(invalidatedHeaderIndexPaths: [], invalidatedItemIndexPaths: [], invalidatedFooterIndexPaths: []) +} + +public class ColumnarCollectionViewLayoutInfo { + var sections: [ColumnarCollectionViewLayoutSection] = [] + var contentSize: CGSize = .zero + + func layout(with metrics: ColumnarCollectionViewLayoutMetrics, delegate: ColumnarCollectionViewLayoutDelegate, collectionView: UICollectionView, invalidationContext context: ColumnarCollectionViewLayoutInvalidationContext?) { + guard let dataSource = collectionView.dataSource else { + return + } + guard let countOfSections = dataSource.numberOfSections?(in: collectionView), countOfSections > 0 else { + return + } + sections.reserveCapacity(countOfSections) + let x = metrics.layoutMargins.left + var y = metrics.layoutMargins.top + let width = metrics.boundsSize.width - metrics.layoutMargins.left - metrics.layoutMargins.right + for sectionIndex in 0.. UICollectionViewLayoutAttributes? { + guard sections.indices.contains(indexPath.section) else { + return nil + } + let section = sections[indexPath.section] + guard section.items.indices.contains(indexPath.item) else { + return nil + } + return section.items[indexPath.item] + } + + public func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { + guard sections.indices.contains(indexPath.section) else { + return nil + } + let section = sections[indexPath.section] + switch elementKind { + case UICollectionView.elementKindSectionHeader: + guard section.headers.indices.contains(indexPath.item) else { + return nil + } + return section.headers[indexPath.item] + case UICollectionView.elementKindSectionFooter: + guard section.footers.indices.contains(indexPath.item) else { + return nil + } + return section.footers[indexPath.item] + default: + return nil + } + } +} + +class ColumnarCollectionViewLayoutInvalidationContext: UICollectionViewLayoutInvalidationContext { + var originalLayoutAttributes: UICollectionViewLayoutAttributes? + var preferredLayoutAttributes: UICollectionViewLayoutAttributes? + var boundsDidChange: Bool = false +} + +public class ColumnarCollectionViewLayoutAttributes: UICollectionViewLayoutAttributes { + public var precalculated: Bool = false + public var layoutMargins: UIEdgeInsets = .zero + + override public func copy(with zone: NSZone? = nil) -> Any { + let copy = super.copy(with: zone) + guard let la = copy as? ColumnarCollectionViewLayoutAttributes else { + return copy + } + la.precalculated = precalculated + la.layoutMargins = layoutMargins + return la + } +} + +public struct ColumnarCollectionViewLayoutHeightEstimate { + public var precalculated: Bool + public var height: CGFloat + public init(precalculated: Bool, height: CGFloat) { + self.precalculated = precalculated + self.height = height + } +} diff --git a/Apps/Wikipedia/WMF Framework/ColumnarCollectionViewLayoutMetrics.swift b/Apps/Wikipedia/WMF Framework/ColumnarCollectionViewLayoutMetrics.swift new file mode 100644 index 0000000..51837a4 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ColumnarCollectionViewLayoutMetrics.swift @@ -0,0 +1,52 @@ +public struct ColumnarCollectionViewLayoutMetrics { + public static let defaultItemLayoutMargins = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15) // individual cells on each explore card + public static let defaultExploreItemLayoutMargins = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15) // explore card cells + let boundsSize: CGSize + let layoutMargins: UIEdgeInsets + let countOfColumns: Int + let itemLayoutMargins: UIEdgeInsets + let readableWidth: CGFloat + let interSectionSpacing: CGFloat + let interColumnSpacing: CGFloat + let interItemSpacing: CGFloat + var shouldMatchColumnHeights = false + + public static func exploreViewMetrics(with boundsSize: CGSize, readableWidth: CGFloat, layoutMargins: UIEdgeInsets) -> ColumnarCollectionViewLayoutMetrics { + let useTwoColumns = boundsSize.width >= 600 || (boundsSize.width > boundsSize.height && readableWidth >= 500) + let countOfColumns = useTwoColumns ? 2 : 1 + let interColumnSpacing: CGFloat = useTwoColumns ? 20 : 0 + let interItemSpacing: CGFloat = 35 + let interSectionSpacing: CGFloat = useTwoColumns ? 20 : 0 + + let layoutMarginsForMetrics: UIEdgeInsets + let itemLayoutMargins: UIEdgeInsets + let defaultItemMargins = ColumnarCollectionViewLayoutMetrics.defaultExploreItemLayoutMargins + let topAndBottomMargin: CGFloat = 30 // space between top of navigation bar and first section + if useTwoColumns { + let itemMarginWidth = max(defaultItemMargins.left, defaultItemMargins.right) + let marginWidth = max(max(max(layoutMargins.left, layoutMargins.right), round(0.5 * (boundsSize.width - (readableWidth * CGFloat(countOfColumns))))), itemMarginWidth) + layoutMarginsForMetrics = UIEdgeInsets(top: topAndBottomMargin, left: marginWidth - itemMarginWidth, bottom: topAndBottomMargin, right: marginWidth - itemMarginWidth) + itemLayoutMargins = UIEdgeInsets(top: defaultItemMargins.top, left: itemMarginWidth, bottom: defaultItemMargins.bottom, right: itemMarginWidth) + } else { + let marginWidth = max(layoutMargins.left, layoutMargins.right) + itemLayoutMargins = UIEdgeInsets(top: defaultItemMargins.top, left: marginWidth, bottom: defaultItemMargins.bottom, right: marginWidth) + layoutMarginsForMetrics = UIEdgeInsets(top: topAndBottomMargin, left: 0, bottom: topAndBottomMargin, right: 0) + } + + return ColumnarCollectionViewLayoutMetrics(boundsSize: boundsSize, layoutMargins: layoutMarginsForMetrics, countOfColumns: countOfColumns, itemLayoutMargins: itemLayoutMargins, readableWidth: readableWidth, interSectionSpacing: interSectionSpacing, interColumnSpacing: interColumnSpacing, interItemSpacing: interItemSpacing, shouldMatchColumnHeights: false) + } + + + public static func tableViewMetrics(with boundsSize: CGSize, readableWidth: CGFloat, layoutMargins: UIEdgeInsets, interSectionSpacing: CGFloat = 0, interItemSpacing: CGFloat = 0) -> ColumnarCollectionViewLayoutMetrics { + let marginWidth = max(max(layoutMargins.left, layoutMargins.right), round(0.5 * (boundsSize.width - readableWidth))) + var itemLayoutMargins = ColumnarCollectionViewLayoutMetrics.defaultItemLayoutMargins + itemLayoutMargins.left = max(marginWidth, itemLayoutMargins.left) + itemLayoutMargins.right = max(marginWidth, itemLayoutMargins.right) + return ColumnarCollectionViewLayoutMetrics(boundsSize: boundsSize, layoutMargins: .zero, countOfColumns: 1, itemLayoutMargins: itemLayoutMargins, readableWidth: readableWidth, interSectionSpacing: interSectionSpacing, interColumnSpacing: 0, interItemSpacing: interItemSpacing, shouldMatchColumnHeights: false) + } + + public static func exploreCardMetrics(with boundsSize: CGSize, readableWidth: CGFloat, layoutMargins: UIEdgeInsets) -> ColumnarCollectionViewLayoutMetrics { + let itemLayoutMargins = ColumnarCollectionViewLayoutMetrics.defaultItemLayoutMargins + return ColumnarCollectionViewLayoutMetrics(boundsSize: boundsSize, layoutMargins: layoutMargins, countOfColumns: 1, itemLayoutMargins: itemLayoutMargins, readableWidth: readableWidth, interSectionSpacing: 0, interColumnSpacing: 0, interItemSpacing: 0, shouldMatchColumnHeights: false) + } +} diff --git a/Apps/Wikipedia/WMF Framework/CommonStrings.swift b/Apps/Wikipedia/WMF Framework/CommonStrings.swift new file mode 100644 index 0000000..3b7f854 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/CommonStrings.swift @@ -0,0 +1,470 @@ +import Foundation + +// Utilize this class to define localized strings that are used in multiple places in similar contexts. +// There should only be one WMF Localized String function in code for every localization key. +// If the same string value is used in different contexts, use different localization keys. + +@objc(WMFCommonStrings) +public class CommonStrings: NSObject { + @objc public static let plainWikipediaName = CommonStrings.plainWikipediaName() + @objc public static func plainWikipediaName(with languageCode: String? = nil) -> String { + WMFLocalizedString("about-wikipedia", languageCode: languageCode, value:"Wikipedia", comment: "Wikipedia {{Identical|Wikipedia}}") + } + + @objc public static let articleCountFormat = WMFLocalizedString("places-filter-top-articles-count", value:"{{PLURAL:%1$d|%1$d article|%1$d articles}}", comment: "Describes how many top articles are found in the top articles filter - %1$d is replaced with the number of articles") + @objc public static let readingListCountFormat = WMFLocalizedString("reading-lists-count", value:"{{PLURAL:%1$d|%1$d reading list|%1$d reading lists}}", comment: "Describes the number of reading lists - %1$d is replaced with the number of reading lists") + + @objc public static let shortSavedTitle = WMFLocalizedString("action-saved", value: "Saved", comment: "Short title for the save button in the 'Saved' state - Indicates the article is saved. Please use the shortest translation possible. {{Identical|Saved}}") + @objc public static let accessibilitySavedTitle = WMFLocalizedString("action-saved-accessibility", value: "Saved. Activate to unsave.", comment: "Accessibility title for the 'Unsave' action {{Identical|Saved}}") + @objc public static let shortUnsaveTitle = WMFLocalizedString("action-unsave", value: "Unsave", comment: "Short title for the 'Unsave' action. Please use the shortest translation possible. {{Identical|Saved}}") + + @objc public static let accessibilityBackTitle = WMFLocalizedString("back-button-accessibility-label", value: "Back", comment: "Accessibility label for a button to navigate back. {{Identical|Back}}") + + @objc public static let accessibilitySavedNotification = WMFLocalizedString("action-saved-accessibility-notification", value: "Article saved for later", comment: "Notification spoken after user saves an article for later.") + @objc public static let accessibilityUnsavedNotification = WMFLocalizedString("action-unsaved-accessibility-notification", value: "Article unsaved", comment: "Notification spoken after user removes an article from Saved articles.") + + @objc public static func articleDeletedNotification(articleCount: Int) -> String { + return String.localizedStringWithFormat(WMFLocalizedString("article-deleted-accessibility-notification", value: "{{PLURAL:%1$d|article|articles}} deleted", comment: "Notification spoken after user deletes an article from the list. %1$d will be replaced with the number of deleted articles."), articleCount) + } + + @objc public static func unsaveArticleAndRemoveFromListsTitle(articleCount: Int) -> String { + return String.localizedStringWithFormat(WMFLocalizedString("saved-unsave-article-and-remove-from-reading-lists-title", value: "Unsave {{PLURAL:%1$d|article|articles}}?", comment: "Title of the alert action that unsaves a selected article and removes it from all associated reading lists. %1$d will be replaced with the number of articles to be unsaved."), articleCount) + } + @objc public static func unsaveArticleAndRemoveFromListsMessage(articleCount: Int) -> String { + return String.localizedStringWithFormat(WMFLocalizedString("saved-unsave-article-and-remove-from-reading-lists-message", value: "Unsaving {{PLURAL:%1$d|this article will remove it|these articles will remove them}} from all associated reading lists", comment: "Message of the alert action that unsaves a selected article and removes it from all associated reading lists. %1$d will be replaced with the number of articles being unsaved."), articleCount) + } + + @objc public static let shortSaveTitle = WMFLocalizedString("action-save", value: "Save", comment: "Title for the 'Save' action {{Identical|Save}}") + @objc public static let savedTitle:String = CommonStrings.savedTitle(languageCode: nil) + @objc public static let saveTitle:String = CommonStrings.saveTitle(languageCode: nil) + @objc public static let dimImagesTitle = WMFLocalizedString("dim-images", value: "Dim images", comment: "Label for image dimming setting") + + @objc public static let searchTitle = WMFLocalizedString("search-title", value: "Search", comment: "Title for search interface. {{Identical|Search}}") + @objc public static let settingsTitle = WMFLocalizedString("settings-title", value: "Settings", comment: "Title of the view where app settings are displayed. {{Identical|Settings}}") + @objc public static let placesTabTitle = WMFLocalizedString("places-title", value: "Places", comment: "Title of the Places screen shown on the places tab.") + @objc public static let historyTabTitle = WMFLocalizedString("history-title", value: "History", comment: "Title of the history screen shown on history tab {{Identical|History}}") + @objc public static let exploreTabTitle = WMFLocalizedString("home-title", value: "Explore", comment: "Title for home interface. {{Identical|Explore}}") + @objc public static let savedTabTitle = WMFLocalizedString("saved-title", value: "Saved", comment: "Title of the saved screen shown on the saved tab {{Identical|Saved}}") + + @objc public static let notificationsCenterTitle = WMFLocalizedString("notifications-center-title", value: "Notifications", comment: "Title for Notifications Center interface, as well as the accessibility label for the button that navigates to Notifications Center.") + @objc public static let notificationsCenterBadgeTitle = WMFLocalizedString("notifications-center-badge-button-accessibility-label", value: "Notifications with unread badge", comment: "Accessibility label for a button that navigates to Notifications Center. This button has a badge indicating there are unread notifications.") + public static let notificationsCenterMarkAsRead = WMFLocalizedString("notifications-center-mark-as-read", value: "Mark as Read", comment: "Button text in Notifications Center to mark a notification as read.") + public static let notificationsCenterMarkAsReadSwipe = WMFLocalizedString("notifications-center-swipe-mark-as-read", value: "Mark as read", comment: "Button text in Notifications Center swipe actions to mark a notification as read.") + public static let notificationsCenterMarkAsUnread = WMFLocalizedString("notifications-center-mark-as-unread", value: "Mark as Unread", comment: "Button text in Notifications Center to mark a notification as unread.") + public static let notificationsCenterMarkAsUnreadSwipe = WMFLocalizedString("notifications-center-swipe-mark-as-unread", value: "Mark as unread", comment: "Button text in Notifications Center swipe actions to mark a notification as unread.") + public static let notificationsCenterAllNotificationsStatus = WMFLocalizedString("notifications-center-status-all", value: "All", comment: "Text to indicate all notifications in Notifications Center.") + public static let notificationsCenterReadNotificationsStatus = WMFLocalizedString("notifications-center-status-read", value: "Read", comment: "Text to indicate a read notification in Notifications Center.") + public static let notificationsCenterUnreadNotificationsStatus = WMFLocalizedString("notifications-center-status-unread", value: "Unread", comment: "Text to indicate an unread notification in Notifications Center.") + public static let notificationsCenterAgentDescriptionFromFormat = WMFLocalizedString("notifications-center-agent-description-from-format", value: "From %1$@", comment: "Text indicating who triggered a notification in notifications center. %1$@ will be replaced with the origin agent of the notification, which could be a username.") + public static let notificationsCenterAlert = WMFLocalizedString("notifications-center-alert", value: "Alert", comment: "Description of various \"alert\" notification types, used on the notifications cell and detail views.") + public static let notificationsCenterNotice = WMFLocalizedString("notifications-center-type-item-description-notice", value: "Notice", comment: "Description of \"notice\" notification types, used on the notification cell and detail views.") + public static let notificationsChangePassword = WMFLocalizedString("notifications-center-change-password", value: "Change password", comment: "Button text in Notifications Center that routes user to change password screen.") + public static let notificationsCenterDestinationWeb = WMFLocalizedString("notifications-center-destination-web", value: "On web", comment: "Informational text next to each notification center action on the detail screen, informing the user that the action will take them to a web view or outside of the app.") + public static let notificationsCenterDestinationApp = WMFLocalizedString("notifications-center-destination-app", value: "In app", comment: "Informational text next to each notification center action on the detail screen, informing the user that the action will take them to a native view within the app.") + public static let notificationsCenterLoginSuccessDescription = WMFLocalizedString("notifications-center-subheader-login-success-unknown-device", value: "Login from an unfamiliar device", comment: "Subtitle text for 'Successful login from an unknown device' notifications in Notifications Center and filters.") + public static let notificationsCenterUserTalkPageMessage = WMFLocalizedString("notifications-center-type-title-user-talk-page-messsage", value: "Talk page message", comment: "Title of \"user talk page message\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterPageReviewed = WMFLocalizedString("notifications-center-type-title-page-review", value: "Page review", comment: "Title of \"page review\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterPageLinked = + WMFLocalizedString("notifications-center-type-title-page-link", value: "Page link", comment: "Title of \"page link\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterConnectionWithWikidata = WMFLocalizedString("notifications-center-type-title-connection-with-wikidata", value: "Connection with Wikidata", comment: "Title of \"connection with Wikidata\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterEmailFromOtherUser = WMFLocalizedString("notifications-center-type-title-email-from-other-user", value: "Email from other user", comment: "Title of \"email from other user\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterMentionInTalkPage = WMFLocalizedString("notifications-center-type-title-talk-page-mention", value: "Talk page mention", comment: "Title of \"talk page mention\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterMentionInEditSummary = WMFLocalizedString("notifications-center-type-title-edit-summary-mention", value: "Edit summary mention", comment: "Title of \"edit summary mention\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterSuccessfulMention = WMFLocalizedString("notifications-center-type-title-sent-mention-success", value: "Sent mention success", comment: "Title of \"sent mention success\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterFailedMention = WMFLocalizedString("notifications-center-type-title-sent-mention-failure", value: "Sent mention failure", comment: "Title of \"sent mention failure\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterUserRightsChange = WMFLocalizedString("notifications-center-type-title-user-rights-change", value: "User rights change", comment: "Title of \"user rights change\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterEditReverted = WMFLocalizedString("notifications-center-type-title-edit-reverted", value: "Edit reverted", comment: "Title of \"edit reverted\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterLoginAttempts = WMFLocalizedString("notifications-center-type-title-login-attempts", value: "Login attempts", comment: "Title of \"Login attempts\" notification type. Used on filters view toggles and the notification detail view. Represents failed logins from both a known and unknown device.") + public static let notificationsCenterLoginSuccess = WMFLocalizedString("notifications-center-type-title-login-success", value: "Login success", comment: "Title of \"login success\" notification type. Used on filters view toggles and the notification detail view. Represents successful logins from an unknown device.") + public static let notificationsCenterEditMilestone = WMFLocalizedString("notifications-center-type-title-edit-milestone", value: "Edit milestone", comment: "Title of \"edit milestone\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterTranslationMilestone = WMFLocalizedString("notifications-center-type-title-translation-milestone", value: "Translation milestone", comment: "Title of \"translation milestone\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterThanks = WMFLocalizedString("notifications-center-type-title-thanks", value: "Thanks", comment: "Title of \"thanks\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterWelcome = WMFLocalizedString("notifications-center-type-title-welcome", value: "Welcome", comment: "Title of \"welcome\" notification type. Used on filters view toggles and the notification detail view.") + public static let notificationsCenterOtherFilter = WMFLocalizedString("notifications-center-type-title-other", value: "Other", comment: "Title of \"other\" notifications filter. Used on filter toggles.") + + @objc public static let exploreFeedTitle = WMFLocalizedString("welcome-exploration-explore-feed-title", value:"Explore feed", comment:"Title for Explore feed") + @objc public static let featuredArticleTitle = WMFLocalizedString("explore-featured-article-heading", value: "Featured article", comment: "Text for 'Featured article' header") + @objc public static let onThisDayTitle = CommonStrings.onThisDayTitle() + @objc public static func onThisDayTitle(with languageCode: String? = nil) -> String { + WMFLocalizedString("on-this-day-title", languageCode: languageCode, value: "On this day", comment: "Title for the 'On this day' feed section") + } + @objc public static let topReadTitle = WMFLocalizedString("places-filter-top-articles", value:"Top read", comment: "Title of places search filter that searches top articles") + @objc public static let pictureOfTheDayTitle = WMFLocalizedString("explore-potd-heading", value: "Picture of the day", comment: "Text for 'Picture of the day' header") + @objc public static let randomizerTitle = WMFLocalizedString("explore-randomizer", value: "Randomizer", comment: "Displayed on a button that loads another random article - it's a 'Randomizer'") + @objc public static let languagesTitle = WMFLocalizedString("languages-settings-title", value: "Languages", comment: "Title for the 'Languages' section in Settings") + @objc public static let relatedPagesTitle = WMFLocalizedString("explore-because-you-read", value: "Because you read", comment: "Text for 'Because you read' header") + @objc public static let continueReadingTitle = WMFLocalizedString("explore-continue-reading-heading", value: "Continue reading", comment: "Text for 'Continue Reading' header") + + @objc public static let hideCardTitle = WMFLocalizedString("explore-hide-card-prompt", value: "Hide this card", comment: "Title of button shown for users to confirm the hiding of a suggestion in the explore feed") + + @objc static public func savedTitle(languageCode: String?) -> String { + return WMFLocalizedString("button-saved-for-later", languageCode: languageCode, value: "Saved for later", comment: "Longer button text for already saved button used in various places.") + } + + @objc static public func saveTitle(languageCode: String?) -> String { + return WMFLocalizedString("button-save-for-later", languageCode: languageCode, value: "Save for later", comment: "Longer button text for save button used in various places.") + } + + @objc public static let shortShareTitle = WMFLocalizedString("action-share", value: "Share", comment: "Short title for the 'Share' action. Please use the shortest translation possible. {{Identical|Share}}") + @objc public static let accessibilityShareTitle = WMFLocalizedString("action-share-accessibility", value: "Share", comment: "Accessibility title for the 'Share' action") + + @objc public static let accessibilityLanguagesTitle = WMFLocalizedString("action-language-accessibility", value: "Change language", comment: "Accessibility title for the 'Language' toolbar button on articles and talk pages.") + + @objc public static let shortReadTitle = WMFLocalizedString("action-read", value: "Read", comment: "Title for the 'Read' action\n{{Identical|Read}}") + + @objc public static let dismissButtonTitle = WMFLocalizedString("announcements-dismiss", value: "Dismiss", comment: "Button text indicating a user wants to dismiss an announcement {{Identical|No thanks}}") + + @objc public static let textSizeSliderAccessibilityLabel = WMFLocalizedString("reading-themes-controls-accessibility-text-size-slider", value: "Text size slider", comment: "Accessibility label for the text size slider that adjusts article text size.") + + @objc public static let deleteActionTitle = WMFLocalizedString("article-delete", value: "Delete", comment: "Title of the action that deletes the selected articles article.") + + @objc public static let removeActionTitle = WMFLocalizedString("action-remove", value: "Remove", comment: "Title of the action that removes the selection from the current context.") + + @objc public static let createNewListTitle = WMFLocalizedString("reading-list-create-new-list-title", value: "Create a new list", comment: "Title for the view in charge of creating a new reading list.") + @objc public static let moveToReadingListActionTitle = WMFLocalizedString("action-move-to-reading-list", value: "Move to reading list", comment: "Title of the action that moves the selected articles to another reading list") + @objc public static let addToReadingListActionTitle = WMFLocalizedString("action-add-to-reading-list", value: "Add to reading list", comment: "Title of the action that adds selected articles to a reading list") + @objc public static let addToReadingListShortActionTitle = WMFLocalizedString("action-add-to-reading-list-short", value: "Add to list", comment: "Shorter title for the action that adds selected articles to a reading list") + + @objc public static let moveToActionTitle = WMFLocalizedString("action-move-to", value: "Move to…", comment: "Title of the action that moves the selection elsewhere.") + + @objc public static let addToActionTitle = WMFLocalizedString("action-add-to", value: "Add to…", comment: "Title of the action that adds the selection to something else.") + + @objc public static let shareActionTitle = WMFLocalizedString("article-share", value: "Share", comment: "Text of the article list row action shown on swipe which allows the user to choose the sharing option") + public static let shareMenuTitle = WMFLocalizedString("share-menu-item", value: "Share…", comment:"'Share…' menu item with ellipsis to indicate further actions are required.") + + @objc public static let updateActionTitle = WMFLocalizedString("action-update", value: "Update", comment: "Title of the update action.") + @objc public static let cancelActionTitle = WMFLocalizedString("action-cancel", value: "Cancel", comment: "Title of the cancel action.") + @objc public static let retryActionTitle = WMFLocalizedString("action-retry", value: "Retry", comment: "Title of the retry action.") + @objc public static let discardEditsActionTitle = WMFLocalizedString("action-discard-edits", value: "Discard edits", comment: "Title of the discard edits action.") + + @objc public static let sortActionTitle = WMFLocalizedString("action-sort", value: "Sort", comment: "Title of the sort action.") + + @objc public static let sortAlertTitle = WMFLocalizedString("reading-lists-sort-saved-articles", value: "Sort saved articles", comment: "Title of the alert that allows sorting saved articles.") + + @objc public static let nextTitle = WMFLocalizedString("button-next", value: "Next", comment: "Button text for next button used in various places. {{Identical|Next}}") + @objc public static let skipTitle = WMFLocalizedString("button-skip", value: "Skip", comment: "Button text for skip button used in various places.") + @objc public static let okTitle = WMFLocalizedString("button-ok", value: "OK", comment: "Button text for ok button used in various places {{Identical|OK}}") + @objc public static let doneTitle = WMFLocalizedString("description-published-button-title", value: "Done", comment: "Title for description panel done button.") + public static let goBackTitle = WMFLocalizedString("button-go-back", value: "Go back", comment: "Button text for Go back button used in various places") + public static let publishAnywayTitle = WMFLocalizedString("button-publish-anyway", value: "Publish anyway", comment: "Button text for publish button used when first warned against publishing.") + + @objc public static let editNotices = WMFLocalizedString("edit-notices", value: "Edit notices", comment: "Title text and accessibility label for edit notices button.") + @objc public static let undo = WMFLocalizedString("action-undo", value: "Undo", comment: "Title text and accessibility label for undo action on buttons or info sheets.") + @objc public static let redo = WMFLocalizedString("action-redo", value: "Redo", comment: "Title text and accessibility label for redo action on buttons or info sheets.") + @objc public static let findInPage = WMFLocalizedString("action-find-in-page", value: "Find in page", comment: "Title text and accessibility label for find in page action on buttons or info sheets.") + @objc public static let readingThemesControls = WMFLocalizedString("article-toolbar-reading-themes-controls-toolbar-item", value: "Reading Themes Controls", comment: "Accessibility label for the Reading Themes Controls article toolbar item") + + public static let welcomePromiseTitle = WMFLocalizedString("description-welcome-promise-title", value:"By starting, I promise not to misuse this feature", comment:"Title text asking user to edit descriptions responsibly") + @objc public static let gotItButtonTitle = WMFLocalizedString("welcome-explore-tell-me-more-done-button", value: "Got it", comment:"Text for button dismissing detailed explanation of new features") + public static let getStartedTitle = WMFLocalizedString("welcome-explore-continue-button", value:"Get started", comment:"Text for button for dismissing welcome screens {{Identical|Get started}}") + + @objc public static let privacyPolicyURLString = "https://foundation.m.wikimedia.org/wiki/Privacy_policy" + + @objc public static let account = WMFLocalizedString("settings-account", value: "Account", comment: "Title for button and page letting user view their account page.") + + @objc public static let myLanguages = WMFLocalizedString("settings-my-languages", value: "My languages", comment: "Title for list of user's preferred languages") + @objc public static let readingPreferences = WMFLocalizedString("settings-appearance", value: "Reading preferences", comment: "Title of the reading preferences screen.") + @objc public static let pushNotifications = WMFLocalizedString("settings-notifications", value: "Push notifications", comment: "Title for view and button letting users change their push notifications settings.") + + public static let tryAgain = WMFLocalizedString("settings-notifications-echo-failure-try-again", value: "Try again", comment: "Text alerting the user to try action again after error") + + @objc public static let settingsStorageAndSyncing = WMFLocalizedString("settings-storage-and-syncing-title", value: "Article storage and syncing", comment: "Title of the saved articles storage and syncing settings screen") + + @objc public static let inTheNewsTitle = WMFLocalizedString("in-the-news-title", value:"In the news", comment:"Title for the 'In the news' notification & feed section") + + @objc public static let wikipediaLanguages = WMFLocalizedString("languages-wikipedia", value: "Wikipedia languages", comment: "Title for list of Wikipedia languages") + + @objc public static let unknownError = WMFLocalizedString("error-unknown", value: "An unknown error occurred", comment: "Message displayed when an unknown error occurred") + + @objc public static let readingListsDefaultListTitle = WMFLocalizedString("reading-lists-default-list-title", value: "Saved", comment: "The title of the default saved pages list {{Identical|Saved}}") + + @objc public static let localizedEnableLocationTitle = WMFLocalizedString("places-enable-location-title", value:"Explore articles near your location by enabling Location Access", comment:"Explains that you can explore articles near you by enabling location access. \"Location\" should be the same term, which is used in the device settings, under \"Privacy\".") + @objc public static let localizedEnableLocationExploreTitle = WMFLocalizedString("explore-enable-location-title", value:"Explore articles near your current location", comment:"Explains that you can explore articles near your current location. \"Location\" should be the same term, which is used in the device settings, under \"Privacy\".") + @objc public static let localizedEnableLocationDescription = WMFLocalizedString("places-enable-location-description", value:"Access to your location is available only when the app or one of its features is visible on your screen.", comment:"Describes that access to your location is only used when the app or one of its features is on the screen") + @objc public static let localizedEnableLocationButtonTitle = WMFLocalizedString("places-enable-location-action-button-title", value:"Enable location", comment:"Button title to enable location access") + @objc public static let nearbyFooterTitle = WMFLocalizedString("home-nearby-footer", value: "More places near your location", comment: "Footer for presenting user option to see longer list of nearby articles.") + + @objc public static let readingListLoginSubtitle = WMFLocalizedString("reading-list-login-subtitle", value:"Log in or create an account to allow your saved articles and reading lists to be synced across devices and saved to your user preferences.", comment:"Subtitle explaining that saved articles and reading lists can be synced across Wikipedia apps.") + @objc public static let readingListLoginButtonTitle = WMFLocalizedString("reading-list-login-button-title", value:"Log in to sync your saved articles", comment:"Title for button to login to sync saved articles and reading lists.") + + @objc public static let readingListDoNotKeepSubtitle = WMFLocalizedString("reading-list-do-not-keep-button-title", value:"No, delete articles from device", comment:"Title for button to remove saved articles from device.") + + @objc public static let readingListsDefaultListDescription = WMFLocalizedString("reading-lists-default-list-description", value: "Default list for your saved articles", comment: "The description of the default saved pages list.") + + @objc public static let readingListsEntryLimitReachedFormat = WMFLocalizedString("reading-list-entry-limit-reached", value: "{{PLURAL:%1$d|Article|Articles}} cannot be added to this list. You have reached the limit of %2$d articles per reading list for %3$@", comment: "Informs the user that adding the selected articles to their reading list would put them over the limit. %1$d will be replaced with the number of articles the user is trying to add. %2$d will be replaced with the maximum number of articles allowed per list. %3$@ will be replaced with the name of the list.") + @objc public static let readingListsListLimitReachedFormat = WMFLocalizedString("reading-list-list-limit-reached", value: "You have reached the limit of %1$d reading lists per account", comment: "Informs the user that they have reached the allowed limit of reading lists per account. %1$d will be replaced with the maximum number of allowed reading lists") + @objc public static let eraseAllSavedArticles = WMFLocalizedString("settings-storage-and-syncing-erase-saved-articles-title", value: "Erase saved articles", comment: "Title of the settings option that enables erasing saved articles") + + @objc public static let keepSavedArticlesOnDeviceMessage = WMFLocalizedString("reading-list-keep-subtitle", value: "There are articles synced to your Wikipedia account. Would you like to keep them on this device after you log out?", comment: "Subtitle asking if synced articles should be kept on device after logout.") + + @objc public static let closeButtonAccessibilityLabel = WMFLocalizedString("close-button-accessibility-label", value: "Close", comment: "Accessibility label for a button that closes a dialog. {{Identical|Close}}") + + @objc public static let onTitle = WMFLocalizedString("explore-feed-preferences-feed-card-visibility-global-cards-on", value: "On", comment: "Text for Explore feed card setting indicating that the global feed card is active") + @objc public static let onAllTitle = WMFLocalizedString("explore-feed-preferences-feed-card-visibility-all-languages-on", value: "On all", comment: "Text for Explore feed card setting indicating that the feed card is active in all preferred languages") + @objc public static let offTitle = WMFLocalizedString("explore-feed-preferences-feed-card-visibility-all-languages-off", value: "Off", comment: "Text for Explore feed card setting indicating that the feed card is hidden in all preferred languages") + @objc public static func onTitle(_ count: Int) -> String { + return String.localizedStringWithFormat(WMFLocalizedString("explore-feed-preferences-feed-card-visibility-languages-count", value:"On %1$d", comment: "Text for Explore feed card setting indicating the number of languages it's visible in - %1$d is replaced with the number of languages"), count) + } + + @objc public static let turnOnExploreTabTitle = WMFLocalizedString("explore-feed-preferences-turn-on-explore-tab-title", value: "Turn on the Explore tab?", comment: "Title for alert that allows users to turn on the Explore tab") + @objc public static let turnOnExploreActionTitle = WMFLocalizedString("explore-feed-preferences-turn-on-explore-tab-action-title", value: "Turn on Explore", comment: "Title for action that allows users to turn on the Explore tab") + @objc public static let customizeExploreFeedTitle = WMFLocalizedString("explore-feed-preferences-customize-explore-feed-action-title", value: "Customize Explore feed", comment: "Title for action that allows users to go to the Explore feed settings screen") + + @objc public static let revertedEditTitle = WMFLocalizedString("reverted-edit-title", value: "Reverted edit", comment: "Title for notification informing user that their edit was reverted.") + + @objc public static let noInternetConnection = WMFLocalizedString("no-internet-connection", value: "No internet connection", comment: "String used in various places to indicate no internet connection") + + @objc public static let noEmailClient = WMFLocalizedString("no-email-account-alert", value: "Please setup an email account on your device and try again.", comment: "Displayed to the user when they try to send a feedback email, but they have never set up an account on their device") + + @objc public static let vanishAccount = WMFLocalizedString("account-request-vanishing", value: "Vanish account", comment: "This will initiate the process of requesting your account to be vanished ") + @objc public static var usernameFieldTitle = WMFLocalizedString("vanish-account-username-field", value: "Username and user page", comment: "Title for the username and userpage form field") + @objc public static let learnMoreButtonText = WMFLocalizedString("vanish-account-learn-more-text", value: "Learn more", comment: "Text for button on vanish account request screen that redirects to the meta page about the process") + + // REMINDER: do not delete the app store strings below. We're not using them anywhere within the app itself but we need them to remain so they get upstreamed into TWN. ("localizations.swift copies the non-EN translations of these strings into respective Fastlane "Localized Metadata" files. See: https://docs.fastlane.tools/actions/deliver/) + @objc public static let appStoreSubtitle = WMFLocalizedString("app-store-subtitle", value: "The free encyclopedia", comment: "Subtitle describing the app for the app store") + @objc public static let appStoreShortDescription = WMFLocalizedString("app-store-short-description", value: "Download the Wikipedia app to explore places near you, sync articles to read offline and customize your reading experience.", comment: "Short description of the app for the app store") + @objc public static let appStoreReleaseNotes = WMFLocalizedString("app-store-release-notes", value: "Fully customizable and easier to read Explore feed. Localization, performance improvements and bug fixes.", comment: "Short summary of what is new in this version of the app for the app store") + @objc public static let appStoreKeywords = WMFLocalizedString("app-store-keywords", value: "Wikipedia, reference, wiki, encyclopedia, info, knowledge, research, information, explore, learn", comment: "Short list of keywords describing the app for the app store. It is required that these are individual words, not phrases, and are comma separated.") + + @objc public static let editAttribution = WMFLocalizedString("wikitext-upload-save-anonymously-warning", value: "Edits will be attributed to the IP address of your device. If you %1$@ you will have more privacy.", comment: "Button sub-text informing user or draw-backs of not signing in before saving wikitext. Parameters:\n* %1$@ - sign in button text") + + @objc public static let editSignIn = WMFLocalizedString("wikitext-upload-save-sign-in", value: "Log in", comment: "{{Identical|Log in}}") + + public static let genericErrorDescription = WMFLocalizedString("fetcher-error-generic", value: "Something went wrong. Please try again later.", comment: "Error shown to the user for generic errors with no clear recovery steps for the user.") + + public static let insertMediaTitle = WMFLocalizedString("insert-media-title", value: "Insert media", comment: "Title for the view in charge of inserting media into an article") + + public static let publishTitle = WMFLocalizedString("button-publish", value: "Publish", comment: "Button text for publish button used in various places. Please prioritize for de, ar and zh wikis. {{Identical|Publish}}") + public static let logoutTitle = WMFLocalizedString("main-menu-account-logout", value: "Log out", comment: "Button text for logging out.") + + public static let insertLinkTitle = WMFLocalizedString("insert-link-title", value: "Insert link", comment: "Title for the Insert link screen") + public static let editLinkTitle = WMFLocalizedString("edit-link-title", value: "Edit link", comment: "Title for the Edit link screen") + + public static let readStatusAccessibilityLabel = WMFLocalizedString("talk-page-discussion-read-accessibility-label", value: "Read", comment: "Accessibility text for indicating that some content have been read.") + + public static let unreadStatusAccessibilityLabel = WMFLocalizedString("talk-page-discussion-unread-accessibility-label", value: "Unread", comment: "Accessibility text for indicating that some content have not been read.") + + public static let talkPageNewBannerTitle = WMFLocalizedString("talk-page-new-banner-title", value: "Please be kind", comment: "Title text on banner that appears once user posts a new reply or discussion topic on their talk page.") + + public static let talkPageNewBannerSubtitle = WMFLocalizedString("talk-page-new-banner-subtitle", value: "Remember, we are all humans here", comment: "Subtitle text on banner that appears once user posts a new reply or discussion topic on their talk page.") + + public static func talkPageTitleUserTalk(languageCode: String?) -> String { + WMFLocalizedString("talk-page-title-user-talk", languageCode: languageCode, value: "User Talk", comment: "This title label is displayed at the top of a talk page topic list, if the talk page type is a user talk page. Please prioritize for de, ar and zh wikis.") + } + + public static func talkPageTitleArticleTalk(languageCode: String?) -> String { + WMFLocalizedString("talk-page-title-article-talk", languageCode: languageCode, value: "Article Talk", comment: "This title label is displayed at the top of a talk page topic list, if the talk page type is an article talk page. Please prioritize for de, ar and zh wikis.") + } + + public static let accessibilityClearTitle = WMFLocalizedString("clear-title-accessibility-label", value: "Clear", comment: "Accessibility label title for action that clears text") + + public static let successfullyPublishedDiscussion = WMFLocalizedString("talk-page-new-topic-success-text", value: "Your discussion was successfully published", comment: "Banner text that appears after a new discussion was successfully published on a talk page.") + + public static let successfullyPublishedReply = WMFLocalizedString("talk-page-new-reply-success-text", value: "Your reply was successfully published", comment: "Banner text that appears after a new reply was successfully published on a talk page discussion.") + + public static func talkPageReply(languageCode: String?) -> String { + WMFLocalizedString("talk-page-reply-button", languageCode: languageCode, value: "Reply", comment: "Text used on button to reply to talk page messages. Please prioritize for de, ar and zh wikis.") + } + @objc public static let talkPageReplyAccessibilityText = WMFLocalizedString("talk-page-reply-button-accessibility-label", value: "Reply to %@", comment: "Accessibility text for reply button. The %@ will be replaced with the name of the user whose comment is being responded") + + public static let revisionHistory = WMFLocalizedString("talk-page-revision-history", value: "Revision history", comment: "Title for option that leads to talk pages revision history. Please prioritize for de, ar and zh wikis.") + + public static let defaultThemeDisplayName = WMFLocalizedString("theme-default-display-name", value: "Default", comment: "Default theme name presented to the user") + + public static let diffSingleLineFormat = WMFLocalizedString("diff-single-line-format", value:"Line %1$d", comment:"Label in diff to indicate how many lines a change section encompases. This format is for a single change line. %1$d is replaced by the change line number.") + + public static let diffMultiLineFormat = WMFLocalizedString("diff-multi-line-format", value:"Lines %1$d - %2$d", comment:"Label in diff to indicate how many lines a change section encompases. This format is for multiple change lines. %1$d is replaced by the starting line number and %2$d is replaced by the ending line number.") + + public static let compareTitle = WMFLocalizedString("page-history-compare-title", value: "Compare", comment: "Title for action button that allows users to contrast different items") + public static let maxRevisionsSelectedWarningTitle = WMFLocalizedString("page-history-revisions-comparison-warning", value: "Only two revisions can be selected", comment: "Text telling the user how many revisions can be selected for comparison") + + public static let loginOrCreateAccountTitle = WMFLocalizedString("reading-list-login-or-create-account-button-title", value:"Log in or create account", comment:"Title for button to login or create account.") + + @objc public static let diffErrorTitle = WMFLocalizedString("diff-revision-error-title", value: "Unable to load revision", comment: "Text for placeholder label visible when there has been an error while fetching the diff.") + + @objc public static let minorEditTitle = WMFLocalizedString("page-history-revision-minor-edit-accessibility-label", value: "Minor edit", comment: "Accessibility label text used if edit was minor") + + @objc public static let authorTitle = WMFLocalizedString("page-history-revision-author-accessibility-label", value: "Author: %@", comment: "Accessibility label text telling the user who authored a revision. %@ is replaced with the author.") + + @objc public static let unknownTitle = WMFLocalizedString("unknown-generic-text", value: "Unknown", comment: "Default text used in places where no contextual information is provided") + + public static func aboutThisArticleTitle(with languageCode: String) -> String { + return WMFLocalizedString("article-about-title", languageCode: languageCode, value: "About this article", comment: "The text that is displayed before the 'about' section at the bottom of an article") + } + public static func readMoreTitle(with languageCode: String) -> String { + return WMFLocalizedString("article-read-more-title", languageCode: languageCode, value: "Read more", comment: "The text that is displayed before the read more section at the bottom of an article {{Identical|Read more}}") + } + + public static let revisionMadeFormat = WMFLocalizedString("page-history-revision-time-accessibility-label", value: "Revision made %@", comment: "Label text telling the user what time revision was made - %@ is replaced with the time") + + public static let compareRevisionsTitle = WMFLocalizedString("diff-compare-header-heading", value: "Compare Revisions", comment: "Heading label in header when comparing two revisions.") + + // Article As A Living Doucment Strings - for some reason build script doesn't auto generate these when used directly in SignificantEventsViewModels.swift + + public static let viewFullHistoryText = WMFLocalizedString("aaald-view-full-history-button", value: "View full article history", comment: "Text displayed in a button for pushing to the full article history view on the article as a living document screen.") + + static let smallChangeDescription = WMFLocalizedString("aaald-small-change-description", + value:"{{PLURAL:%1$d|0=No small changes made|%1$d small change made|%1$d small changes made}}", + comment:"Describes how many small changes are batched together in the article as a living document timeline view. %1$d is replaced with the number of small changes.") + + static let newTalkTopicDescriptionFormat = WMFLocalizedString("aaald-new-talk-topic-description-format", value: "%1$@ about this article", comment: "Title displayed in an article as a living document timeline cell and content insert explaining that a new article talk page topic has been posted. %1$@ is replaced by `New discussion` text.") + static let newTalkTopicDiscussion = WMFLocalizedString("aaald-new-discussion", value: "New discussion", comment: "Portion of title displayed in article as a living document timeline cell and content insert explaining that a new article talk page topic has been posted.") + + static let vandalismRevertDescription = WMFLocalizedString("aaald-vandalism-revert-description", value: "Suspected Vandalism reverted", comment: "Title displayed in an article as a living document timeline cell explaining that a vandalism revision was reverted.") + + static let multipleChangesMadeDescription = WMFLocalizedString("aaald-multiple-changes-description", value: "Multiple changes made", comment: "Title displayed in article as a living document content insert explaining that multiple changes were made in a revision.") + + static let addedTextDescription = WMFLocalizedString("aaald-added-text-description-2", value:"%1$@ added", comment:"Title displayed in an article as a living document cell explaining that a revision has a certain number of characters added. %1$@ is replaced by a formatted string representing characters added.") + + static let deletedTextDescription = WMFLocalizedString("aaald-deleted-text-description-2", value:"%1$@ deleted", comment:"Title displayed in an article as a living document cell explaining that a revision has a certain number of characters deleted. %1$@ is replaced by a formatted string representing characters deleted.") + + static let charactersTextDescription = WMFLocalizedString("aaald-characters-text-description", value:"{{PLURAL:%1$d|0=characters|character|characters}}", + comment:"Displayed in an article as a living document cell explaining that a revision has a certain number of characters added or deleted. %1$d is the number of characters added or deleted.") + + static let articleDescriptionUpdatedDescription = WMFLocalizedString("aaald-article-description-updated-description", value:"Article description updated", + comment:"Title displayed in an article as a living document cell explaining that an article's description was updated in a revision.") + + static let singleReferenceAddedDescription = WMFLocalizedString("aaald-single-reference-added-description", value:"Reference added", + comment:"Title displayed in an article as a living document timeline cell when a reference was added (and no other changes) to a revision.") + + static let multipleReferencesAddedDescription = WMFLocalizedString("aaald-multiple-references-added-description", value:"Multiple references added", + comment:"Title displayed in an article as a living document cell when multiple references were added (and no other changes) to a revision.") + + static let numericalMultipleReferencesAddedDescription = WMFLocalizedString("aaald-numerical-multiple-references-added-description", value:"{{PLURAL:%1$d|0=0 references|%1$d reference|%1$d references}} added", + comment:"Title displayed in an article as a living document cell explaining that multiple references were added to a revision. This string is used alongside other changes types like added characters. %1$d is replaced with the number of references.") + + static let oneSectionDescription = WMFLocalizedString("aaald-one-section-description", value: "in the %1$@ section", comment: "Text explaining what section an article as a living document event change occurred in, if occurred in only one section. %1$@ is replaced with the section name.") + + static let twoSectionsDescription = WMFLocalizedString("aaald-two-sections-description", value: "in the %1$@ and %2$@ sections", comment: "Text explaining what sections an article as a living document event change occurred in, if occurred in two sections. %1$@ is replaced with the first section name, %2$@ with the second.") + + static let manySectionsDescription = WMFLocalizedString("aaald-many-sections-description", value: "in %1$d sections", comment: "Text explaining what sections an article as a living document change occurred in, if occurred in 3+ sections. %1$d is replaced with the number of sections.") + + static let newBookReferenceTitle = WMFLocalizedString("aaald-new-book-reference-title", + value:"Book", comment: "Header text for a new book reference type that was added in an article as a living document cell.") + + static let newJournalReferenceTitle = WMFLocalizedString("aaald-new-journal-reference-title", + value:"Journal", comment: "Header text for a new journal reference type that was added in an article as a living document cell.") + + static let newNewsReferenceTitle = WMFLocalizedString("aaald-new-news-reference-title", + value:"News", comment: "Header text for a new news reference type that was added in an article as a living document cell.") + + static let newWebsiteReferenceTitle = WMFLocalizedString("aaald-new-website-reference-title", + value:"Website", comment: "Header text for a new website reference type that was added in an article as an living document cell.") + + static let newJournalReferenceVolume = WMFLocalizedString("aaald-new-journal-reference-volume", + value:"Volume %1$@:", comment: "Volume text for a new journal reference type that was added in an article as a living document cell. %1$@ is replaced by the journal volume number of the reference.") + + static let newJournalReferenceDatabase = WMFLocalizedString("aaald-new-journal-reference-database", + value:"via %1$@", comment: "Database text for a new journal reference type that was added in an article as a living document cell. %1$@ is replaced by the database volume number of the reference.") + + static let newWebsiteReferenceArchiveUrlText = WMFLocalizedString("aaald-new-website-reference-archive-url-text", + value:"Archive.org URL", comment: "Archive.org URL text for a new website reference type that was added in an article as a living document cell. This will be turned into a link that goes to the reference's Archive.org URL.") + + static let newWebsiteReferenceArchiveDateText = WMFLocalizedString("aaald-new-website-reference-archive-date-text", + value:"from the original on %1$@", comment: "Text in a new website reference in an article as a living document cell that describes when the reference was retrieved for Archive.org. %1$@ is replaced with the reference's archive date.") + + static let newNewsReferenceRetrievedDate = WMFLocalizedString("aaald-new-news-reference-retrieved-date", + value:"Retrieved %1$@", comment: "Retrieved date text for a new news reference type that was added in an article as a living document cell. %1$@ is replaced by the reference's retrieved date.") + + // tonitodo: this fails with EXC_BADACCESS when I try to use plural edits + static let revisionUserInfo = WMFLocalizedString( + "aaald-revision-userInfo", + value:"Edit by %1$@ (%2$@ edits)", comment: "Text describing details about the user that made a significant revision in the article as a living document view. %1$@ is replaced by the editor name and %2$d is replaced by the number of edits they have made.") + + static let revisionUserInfoAnonymous = WMFLocalizedString("aaald-revision-by-anonymous", + value:"Edit by anonymous user", comment: "Text describing the anonymous user that made a significant revision in the article as a living document view.") + + static let articleAsLivingDocSummaryTitle = WMFLocalizedString( + "aaald-summary-title", + value:"{{PLURAL:%1$d|0=0 changes|%1$d change|%1$d changes}} by {{PLURAL:%2$d|0=0 editors|%2$d editor|%2$d editors}} in {{PLURAL:%3$d|0=0 days|%3$d day|%3$d days}}", + comment:"Describes how many small changes are batched together in the article as a living document timeline view. %1$d is replaced by the number of accumulated changes editors made, %2$d is replaced by the number of editors that made that change and %3$d is replaced with relative timeframe date that the edit counting started (e.g. 10 days).") + + @objc public static func onThisDayAdditionalEventsMessage(with languageCode: String?, locale: Locale, eventsCount: Int) -> String { + return String(format: WMFLocalizedString("on-this-day-detail-header-title", languageCode: languageCode, value:"{{PLURAL:%1$d|%1$d historical event|%1$d historical events}}", comment:"Title for 'On this day' detail view - %1$d is replaced with the number of historical events which occurred on the given day"), locale: locale, eventsCount) + } + @objc public static func onThisDayHeaderDateRangeMessage(with languageCode: String?, locale: Locale, lastEvent: String, firstEvent: String) -> String { + return String(format: WMFLocalizedString("on-this-day-detail-header-date-range", languageCode: languageCode, value:"from %1$@ - %2$@", comment:"Text for 'On this day' detail view events 'year range' label - %1$@ is replaced with string version of the oldest event year - i.e. '300 BC', %2$@ is replaced with string version of the most recent event year - i.e. '2006', "), locale: locale, lastEvent, firstEvent) + } + public static func onThisDayFooterWith(with eventCount: Int, languageCode: String? = nil, locale: Locale = Locale.autoupdatingCurrent) -> String { + return String(format: WMFLocalizedString("on-this-day-footer-showing-event-count", languageCode: languageCode, value: "{{PLURAL:%1$d|%1$d more historical event|%1$d more historical events}} on this day", comment: "Footer for presenting user option to see longer list of 'On this day' articles. %1$@ will be substituted with the number of events"), locale: locale, eventCount) + } + public static let articleAsLivingDocErrorTitle = WMFLocalizedString("aaald-error-title", value: "Unable to load inline article history", comment: "Title of error banner that appears at the bottom of an article when significant events fail to load.") + + public static let articleAsLivingDocErrorSubtitle = WMFLocalizedString("aaald-error-subitle", value: "Refresh to try again", comment: "Subtitle of error banner that appears at the bottom of an article when significant events fail to load.") + + public static let editorExitConfirmationTitle = WMFLocalizedString("editor-exit-confirmation-title", value: "Dismiss the editing mode?", comment: "Title text of editing mode confirmation alert. Presented to the user when they they are about to be navigated away from the editor flow.") + public static let editorExitConfirmationBody = WMFLocalizedString("editor-exit-confirmation-body", value: "Are you sure you want to leave editing mode without publishing first?", comment: "Body text of editing mode confirmation alert. Presented to the user when they they are about to be navigated away from the editor flow.") + + public static let talkPageCloseConfirmationKeepEditing = WMFLocalizedString("talk-pages-compose-close-confirmation-keep", value: "Keep Editing", comment: "Title of keep editing action, displayed within a confirmation alert to user when they attempt to close the new topic view or new reply after entering text. Please prioritize for de, ar and zh wikis.") +} + +// Language variant strings +public extension CommonStrings { + + // General + + static let variantsAlertPreferencesButton = WMFLocalizedString("variants-alert-preferences-button", value: "Review your preferences", comment: "Action button on alert used to inform users about variant support.") + + static let variantsAlertDismissButton = WMFLocalizedString("variants-alert-dismiss-button", value: "No thanks", comment: "Dismiss button on alert used to inform users about variant support.") + + // Chinese (zh) + + static let chineseVariantsAlertTitle = WMFLocalizedString("chinese-variants-alert-title", value: "Updates to Chinese variant support", comment: "Title of alert used to inform users about Chinese variant support.") + + static let chineseVariantsAlertBody = WMFLocalizedString("chinese-variants-alert-body", value: "The Wikipedia app now supports the following Chinese variants as primary or secondary languages within the app, making it easier to read, search and edit in your preferred variants:\n\n简体 Chinese, Simplified (zh-hans)\n香港繁體 Hong Kong Traditional (zh-hk)\n澳門繁體 Macau Traditional (zh-mo)\n大马简体 Malaysia Simplified (zh-my)\n新加坡简体 Singapore Simplified (zh-sg)\n臺灣正體 Taiwanese Traditional (zh-tw)", comment: "Body text of alert used to inform users about Chinese variant support. Please do not translate the newlines (\n) or Chinese characters (简体, 繁體, etc.).") + + // Crimean Tatar (crh) + + static let crimeanTatarVariantsAlertTitle = WMFLocalizedString("crimean-tatar-variants-alert-title", value: "Updates to Crimean Tatar variant support", comment: "Title of alert used to inform users about Crimean Tatar variant support.") + + static let crimeanTatarVariantsAlertBody = WMFLocalizedString("crimean-tatar-variants-alert-body", value: "The Wikipedia app now supports the following Crimean Tatar variants as primary or secondary languages within the app, making it easier to read, search and edit in your preferred variants:\n\nQırımtatarca, Latin Crimean Tatar Latin (chr-latn)\nкъырымтатарджа, Кирил Crimean Tatar Cyrillic (crh-cyrl)", comment: "Body text of alert used to inform users about Crimean Tatar variant support. Please do not translate the newlines (\n) or Crimean Tatar characters (къырымтатарджа, etc.).") + + // Gan (gan) + + static let ganVariantsAlertTitle = WMFLocalizedString("gan-variants-alert-title", value: "Updates to Gan variant support", comment: "Title of alert used to inform users about Gan variant support.") + + static let ganVariantsAlertBody = WMFLocalizedString("gan-variants-alert-body", value: "The Wikipedia app now supports the following Gan variants as primary or secondary languages within the app, making it easier to read, search and edit in your preferred variants:\n\n贛語 原文 Gan (gan)\n赣语 简体 Gan, Simplified (gan-hans)\n贛語 繁體 Gan, Traditional (gan-hant)", comment: "Body text of alert used to inform users about Gan variant support. Please do not translate the newlines (\n) or Gan characters (贛語 原文, etc.).") + + // Inuktitut (iu) + + static let inuktitutVariantsAlertTitle = WMFLocalizedString("inuktitut-variants-alert-title", value: "Updates to Inuktitut variant support", comment: "Title of alert used to inform users about Inuktitut variant support.") + + static let inuktitutVariantsAlertBody = WMFLocalizedString("inuktitut-variants-alert-body", value: "The Wikipedia app now supports the following Inuktitut variants as primary or secondary languages within the app, making it easier to read, search and edit in your preferred variants:\n\nᐃᓄᒃᑎᑐᑦ ᑎᑎᕋᐅᓯᖅ ᓄᑖᖅ Inuktitut, Syllabics (ike-cans)\nInuktitut ilisautik, Inuktitut, Latin (ike-latn)", comment: "Body text of alert used to inform users about Inuktitut variant support. Please do not translate the newlines (\n) or Inuktitut characters (ᐃᓄᒃᑎᑐᑦ ᑎᑎᕋᐅᓯᖅ ᓄᑖᖅ, etc.).") + + // Kazakh (kk) + + static let kazakhVariantsAlertTitle = WMFLocalizedString("kazakh-variants-alert-title", value: "Updates to Kazakh variant support", comment: "Title of alert used to inform users about Kazakh variant support.") + + static let kazakhVariantsAlertBody = WMFLocalizedString("kazakh-variants-alert-body", value: "The Wikipedia app now supports the following Kazakh variants as primary or secondary languages within the app, making it easier to read, search and edit in your preferred variants:\n\nҚазақша Kazakh (kk)\nҚазақша Кирил Kazakh, Cyrillic (kk-cyrl)\nqazaqşa latin Kazakh, Latin (kk-latn)\nتوتە قازاقشا Kazakh, Arabic (kk-arab)", comment: "Body text of alert used to inform users about Kazakh variant support. Please do not translate the newlines (\n) or Kazakh characters (Қазақша, etc.).") + + // Kurdish (ku) + + static let kurdishVariantsAlertTitle = WMFLocalizedString("kurdish-variants-alert-title", value: "Updates to Kurdish variant support", comment: "Title of alert used to inform users about Kurdish variant support.") + + static let kurdishVariantsAlertBody = WMFLocalizedString("kurdish-variants-alert-body", value: "The Wikipedia app now supports the following Kurdish variants as primary or secondary languages within the app, making it easier to read, search and edit in your preferred variants:\n\nKurdî Latînî Kurdish, Latin (ku-latn)\nكوردی Kurdish, Arabic (kk-arab)", comment: "Body text of alert used to inform users about Kurdish variant support. Please do not translate the newlines (\n) or Kurdish characters (كوردی, etc.).") + + // Serbian (sr) + + static let serbianVariantsAlertTitle = WMFLocalizedString("serbian-variants-alert-title", value: "Updates to Serbian variant support", comment: "Title of alert used to inform users about Serbian variant support.") + + static let serbianVariantsAlertBody = WMFLocalizedString("serbian-variants-alert-body", value: "The Wikipedia app now supports the following Serbian variants as primary or secondary languages within the app, making it easier to read, search and edit in your preferred variants:\n\nсрпски ћирилица Serbian, Cyrillic (sr-ec)\nsrpski latinica Serbian, Latin (sr-el)", comment: "Body text of alert used to inform users about Serbian variant support. Please do not translate the newlines (\n) or Serbian characters (nсрпски ћирилица, etc.).") + + // Tajik (tg) + + static let tajikVariantsAlertTitle = WMFLocalizedString("tajik-variants-alert-title", value: "Updates to Tajik variant support", comment: "Title of alert used to inform users about Tajik variant support.") + + static let tajikVariantsAlertBody = WMFLocalizedString("tajik-variants-alert-body", value: "The Wikipedia app now supports the following Tajik variants as primary or secondary languages within the app, making it easier to read, search and edit in your preferred variants:\n\nтоҷикӣ кирилликӣ Tajik, Cyrillic (tg-cyrl)\ntojikī lotinī Tajik, Latin (tg-latn)", comment: "Body text of alert used to inform users about Tajik variant support. Please do not translate the newlines (\n) or Tajik characters (тоҷикӣ кирилликӣ, etc.).") + + // Uzbek (uz) + + static let uzbekVariantsAlertTitle = WMFLocalizedString("uzbek-variants-alert-title", value: "Updates to Uzbek variant support", comment: "Title of alert used to inform users about Uzbek variant support.") + + static let uzbekVariantsAlertBody = WMFLocalizedString("uzbek-variants-alert-body", value: "The Wikipedia app now supports the following Uzbek variants as primary or secondary languages within the app, making it easier to read, search and edit in your preferred variants:\n\noʻzbekcha lotin Uzbek, Latin (uz-latin)\nўзбекча кирилл Uzbek, Cyrillic (uz-cyrl)", comment: "Body text of alert used to inform users about Uzbek variant support. Please do not translate the newlines (\n) or Uzbek characters (ўзбекча кирилл, etc.).") + + // Tachelhit + + static let tachelhitVariantsAlertTitle = WMFLocalizedString("tachelhit-variants-alert-title", value: "Updates to Tachelhit variant support", comment: "Title of alert used to inform users about Tachelhit variant support.") + + static let tachelhitVariantsAlertBody = WMFLocalizedString("tachelhit-variants-alert-body", value: "The Wikipedia app now supports the following Tachelhit variants as primary or secondary languages within the app, making it easier to read, search and edit in your preferred variants:\n\nⵜⴰⵛⵍⵃⵉⵜ Tachelhit, Tifinagh (shi-tfng)\nTaclḥit Tachelhit, Latin (shi-latn)", comment: "Body text of alert used to inform users about Tachelhit variant support. Please do not translate the newlines (\n) or Tachelhit characters (ⵜⴰⵛⵍⵃⵉⵜ, etc.).") + +} diff --git a/Apps/Wikipedia/WMF Framework/Configuration.swift b/Apps/Wikipedia/WMF Framework/Configuration.swift new file mode 100644 index 0000000..2029384 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Configuration.swift @@ -0,0 +1,413 @@ +import Foundation + + +/// Configuration handles the current environment - production, beta, staging, labs +/// It has the functions that build URLs for the various APIs utilized by the app. +/// It also maintains the list of relevant domains - default domain, domains that require the CentralAuth cookies to be copied, etc. +@objc(WMFConfiguration) +public class Configuration: NSObject { + + public struct StagingOptions: OptionSet { + public let rawValue: Int + + public static let appsLabsforPCS = StagingOptions(rawValue: 1 << 0) + public static let deploymentLabsForEventLogging = StagingOptions(rawValue: 1 << 1) + public static let betaCluster = StagingOptions(rawValue: 1 << 2) // note, this will force beta cluster for PCS (thus ignoring an appsLabsforPCS value if also set) and force deploymentLabsForEventLogging + + public init(rawValue: Int) { + self.rawValue = rawValue + } + } + + public struct LocalOptions: OptionSet { + public let rawValue: Int + + public static let localAnnouncements = LocalOptions(rawValue: 1 << 0) + public static let localPCS = LocalOptions(rawValue: 1 << 1) + + public init(rawValue: Int) { + self.rawValue = rawValue + } + } + + public enum Environment { + case production + case staging(StagingOptions) + case local(LocalOptions) + } + + public let environment: Environment + + @objc public static let current: Configuration = { + #if WMF_LOCAL + return Configuration.local(options: [.localPCS, .localAnnouncements]) + #elseif WMF_STAGING + + /* NOTE: .betaCluster attempts to point to the MediaWiki beta cluster for all possible endpoints. + Change this to .appsLabsForPCS and/or .deploymentLabsForEventLogging for alternative staging environments. + Example: Configuration.staging(options: [.appsLabsForPCS, .deploymentLabsForEventLogging]) + .appsLabsForPCS = Product Infrastructure team's labs instance for PCS endpoints + .deploymentLabsForEventLogging = labs instance for testing event logging endpoints + All other endpoints would point to production */ + + return Configuration.staging(options: [.deploymentLabsForEventLogging]) + #else + return .production + #endif + }() + + private let pageContentServiceAPIType: APIURLComponentsBuilder.RESTBase.BuilderType + private let feedContentAPIType: APIURLComponentsBuilder.RESTBase.BuilderType + private let announcementsAPIType: APIURLComponentsBuilder.RESTBase.BuilderType + private let eventLoggingAPIType: APIURLComponentsBuilder.EventLogging.BuilderType + private let mediaWikiRestAPIType = APIURLComponentsBuilder.MediaWiki.BuilderType.productionRest + private let mediaWikiAPIType = APIURLComponentsBuilder.MediaWiki.BuilderType.production + private let wikidataAPIType: APIURLComponentsBuilder.Wikidata.BuilderType + private let commonsAPIType: APIURLComponentsBuilder.Commons.BuilderType + private let metricsAPIType = APIURLComponentsBuilder.RESTBase.BuilderType.production + + // MARK: Configurations + + private static var commonProductionCentralAuthCookieTargetDomains = [ + Domain.mediaWiki.withDotPrefix, + Domain.wikimedia.withDotPrefix, + Domain.wiktionary.withDotPrefix, + Domain.wikiquote.withDotPrefix, + Domain.wikibooks.withDotPrefix, + Domain.wikisource.withDotPrefix, + Domain.wikinews.withDotPrefix, + Domain.wikiversity.withDotPrefix, + Domain.wikispecies.withDotPrefix, + Domain.wikivoyage.withDotPrefix + ] + + public static let production: Configuration = { + + let centralAuthCookieTargetDomains = commonProductionCentralAuthCookieTargetDomains + [Domain.wikidata.withDotPrefix, Domain.commons.withDotPrefix] + + return Configuration( + environment: .production, + defaultSiteDomain: Domain.wikipedia, + wikipediaCookieDomain: Domain.wikipedia.withDotPrefix, + centralAuthCookieTargetDomains: centralAuthCookieTargetDomains, + pageContentServiceAPIType: .production, + feedContentAPIType: .production, + announcementsAPIType: .production, + wikidataAPIType: .production, + commonsAPIType: .production, + eventLoggingAPIType: .production) + }() + + private static func staging(options: StagingOptions) -> Configuration { + + let defaultSiteDomain = options.contains(.betaCluster) ? Domain.wikipediaBetaLabs : Domain.wikipedia + let wikipediaCookieDomain = options.contains(.betaCluster) ? Domain.wikipediaBetaLabs.withDotPrefix : Domain.wikipedia.withDotPrefix + let wikidataCookieDomain = options.contains(.betaCluster) ? Domain.wikidataBetaLabs.withDotPrefix : Domain.wikidata.withDotPrefix + let commonsCookieDomain = options.contains(.betaCluster) ? Domain.commonsBetaLabs.withDotPrefix : Domain.commons.withDotPrefix + + let centralAuthCookieTargetDomains = commonProductionCentralAuthCookieTargetDomains + [wikidataCookieDomain, commonsCookieDomain] + + let pcsApiType: APIURLComponentsBuilder.RESTBase.BuilderType = options.contains(.appsLabsforPCS) && !options.contains(.betaCluster) ? .stagingAppsLabsPCS : .production + let wikidataApiType: APIURLComponentsBuilder.Wikidata.BuilderType = options.contains(.betaCluster) ? .betaLabs : .production + let commonsApiType: APIURLComponentsBuilder.Commons.BuilderType = options.contains(.betaCluster) ? .betaLabs : .production + let eventLoggingApiType: APIURLComponentsBuilder.EventLogging + .BuilderType = options.contains(.deploymentLabsForEventLogging) || options.contains(.betaCluster) ? .staging : .production + + return Configuration( + environment: .staging(options), + defaultSiteDomain: defaultSiteDomain, + wikipediaCookieDomain: wikipediaCookieDomain, + centralAuthCookieTargetDomains: centralAuthCookieTargetDomains, + pageContentServiceAPIType: pcsApiType, + feedContentAPIType: .production, + announcementsAPIType: .production, + wikidataAPIType: wikidataApiType, + commonsAPIType: commonsApiType, + eventLoggingAPIType: eventLoggingApiType + ) + } + + private static func local(options: LocalOptions) -> Configuration { + + let pcsApiType: APIURLComponentsBuilder.RESTBase.BuilderType = options.contains(.localPCS) ? .localPCS : .production + let announcementsApiType: APIURLComponentsBuilder.RESTBase.BuilderType = options.contains(.localAnnouncements) ? .localAnnouncements : .production + + let centralAuthCookieTargetDomains = commonProductionCentralAuthCookieTargetDomains + [Domain.wikidata.withDotPrefix, Domain.commons.withDotPrefix] + + return Configuration( + environment: .local(options), + defaultSiteDomain: Domain.wikipedia, + wikipediaCookieDomain: Domain.wikipedia.withDotPrefix, + centralAuthCookieTargetDomains: centralAuthCookieTargetDomains, + pageContentServiceAPIType: pcsApiType, + feedContentAPIType: .production, + announcementsAPIType: announcementsApiType, + wikidataAPIType: .production, + commonsAPIType: .production, + eventLoggingAPIType: .production) + } + + // MARK: Constants + + struct Scheme { + static let http = "http" + static let https = "https" + } + + public struct Domain { + public static let wikipedia = "wikipedia.org" + public static let wikipediaBetaLabs = "wikipedia.beta.wmflabs.org" + public static let wikidata = "wikidata.org" + public static let wikidataBetaLabs = "wikidata.beta.wmflabs.org" + public static let commons = "commons.wikimedia.org" + public static let commonsBetaLabs = "commons.wikimedia.beta.wmflabs.org" + public static let mediaWiki = "www.mediawiki.org" + public static let wikispecies = "species.wikimedia.org" + public static let appsLabs = "mobileapps.wmflabs.org" // Product Infrastructure team's labs instance + public static let localhost = "localhost" + public static let englishWikipedia = "en.wikipedia.org" + public static let testWikipedia = "test.wikipedia.org" + public static let wikimedia = "wikimedia.org" + public static let metaWiki = "meta.wikimedia.org" + public static let wikimediafoundation = "wikimediafoundation.org" + public static let uploads = "upload.wikimedia.org" + public static let wikibooks = "wikibooks.org" + public static let wiktionary = "wiktionary.org" + public static let wikiquote = "wikiquote.org" + public static let wikisource = "wikisource.org" + public static let wikinews = "wikinews.org" + public static let wikiversity = "wikiversity.org" + public static let wikivoyage = "wikivoyage.org" + } + + struct Path { + static let wikiResourceComponent = ["wiki"] + static let restBaseAPIComponents = ["api", "rest_v1"] + static let mediaWikiAPIComponents = ["w", "api.php"] + static let mediaWikiRestAPIComponents = ["w", "rest.php"] + static let expandedWikiResourceComponents = ["w", "index.php"] + } + + // MARK: State + + @objc public let defaultSiteDomain: String + public let defaultSiteURL: URL + + public let wikipediaCookieDomain: String + public let centralAuthCookieSourceDomain: String // copy cookies from + public let centralAuthCookieTargetDomains: [String] // copy cookies to + + // Wikipedia Domains + public let wikipediaDomains: [String] + + // Domains that can fall back to in-app web view + public let inAppWebViewRoutingDomains: [String] + + @objc public lazy var router: Router = { + return Router(configuration: self) + }() + + required init(environment: Environment, defaultSiteDomain: String, + wikipediaCookieDomain: String, + centralAuthCookieTargetDomains: [String] = [], + pageContentServiceAPIType: APIURLComponentsBuilder.RESTBase.BuilderType, + feedContentAPIType: APIURLComponentsBuilder.RESTBase.BuilderType, + announcementsAPIType: APIURLComponentsBuilder.RESTBase.BuilderType, + wikidataAPIType: APIURLComponentsBuilder.Wikidata.BuilderType, + commonsAPIType: APIURLComponentsBuilder.Commons.BuilderType, + eventLoggingAPIType: APIURLComponentsBuilder.EventLogging.BuilderType) { + self.environment = environment + self.defaultSiteDomain = defaultSiteDomain + var components = URLComponents() + components.scheme = "https" + components.host = defaultSiteDomain + self.defaultSiteURL = components.url! + self.wikipediaCookieDomain = wikipediaCookieDomain + self.centralAuthCookieSourceDomain = self.wikipediaCookieDomain + self.centralAuthCookieTargetDomains = centralAuthCookieTargetDomains + + self.wikipediaDomains = [Domain.wikipedia, Domain.wikipediaBetaLabs, Domain.appsLabs] + self.inAppWebViewRoutingDomains = wikipediaDomains + [Domain.mediaWiki, Domain.wikidata, Domain.wikimedia, Domain.wikimediafoundation] + self.pageContentServiceAPIType = pageContentServiceAPIType + self.feedContentAPIType = feedContentAPIType + self.announcementsAPIType = announcementsAPIType + self.wikidataAPIType = wikidataAPIType + self.commonsAPIType = commonsAPIType + self.eventLoggingAPIType = eventLoggingAPIType + } + + // MARK: Page Content Service + + public func pageContentServiceBuilder(withWikiHost wikiHost: String? = nil) -> APIURLComponentsBuilder { + let builder = pageContentServiceAPIType.builder(withWikiHost: wikiHost) + return builder + } + + /// The Page Content Service includes mobile-html and the associated endpoints. It can be run locally with this repository: https://gerrit.wikimedia.org/r/admin/projects/mediawiki/services/mobileapps + /// On production, it is run through RESTBase at https://en.wikipedia.org/api/rest_v1/ (works for all language wikis) + @objc(pageContentServiceAPIURLForURL:appendingPathComponents:) + public func pageContentServiceAPIURLForURL(_ url: URL? = nil, appending pathComponents: [String] = [""]) -> URL? { + let builder = pageContentServiceAPIType.builder(withWikiHost: url?.host) + let components = builder.components(byAppending: pathComponents) + return components.wmf_URLWithLanguageVariantCode(url?.wmf_languageVariantCode) + } + + /// Returns the default request headers for Page Content Service API requests + public func pageContentServiceHeaders(for url: URL) -> [String: String] { + + // If the language supports variants, only send a single code with variant for that language. + // This is a workaround for an issue with server-side Accept-Language header handling and + // can be removed when https://phabricator.wikimedia.org/T256491 is fixed. + // NOTE: In general it does not seem that most sites process multi-language Accept-Language headers. + // For variants, sending a single Accept-Language header is sufficient and seems the least error-prone. + if let languageVariantCode = url.wmf_languageVariantCode { + return ["Accept-Language": languageVariantCode] + } else { + return [:] + } + } + + // MARK: Metrics + + /// The metrics API lives only on wikimedia.org: https://wikimedia.org/api/rest_v1/ + @objc(metricsAPIURLComponentsAppendingPathComponents:) + public func metricsAPIURLComponents(appending pathComponents: [String] = [""]) -> URLComponents { + let builder = metricsAPIType.builder(withWikiHost: Domain.wikimedia) + return builder.components(byAppending: ["metrics"] + pathComponents) + } + + // MARK: Wikifeeds (Feed Content and Announcements) + + /// Feed content is located in the wikifeeds repository. It can be run locally with: https://gerrit.wikimedia.org/r/admin/projects/mediawiki/services/wikifeeds + /// On production, it is run through RESTBase at https://en.wikipedia.org/api/rest_v1/ (works for all language wikis) + @objc(feedContentAPIURLForURL:appendingPathComponents:) + public func feedContentAPIURLForURL(_ url: URL?, appending pathComponents: [String] = [""]) -> URL? { + let builder = feedContentAPIType.builder(withWikiHost: url?.host) + let components = builder.components(byAppending: pathComponents) + return components.wmf_URLWithLanguageVariantCode(url?.wmf_languageVariantCode) + } + + /// Announcements are located in the wikifeeds repository. It can be run locally with: https://gerrit.wikimedia.org/r/admin/projects/mediawiki/services/wikifeeds + /// On production, it is run through RESTBase at https://en.wikipedia.org/api/rest_v1/ (works for all language wikis) + @objc(announcementsAPIURLForURL:appendingPathComponents:) + public func announcementsAPIURLForURL(_ url: URL?, appending pathComponents: [String] = [""]) -> URL? { + let builder = announcementsAPIType.builder(withWikiHost: url?.host) + let components = builder.components(byAppending: pathComponents) + return components.wmf_URLWithLanguageVariantCode(url?.wmf_languageVariantCode) + } + + // MARK: Event Logging + + @objc(eventLoggingAPIURLWithPayload:) + public func eventLoggingAPIURL(with payload: NSObject) -> URL? { + let builder = eventLoggingAPIType.builder() + let components = try? builder.components(byAssigningPayloadToPercentEncodedQuery: payload) + return components?.url + } + + // MARK: MediaWiki Rest + + public func mediaWikiRestAPIURLForURL(_ url: URL? = nil, appending pathComponents: [String] = [""], queryParameters: [String: Any]? = nil) -> URL? { + let builder = mediaWikiRestAPIType.builder(withWikiHost: url?.host) + let components = builder.components(byAppending: pathComponents, queryParameters: queryParameters) + return components.wmf_URLWithLanguageVariantCode(url?.wmf_languageVariantCode) + } + + // MARK: MediaWiki + + @objc(mediaWikiAPIURLForURL:withQueryParameters:) + public func mediaWikiAPIURLForURL(_ url: URL?, with queryParameters: [String: Any]? = nil) -> URL? { + let components = mediaWikiAPIURLForHost(url?.host, with: queryParameters) + return components.wmf_URLWithLanguageVariantCode(url?.wmf_languageVariantCode) + } + + public func mediaWikiAPIURLForHost(_ host: String? = nil, with queryParameters: [String: Any]? = nil) -> URLComponents { + let builder = mediaWikiAPIType.builder(withWikiHost: host) + guard let queryParameters = queryParameters else { + return builder.components() + } + return builder.components(queryParameters: queryParameters) + } + + public func mediaWikiAPIURLForLanguageCode(_ languageCode: String, siteDomain: String? = nil, queryParameters: [String: Any]?) -> URLComponents { + let domain = siteDomain ?? defaultSiteDomain + let host = "\(languageCode).\(domain)" + return mediaWikiAPIURLForHost(host, with: queryParameters) + } + + // MARK: Wikidata + + public func wikidataAPIURLComponents(with queryParameters: [String: Any]?) -> URLComponents { + let builder = wikidataAPIType.builder() + return builder.components(queryParameters: queryParameters) + } + + // MARK: Commons + + @objc(commonsAPIURLComponentsWithQueryParameters:) + public func commonsAPIURLComponents(with queryParameters: [String: Any]?) -> URLComponents { + let builder = commonsAPIType.builder() + return builder.components(queryParameters: queryParameters) + } + + // MARK: Article URLs + + func articleURLComponentsBuilder(for host: String) -> APIURLComponentsBuilder { + var components = URLComponents() + components.host = host + components.scheme = Scheme.https + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: Path.wikiResourceComponent) + } + + func expandedArticleURLComponentsBuilder(for host: String) -> APIURLComponentsBuilder { + var components = URLComponents() + components.host = host + components.scheme = Scheme.https + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: Path.expandedWikiResourceComponents) + } + + public func articleURLForHost(_ host: String, languageVariantCode: String?, appending pathComponents: [String]) -> URL? { + let builder = articleURLComponentsBuilder(for: host) + let components = builder.components(byAppending: pathComponents) + return components.wmf_URLWithLanguageVariantCode(languageVariantCode) + } + + // Uses format https://en.wikipedia.org/w/index.php?title=Main_Page + // As opposed to https://en.wikipedia.org/wiki/Main_Page + public func expandedArticleURLForHost(_ host: String, languageVariantCode: String?, queryParameters: [String: Any]?) -> URL? { + let builder = expandedArticleURLComponentsBuilder(for: host) + let components = builder.components(byAppending: [], queryParameters: queryParameters) + return components.wmf_URLWithLanguageVariantCode(languageVariantCode) + } + + // MARK: Routing Helpers + + public func isWikipediaHost(_ host: String?) -> Bool { + guard let host = host else { + return false + } + for domain in wikipediaDomains { + if host.isDomainOrSubDomainOf(domain) { + return true + } + } + + return false + } + + /// Indicates if a url should fall back to an in-app web view or not + /// Please inspect url namespace first and confirm url cannot display natively before using this method. + /// - Parameter host: url host that you are trying to route + /// - Returns: true = host should fall back to app web view, route to in-app web view. false = host should fall back to external Safari web browser (business logic for parental controls). + public func hostCanRouteToInAppWebView(_ host: String?) -> Bool { + guard let host = host else { + return false + } + for domain in inAppWebViewRoutingDomains { + if host.isDomainOrSubDomainOf(domain) { + return true + } + } + return false + } +} diff --git a/Apps/Wikipedia/WMF Framework/DateFormatter+WikipediaLanguage.swift b/Apps/Wikipedia/WMF Framework/DateFormatter+WikipediaLanguage.swift new file mode 100644 index 0000000..4d9f94a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/DateFormatter+WikipediaLanguage.swift @@ -0,0 +1,81 @@ +import Foundation + +extension DateFormatter { + + // Returns year string - i.e. '1000' or '200 BC'. (Negative years are 'BC') + class func wmf_yearString(for year: Int, with wikipediaLanguageCode: String?) -> String? { + var components = DateComponents() + components.year = year + let calendar = NSCalendar.wmf_utcGregorian() + guard let date = calendar?.date(from: components) else { + return nil + } + let formatter = year < 0 ? DateFormatter.wmf_yearWithEraGMTDateFormatter(for: wikipediaLanguageCode) : DateFormatter.wmf_yearGMTDateFormatter(for: wikipediaLanguageCode) + return formatter.string(from: date) + } + + private static var wmf_yearGMTDateFormatterCache: [String: DateFormatter] = [:] + + public static func wmf_yearGMTDateFormatter(for wikipediaLanguageCode: String?) -> DateFormatter { + let wikipediaLanguageCode = wikipediaLanguageCode ?? "en" + if let formatter = wmf_yearGMTDateFormatterCache[wikipediaLanguageCode] { + return formatter + } + + let dateFormatter = DateFormatter() + dateFormatter.locale = NSLocale.wmf_locale(for: wikipediaLanguageCode) + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + dateFormatter.setLocalizedDateFormatFromTemplate("y") + wmf_yearGMTDateFormatterCache[wikipediaLanguageCode] = dateFormatter + return dateFormatter + } + + private static var wmf_yearWithEraGMTDateFormatterCache: [String: DateFormatter] = [:] + + public static func wmf_yearWithEraGMTDateFormatter(for wikipediaLanguageCode: String?) -> DateFormatter { + let wikipediaLanguageCode = wikipediaLanguageCode ?? "en" + if let formatter = wmf_yearWithEraGMTDateFormatterCache[wikipediaLanguageCode] { + return formatter + } + + let dateFormatter = DateFormatter() + dateFormatter.locale = NSLocale.wmf_locale(for: wikipediaLanguageCode) + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + dateFormatter.setLocalizedDateFormatFromTemplate("y G") + wmf_yearWithEraGMTDateFormatterCache[wikipediaLanguageCode] = dateFormatter + return dateFormatter + } + + private static var wmf_monthNameDayNumberGMTFormatterCache: [String: DateFormatter] = [:] + + public static func wmf_monthNameDayNumberGMTFormatter(for wikipediaLanguageCode: String?) -> DateFormatter { + let wikipediaLanguageCode = wikipediaLanguageCode ?? "en" + if let formatter = wmf_monthNameDayNumberGMTFormatterCache[wikipediaLanguageCode] { + return formatter + } + + let dateFormatter = DateFormatter() + dateFormatter.locale = NSLocale.wmf_locale(for: wikipediaLanguageCode) + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + dateFormatter.setLocalizedDateFormatFromTemplate("MMMM d") + wmf_monthNameDayNumberGMTFormatterCache[wikipediaLanguageCode] = dateFormatter + return dateFormatter + } + + private static var wmf_longDateGMTFormatterCache: [String: DateFormatter] = [:] + + public static func wmf_longDateGMTFormatter(for wikipediaLanguageCode: String?) -> DateFormatter { + let wikipediaLanguageCode = wikipediaLanguageCode ?? "en" + if let formatter = wmf_longDateGMTFormatterCache[wikipediaLanguageCode] { + return formatter + } + let dateFormatter = DateFormatter() + dateFormatter.locale = NSLocale.wmf_locale(for: wikipediaLanguageCode) + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + dateFormatter.timeStyle = .none + dateFormatter.dateStyle = .long + wmf_longDateGMTFormatterCache[wikipediaLanguageCode] = dateFormatter + return dateFormatter + } + +} diff --git a/Apps/Wikipedia/WMF Framework/DeviceInfo.swift b/Apps/Wikipedia/WMF Framework/DeviceInfo.swift new file mode 100644 index 0000000..4bfa7be --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/DeviceInfo.swift @@ -0,0 +1,34 @@ +import Foundation + +class DeviceInfo { + static let shared = { + return DeviceInfo() + }() + + lazy var model: String? = { + var size : Int = 0 + sysctlbyname("hw.machine", nil, &size, nil, 0) + var machine = [CChar](repeating: 0, count: size) + sysctlbyname("hw.machine", &machine, &size, nil, 0) + return String(cString: machine) + }() + + // Only includes iOS 11 compatible older devices + static let olderDevicePrefixes: Set = [ + "iPad4", // iPad Air + "iPad5", // iPad Air 2 + "iPhone6", // iPhone 5s + "iPhone7", // iPhone 6 + "iPod7" // iPod Touch (6th gen) + ] + + lazy var isOlderDevice: Bool = { + guard let model = model else { + return true + } + guard let substring = model.components(separatedBy: ",").first else { + return true + } + return DeviceInfo.olderDevicePrefixes.contains(substring) + }() +} diff --git a/Apps/Wikipedia/WMF Framework/Dictionary+Equality.swift b/Apps/Wikipedia/WMF Framework/Dictionary+Equality.swift new file mode 100644 index 0000000..769ed94 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Dictionary+Equality.swift @@ -0,0 +1,12 @@ +import Foundation + +public extension Dictionary where Key: Equatable { + func wmf_isEqualTo(_ dictionary: Dictionary, excluding excludedKeys: C? = nil) -> Bool where C.Element == Key { + guard let excludedKeys = excludedKeys else { + return (self as NSDictionary).isEqual(to: dictionary) + } + let left = filter({ !excludedKeys.contains($0.key) }) + let right = dictionary.filter({ !excludedKeys.contains($0.key) }) + return (left as NSDictionary).isEqual(to: right) + } +} diff --git a/Apps/Wikipedia/WMF Framework/Event Platform/EPEventRecord+CoreDataClass.swift b/Apps/Wikipedia/WMF Framework/Event Platform/EPEventRecord+CoreDataClass.swift new file mode 100644 index 0000000..5cd2077 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Event Platform/EPEventRecord+CoreDataClass.swift @@ -0,0 +1,7 @@ +import Foundation +import CoreData + +@objc(WMFEPEventRecord) +public class EPEventRecord: NSManagedObject { + +} diff --git a/Apps/Wikipedia/WMF Framework/Event Platform/EPEventRecord+CoreDataProperties.swift b/Apps/Wikipedia/WMF Framework/Event Platform/EPEventRecord+CoreDataProperties.swift new file mode 100644 index 0000000..0edd2f8 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Event Platform/EPEventRecord+CoreDataProperties.swift @@ -0,0 +1,15 @@ +import Foundation +import CoreData + +extension EPEventRecord { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "WMFEPEventRecord") + } + + @NSManaged public var data: Data + @NSManaged public var stream: String + @NSManaged public var recorded: Date? + @NSManaged public var purgeable: Bool + +} diff --git a/Apps/Wikipedia/WMF Framework/Event Platform/EventPlatformClient.swift b/Apps/Wikipedia/WMF Framework/Event Platform/EventPlatformClient.swift new file mode 100644 index 0000000..ad1cda2 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Event Platform/EventPlatformClient.swift @@ -0,0 +1,730 @@ +/* + * Event Platform Client (EPC) + * + * DESCRIPTION + * Collects events in an input buffer, adds some metadata, places them in an + * ouput buffer where they are periodically bursted to a remote endpoint via + * HTTP POST. + * + * Designed for use with Wikipedia iOS application producing events to a + * stream intake service. + * + * LICENSE NOTICE + * Copyright 2020 Wikimedia Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 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. + * + * 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. + */ + +import Foundation +import CocoaLumberjackSwift + +/** + * Event Platform Client (EPC) + * + * Use `EPC.shared?.submit(stream, event, domain?, date?)` to submit ("log") events to + * streams. + * + * iOS schemas will always include the following fields which are managed by EPC + * and which will be assigned automatically by the library: + * - `dt`: client-side timestamp of when event was originally submitted + * - `app_install_id`: app install ID as in legacy EventLoggingService + * - `app_session_id`: the ID of the session at the time of the event when it was + * originally submitted + */ +public class EventPlatformClient: NSObject, SamplingControllerDelegate { + // MARK: - Properties + + public static let shared: EventPlatformClient = { + return EventPlatformClient() + }() + + // SINGLETONTODO + /// Session for requesting data + let session = MWKDataStore.shared().session + let samplingController: SamplingController + let storageManager: StorageManager? + + /** + * Store events until the library is finished initializing + * + * The EPC library makes an HTTP request to a remote stream configuration service for information + * about how to evaluate incoming event data. Until this initialization is complete, we store any incoming + * events in this buffer. + * + * Only modify (append events to, remove events from) asynchronously via `queue.async` + */ + private var inputBuffer: [(Data, Stream)] = [] + + /** + * Maximum number of events allowed in the input buffer + */ + private let inbutBufferLimit = 128 + + /** + * Streams are the event stream identifiers that can be utilized with the EventPlatformClientLibrary. They should + * correspond to the `$id` of a schema in + * [this repository](https://gerrit.wikimedia.org/g/schemas/event/secondary/). + */ + public enum Stream: String, Codable { + case editHistoryCompare = "ios.edit_history_compare" + case remoteNotificationsInteraction = "ios.notification_interaction" + case talkPagesInteraction = "ios.talk_page_interaction" + } + + /** + * Schema specifies which schema (and specifically which version of that schema) + * a given event conforms to. Analytics schemas can be found in the jsonschema directory of + * [secondary repo](https://gerrit.wikimedia.org/g/schemas/event/secondary/). + * As an example, if instrumenting client-side error logging, a possible + * `$schema` would be `/mediawiki/client/error/1.0.0`. For the most part, the + * `$schema` will start with `/analytics`, since there's where + * analytics-related schemas are collected. + */ + public enum Schema: String, Codable { + case editHistoryCompare = "/analytics/mobile_apps/ios_edit_history_compare/2.1.0" + case remoteNotificationsInteraction = "/analytics/mobile_apps/ios_notification_interaction/2.1.0" + case talkPages = "/analytics/mobile_apps/ios_talk_page_interaction/1.0.0" + } + + /** + * Serial dispatch queue that enables working with properties in a thread-safe + * way + */ + private let queue = DispatchQueue(label: "EventPlatformClient-" + UUID().uuidString) + + /** + * Serial dispatch queue for encoding data on a background thread + */ + private let encodeQueue = DispatchQueue(label: "EventPlatformClientEncode-" + UUID().uuidString, qos: .background) + + /** + * Where to send events to for intake + * + * See [wikitech:Event Platform/EventGate](https://wikitech.wikimedia.org/wiki/Event_Platform/EventGate) + * for more information. Specifically, the section on + * **eventgate-analytics-external**. This service uses the stream + * configurations from Meta wiki as its source of truth. + */ + private static let eventIntakeURI = URL(string: "https://intake-analytics.wikimedia.org/v1/events")! + + /** + * MediaWiki API endpoint which returns stream configurations as JSON + * + * Streams are configured via [mediawiki-config/wmf-config/InitialiseSettings.php](https://gerrit.wikimedia.org/g/operations/mediawiki-config/+/master/wmf-config/InitialiseSettings.php) + * + * The config changes are deployed in [backport windows](https://wikitech.wikimedia.org/wiki/Backport_windows) + * by scheduling on the [Deployments](https://wikitech.wikimedia.org/wiki/Deployments) + * page. Stream configurations are made available for external consumption via + * MediaWiki API via [Extension:EventStreamConfig](https://gerrit.wikimedia.org/g/mediawiki/extensions/EventStreamConfig/) + * + * In production, we use [Meta wiki](https://meta.wikimedia.org/wiki/Main_Page) + * [streamconfigs endpoint](https://meta.wikimedia.org/w/api.php?action=help&modules=streamconfigs) + * with the constraint that the `destination_event_service` is configured to + * be "eventgate-analytics-external" (to filter out irrelevant streams from + * the returned list of stream configurations). + */ + private static let streamConfigsURI = URL(string: "https://meta.wikimedia.org/w/api.php?action=streamconfigs&format=json&constraints=destination_event_service=eventgate-analytics-external")! + + /** + * An individual stream's configuration. + */ + struct StreamConfiguration: Codable { + let sampling: Sampling? + struct Sampling: Codable { + let rate: Double? + let identifier: String? + } + } + + /** + * Holds each stream's configuration. + */ + private var streamConfigurations: [Stream: StreamConfiguration]? { + get { + queue.sync { + return _streamConfigurations + } + } + set { + queue.async { + self._streamConfigurations = newValue + } + } + } + private var _streamConfigurations: [Stream: StreamConfiguration]? = nil + + /** + * Updated when app enters background, used for determining if the session has + * expired. + */ + private var lastTimestamp: Date = Date() + + /** + * Return a session identifier + * - Returns: session ID + * + * The identifier is a string of 20 zero-padded hexadecimal digits + * representing a uniformly random 80-bit integer. + */ + internal var sessionID: String { + queue.sync { + guard let sID = _sessionID else { + let newID = generateID() + _sessionID = newID + return newID + } + + return sID + } + } + private var _sessionID: String? + + + // MARK: - Methods + + public override init() { + self.storageManager = StorageManager.shared + self.samplingController = SamplingController() + + super.init() + + self.samplingController.delegate = self + + guard self.storageManager != nil else { + DDLogError("EPC: Error initializing the storage manager. Event intake and submission will be disabled.") + return + } + + self.fetchStreamConfiguration(retries: 10, retryDelay: 30) + } + + /** + * This method is called by the application delegate in + * `applicationWillResignActive()` and disables event logging. + */ + public func appInBackground() { + lastTimestamp = Date() + } + /** + * This method is called by the application delegate in + * `applicationDidBecomeActive()` and re-enables event logging. + * + * If it has been more than 15 minutes since the app entered background state, + * a new session is started. + */ + public func appInForeground() { + if sessionTimedOut() { + resetSession() + } + } + /** + * This method is called by the application delegate in + * `applicationWillTerminate()` + * + * We do not persist session ID on app close because we have decided that a + * session ends when the user (or the OS) has closed the app or when 15 + * minutes of inactivity have passed. + */ + public func appWillClose() { + // Placeholder for any onTerminate logic + } + + /** + * Generates a new identifier using the same algorithm as EPC libraries for + * web and Android + */ + private func generateID() -> String { + var id: String = "" + for _ in 1...5 { + id += String(format: "%04x", arc4random_uniform(65535)) + } + return id + } + + /** + * Called when user toggles logging permissions in Settings + * + * This assumes storageManager's deviceID will be reset separately by a + * different owner (EventLoggingService's `reset()` method) + */ + public func reset() { + resetSession() + } + + /** + * Unset the session + */ + private func resetSession() { + queue.async { + self._sessionID = nil + } + samplingController.removeAllSamplingCache() + } + + /** + * Check if session expired, based on last active timestamp + * + * A new session ID is required if it has been more than 15 minutes since the + * user was last active (e.g. when app entered background). + */ + private func sessionTimedOut() -> Bool { + /* + * A TimeInterval value is always specified in seconds. + */ + return lastTimestamp.timeIntervalSinceNow < -900 + } + + /** + * Fetch stream configuration from stream configuration service + * - Parameters: + * - retries: number of retries remaining + * - retryDelay: seconds between each attempt, increasing by 50% after + * every failed attempt + */ + private func fetchStreamConfiguration(retries: Int, retryDelay: TimeInterval) { + self.httpGet(url: EventPlatformClient.streamConfigsURI, completion: { (data, response, error) in + guard let httpResponse = response as? HTTPURLResponse, let data = data, httpResponse.statusCode == 200 else { + DDLogWarn("EPC: Server did not respond adequately, will try \(EventPlatformClient.streamConfigsURI.absoluteString) again") + + if retries > 0 { + dispatchOnMainQueueAfterDelayInSeconds(retryDelay) { + self.fetchStreamConfiguration(retries: retries - 1, retryDelay: retryDelay * 1.5) + } + } else { + DDLogWarn("EPC: Ran out of retries when attempting to download stream configs") + } + + return + } + + self.loadStreamConfiguration(data) + }) + } + + /** + * Processes fetched stream config + * - Parameter data: JSON-serialized stream configuration + * + * Example of a retrieved config: + * ``` js + * { + * "streams": { + * "test.instrumentation.sampled": { + * "sampling": { + * "rate":0.1 + * } + * }, + * "test.instrumentation": {}, + * } + * } + * ``` + */ + private func loadStreamConfiguration(_ data: Data) { + #if DEBUG + if let raw = String.init(data: data, encoding: String.Encoding.utf8) { + DDLogDebug("EPC: Downloaded stream configs (raw): \(raw)") + } + #endif + guard let storageManager = self.storageManager else { + DDLogError("Storage manager not initialized; this shouldn't happen!") + return + } + struct StreamConfigurationsJSON: Codable { + let streams: [String: StreamConfiguration] + } + do { + let json = try JSONDecoder().decode(StreamConfigurationsJSON.self, from: data) + + // Make them available to any newly logged events before flushing + // buffer (this is set using serial queue but asynchronously) + streamConfigurations = json.streams.reduce(into: [:], { (result, kv) in + guard let stream = Stream(rawValue: kv.key) else { + return + } + result?[stream] = kv.value + }) + + // Process event buffer after making stream configs available + // NOTE: If any event is re-submitted while streamConfigurations + // is still being set (asynchronously), they will just go back to + // input buffer. + while let (data, stream) = inputBufferPopFirst() { + guard let config = streamConfigurations?[stream] else { + continue + } + guard samplingController.inSample(stream: stream, config: config) else { + continue + } + storageManager.push(data: data, stream: stream) + } + } catch let error { + DDLogError("EPC: Problem processing JSON payload from response: \(error)") + } + } + + /** + * Flush the queue of outgoing requests in a first-in-first-out, + * fire-and-forget fashion + */ + func postAllScheduled(_ completion: (() -> Void)? = nil) { + guard let storageManager = self.storageManager else { + completion?() + return + } + + let events = storageManager.popAll() + if events.count == 0 { +// DDLogDebug("EPC: Nothing to send.") + completion?() + return + } + + DDLogDebug("EPC: Processing all scheduled requests") + let group = DispatchGroup() + for event in events { + group.enter() + httpPost(url: EventPlatformClient.eventIntakeURI, body: event.data) { result in + switch result { + case .success: + storageManager.markPurgeable(event: event) + break + case .failure(let error): + switch error { + case .networkingLibraryError: + /// Leave unmarked to retry on networking library failure + break + default: + /// Give up on events rejected by the server + DDLogError("EPC: The analytics service failed to process an event. A response code of 400 could indicate that the event didn't conform to provided schema. Check the error for more information.: \(error)") + storageManager.markPurgeable(event: event) + break + } + } + group.leave() + } + } + group.notify(queue: queue) { + completion?() + } + } + + /// EventBody is used to encode event data into the POST body of a request to the Modern Event Platform + struct EventBody: Encodable where E: EventInterface { + /// EventGate needs to know which version of the schema to validate against + var meta: Meta + + struct Meta: Codable { + let stream: Stream + + /** + * meta.id is *optional* and should only be done in case the client is + * known to send duplicates of events, otherwise we don't need to + * make the payload any heavier than it already is + */ + let id: UUID + let domain: String? + } + + let appInstallID: String + + /** + * Generated events have the session ID attached to them before stream + * config is available (in case they're generated offline) and before + * they're cc'd to any other streams (once config is available). + */ + let appSessionID: String + + /** + * The top-level field `dt` is for recording the time the event + * was generated. EventGate sets `meta.dt` during ingestion, so for + * analytics events that field is used as "timestamp of reception" and + * is used for partitioning the events in the database. See Phab:T240460 + * for more information. + */ + let dt: Date + + /** + * Event represents the client-provided event data. + * The event is encoded at the top level of the resulting structure. + * If any of the `CodingKeys` conflict with keys defined by `EventBody`, + * the values from `event` will be used. + */ + let event: E + + enum CodingKeys: String, CodingKey { + case schema = "$schema" + case meta + case appInstallID = "app_install_id" + case appSessionID = "app_session_id" + case dt + case event + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + do { + try container.encode(meta, forKey: .meta) + try container.encode(appInstallID, forKey: .appInstallID) + try container.encode(appSessionID, forKey: .appSessionID) + try container.encode(dt, forKey: .dt) + try container.encode(E.schema, forKey: .schema) + try event.encode(to: encoder) + } catch let error { + DDLogError("EPC: Error encoding event body: \(error)") + } + } + } + + /** + * Submit an event according to the given stream's configuration. + * - Parameters: + * - stream: The stream to submit the event to + * - event: The event data + * - domain: Optional domain to include for the event (without protocol) + * + * An example call: + * ``` + * struct TestEvent: EventInterface { + * static let schema = "/analytics/mobile_apps/test/1.0.0" + * let test_string: String + * let test_map: SourceInfo + * struct SourceInfo: Codable { + * let file: String + * let method: String + * } + * } + * + * let sourceInfo = TestEvent.SourceInfo(file: "Features/Feed/ExploreViewController.swift", method: "refreshControlActivated") + * let event = TestEvent(test_string: "Explore Feed refreshed", test_map: sourceInfo) + * + * EventPlatformClient.shared?.submit( + * stream: .test, // Defined in `EPC.Stream` + * event: event + * ) + * ``` + * + * Regarding `domain`: this is *optional* and should be used when event needs + * to be attrributed to a particular wiki (Wikidata, Wikimedia Commons, a + * specific edition of Wikipedia, etc.). If the language is NOT relevant in + * the context, `domain` can be safely omitted. Using "domain" rather than + * "language" is consistent with the other platforms and allows for the + * possibility of setting a non-Wikipedia domain like "commons.wikimedia.org" + * and "wikidata.org" for multimedia/metadata-related in-app analytics. + * Instrumentation code should use the `host` property of a `URL` as the value + * for this parameter. + * + * Cases where instrumentation would set a `domain`: + * - reading or editing an article + * - managing watchlist + * - interacting with feed + * - searching + * + * Cases where it might not be necessary for the instrument to set a `domain`: + * - changing settings + * - managing reading lists + * - navigating map of nearby articles + * - multi-lingual features like Suggested Edits + * - marking session start/end; in which case schema and `data` should have a + * `languages` field where user's list of languages can be stored, although + * it might make sense to set it to the domain associated with the user's + * 1st preferred language – in which case use + * `MWKLanguageLinkController.sharedInstance().appLanguage.siteURL().host` + */ + public func submit(stream: Stream, event: E, domain: String? = nil) { + let date = Date() // Record the date synchronously so there's no delay + encodeQueue.async { + self._submit(stream: stream, event: event, date: date, domain: domain) + } + } + + /// Private, synchronous version of `submit`. + private func _submit(stream: Stream, event: E, date: Date, domain: String? = nil) { + guard let storageManager = self.storageManager else { + return + } + + let userDefaults = UserDefaults.standard + + if !userDefaults.wmf_sendUsageReports { + return + } + + guard let appInstallID = userDefaults.wmf_appInstallId else { + DDLogWarn("EPC: App install ID is unset. This shouldn't happen.") + return + } + + let meta = EventBody.Meta(stream: stream, id: UUID(), domain: domain) + + let eventPayload = EventBody(meta: meta, appInstallID: appInstallID, appSessionID: sessionID, dt: date, event: event) + do { + let encoder = JSONEncoder() + encoder.dateEncodingStrategy = .iso8601 + + #if DEBUG + encoder.outputFormatting = .prettyPrinted + #endif + + let data = try encoder.encode(eventPayload) + + #if DEBUG + let jsonString = String(data: data, encoding: .utf8)! + DDLogDebug("EPC: Scheduling event to be sent to \(EventPlatformClient.eventIntakeURI) with POST body:\n\(jsonString)") + #endif + + guard let streamConfigs = streamConfigurations else { + appendEventToInputBuffer(data: data, stream: stream) + return + } + guard let config = streamConfigs[stream] else { + DDLogDebug("EPC: Event submitted to '\(stream)' but only the following streams are configured: \(streamConfigs.keys.map(\.rawValue).joined(separator: ", "))") + return + } + guard samplingController.inSample(stream: stream, config: config) else { + DDLogDebug("EPC: Stream '\(stream.rawValue)' is not in sample") + return + } + storageManager.push(data: data, stream: stream) + } catch let error { + DDLogError("EPC: \(error.localizedDescription)") + } + + } +} + +// MARK: Thread-safe accessors for collection properties +private extension EventPlatformClient { + + /** + * Thread-safe synchronous retrieval of buffered events + */ + func getInputBuffer() -> [(Data, Stream)] { + queue.sync { + return self.inputBuffer + } + } + + /** + * Thread-safe synchronous buffering of an event + * - Parameter event: event to be buffered + */ + func appendEventToInputBuffer(data: Data, stream: Stream) { + queue.sync { + /* + * Check if input buffer has reached maximum allowed size. Practically + * speaking, there should not have been over a hundred events + * generated when the user first launches the app and before the + * stream configuration has been downloaded and becomes available. In + * such a case we're just going to start clearing out the oldest + * events to make room for new ones. + */ + if self.inputBuffer.count == self.inbutBufferLimit { + _ = self.inputBuffer.remove(at: 0) + } + self.inputBuffer.append((data, stream)) + } + } + + + /** + * Thread-safe synchronous removal of first buffered event + * - Returns: a previously buffered event + */ + func inputBufferPopFirst() -> (Data, Stream)? { + queue.sync { + if self.inputBuffer.isEmpty { + return nil + } + return self.inputBuffer.remove(at: 0) + } + } +} + +// MARK: NetworkIntegration + +private extension EventPlatformClient { + /// PostEventError describes the possible failure cases when POSTing an event + enum PostEventError: Error { + case networkingLibraryError(_ error: Error) + case missingResponse + case unexepectedResponse(_ httpCode: Int) + } + + /** + * HTTP POST + * - Parameter body: Body of the POST request + * - Parameter completion: callback invoked upon receiving the server response + */ + private func httpPost(url: URL, body: Data? = nil, completion: @escaping ((Result) -> Void)) { + DDLogDebug("EPC: Attempting to POST events") + let request = session.request(with: url, method: .post, bodyData: body, bodyEncoding: .json) + let task = session.dataTask(with: request, completionHandler: { (_, response, error) in + let fail: (PostEventError) -> Void = { error in + DDLogDebug("EPC: An error occurred sending the request: \(error)") + completion(.failure(error)) + } + if let error = error { + fail(PostEventError.networkingLibraryError(error)) + return + } + guard let httpResponse = response as? HTTPURLResponse else { + fail(PostEventError.missingResponse) + return + } + guard httpResponse.statusCode == 201 else { + fail(PostEventError.unexepectedResponse(httpResponse.statusCode)) + return + } + completion(.success(())) + }) + task?.resume() + } + + /** + * HTTP GET + * - Parameter url: Where to GET data from + * - Parameter completion: What to do with gotten data + */ + private func httpGet(url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void) { + DDLogDebug("EPC: Attempting to GET data from \(url.absoluteString)") + var request = URLRequest.init(url: url) // httpMethod = "GET" by default + request.setValue(WikipediaAppUtils.versionedUserAgent(), forHTTPHeaderField: "User-Agent") + let task = session.dataTask(with: request, completionHandler: completion) + task?.resume() + } +} + +// MARK: EventInterface + +/** + * Protocol for event data. + * Currently only requires conformance to Codable. + */ +public protocol EventInterface: Codable { + /** + * Defines which schema this event conforms to. + * Check the documentation for `EPC.Schema` for more information. + */ + static var schema: EventPlatformClient.Schema { get } +} diff --git a/Apps/Wikipedia/WMF Framework/Event Platform/EventPlatformEvents.xcdatamodeld/EventPlatformEvents.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Event Platform/EventPlatformEvents.xcdatamodeld/EventPlatformEvents.xcdatamodel/contents new file mode 100644 index 0000000..d345f9a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Event Platform/EventPlatformEvents.xcdatamodeld/EventPlatformEvents.xcdatamodel/contents @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/WMF Framework/Event Platform/MetricsClientBridge.swift b/Apps/Wikipedia/WMF Framework/Event Platform/MetricsClientBridge.swift new file mode 100644 index 0000000..0121acd --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Event Platform/MetricsClientBridge.swift @@ -0,0 +1,53 @@ +import Foundation + +@objc(WMFMetricsClientBridge) +public class MetricsClientBridge: NSObject { + + let client = EventPlatformClient.shared + + @objc(sharedInstance) public static let shared: MetricsClientBridge = { + return MetricsClientBridge() + }() + + @objc public func appInBackground() { + client.appInBackground() + } + + @objc public func appInForeground() { + client.appInForeground() + } + + @objc public func appWillClose() { + client.appWillClose() + } + + @objc public func reset() { + client.reset() + } + +} + +// MARK: PeriodicWorker + +extension MetricsClientBridge: PeriodicWorker { + public func doPeriodicWork(_ completion: @escaping () -> Void) { + guard let storageManager = self.client.storageManager else { + return + } + storageManager.pruneStaleEvents(completion: { + self.client.postAllScheduled(completion) + }) + } +} + +// MARK: BackgroundFetcher + +extension MetricsClientBridge: BackgroundFetcher { + public func performBackgroundFetch(_ completion: @escaping (UIBackgroundFetchResult) -> Void) { + doPeriodicWork { + completion(.noData) + } + } +} + + diff --git a/Apps/Wikipedia/WMF Framework/Event Platform/SamplingController.swift b/Apps/Wikipedia/WMF Framework/Event Platform/SamplingController.swift new file mode 100644 index 0000000..b5b3219 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Event Platform/SamplingController.swift @@ -0,0 +1,138 @@ +import Foundation +import CocoaLumberjackSwift + +protocol SamplingControllerDelegate: AnyObject { + var sessionID: String { get } +} + +class SamplingController: NSObject { + + /** + * Serial dispatch queue that enables working with properties in a thread-safe + * way + */ + private let queue = DispatchQueue(label: "EventPlatformClientSampling-" + UUID().uuidString) + + /** + * Cache of "in sample" / "out of sample" determination for each stream + * + * The process of determining only has to happen the first time an event is + * logged to a stream for which stream configuration is available. All other + * times `in_sample` simply returns the cached determination. + * + * Only cache determinations asynchronously via `queue.async` + */ + private var samplingCache: [EventPlatformClient.Stream: Bool] = [:] + + weak var delegate: SamplingControllerDelegate? + + /** + * Compute a boolean function on a random identifier + * - Parameter stream: name of the stream + * - Parameter config: stream configuration for the provided stream name + * - Returns: `true` if in sample or `false` otherwise + * + * The determinations are lazy and cached, so each stream's in-sample vs + * out-of-sample determination is computed only once, the first time an event + * is logged to that stream.ß + * + * Refer to sampling settings section in + * [mw:Wikimedia Product/Analytics Infrastructure/Stream configuration](https://www.mediawiki.org/wiki/Wikimedia_Product/Analytics_Infrastructure/Stream_configuration) + * for more information. + */ + func inSample(stream: EventPlatformClient.Stream, config: EventPlatformClient.StreamConfiguration) -> Bool { + if let cachedValue = getSamplingForStream(stream) { + return cachedValue + } + + guard let rate = config.sampling?.rate else { + /* + * If stream is present in streamConfigurations but doesn't have + * sampling settings, it is always in-sample. + */ + cacheSamplingForStream(stream, inSample: true) + return true + } + + /* + * All platforms use session ID as the default identifier for determining + * in- vs out-of-sample of events sent to streams. On the web, streams can + * be set to use pageview token instead. On the apps, streams can be set + * to use device token instead. + */ + let sessionIdentifierType = "session" + let deviceIdentifierType = "device" + let identifierType = config.sampling?.identifier ?? sessionIdentifierType + let appInstallID = UserDefaults.standard.wmf_appInstallId + + guard identifierType == sessionIdentifierType || identifierType == deviceIdentifierType else { + DDLogDebug("EPC: Logged to stream which is not configured for sampling based on \(sessionIdentifierType) or \(deviceIdentifierType) identifier") + cacheSamplingForStream(stream, inSample: false) + return false + } + + guard let identifier = identifierType == sessionIdentifierType ? delegate?.sessionID : appInstallID else { + DDLogError("EPC: Missing token for determining in- vs out-of-sample. Falling back to out-of-sample.") + cacheSamplingForStream(stream, inSample: false) + return false + } + let result = determine(identifier, rate) + cacheSamplingForStream(stream, inSample: result) + return result + } + + /** + * Yields a deterministic (not stochastic) determination of whether the + * provided `id` is in-sample or out-of-sample according to the `acceptance` + * rate + * - Parameter id: identifier to use for determining sampling + * - Parameter acceptance: the desired proportion of many `token`-s being + * accepted + * + * The algorithm works in a "widen the net on frozen fish" fashion -- tokens + * continue evaluating to true as the acceptance rate increases. For example, + * a device determined to be in-sample for a stream "A" having rate 0.1 will + * be determined to be in-sample for a stream "B" having rate 0.2, and its + * events will show up in tables "A" and "B". + */ + private func determine(_ id: String, _ acceptance: Double) -> Bool { + guard let token = UInt32(id.prefix(8), radix: 16) else { + return false + } + return (Double(token) / Double(UInt32.max)) < acceptance + } + + /** + * Thread-safe asynchronous caching of a stream's in-vs-out-of-sample + * determination + * - Parameter stream: name of stream to cache determination for + * - Parameter inSample: whether the stream was determined to be in-sample + * this session + */ + func cacheSamplingForStream(_ stream: EventPlatformClient.Stream, inSample: Bool) { + queue.async { + self.samplingCache[stream] = inSample + } + } + + /** + * Thread-safe synchronous retrieval of a stream's cached in-vs-out-of-sample determination + * - Parameter stream: name of stream to retrieve determination for from the cache + * - Returns: `true` if stream was determined to be in-sample this session, `false` otherwise + */ + func getSamplingForStream(_ stream: EventPlatformClient.Stream) -> Bool? { + queue.sync { + return self.samplingCache[stream] + } + } + + /** + * Thread-safe asynchronous clearance of cached stream in-vs-out-of-sample determinations + */ + func removeAllSamplingCache() { + queue.async { + self.samplingCache.removeAll() + } + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Event Platform/StorageManager.swift b/Apps/Wikipedia/WMF Framework/Event Platform/StorageManager.swift new file mode 100644 index 0000000..6bfe1e6 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Event Platform/StorageManager.swift @@ -0,0 +1,201 @@ +import Foundation +import CocoaLumberjackSwift + +@objc (WMFEPCStorageManager) +public class StorageManager: NSObject { + + private let managedObjectContext: NSManagedObjectContext + private let pruningAge: TimeInterval = 60*60*24*30 // 30 days + + @objc(sharedInstance) public static let shared: StorageManager? = { + let fileManager = FileManager.default + var storageDirectory = fileManager.wmf_containerURL().appendingPathComponent("Event Platform", isDirectory: true) + + do { + try fileManager.createDirectory(at: storageDirectory, withIntermediateDirectories: true, attributes: nil) + var values = URLResourceValues() + values.isExcludedFromBackup = true + try storageDirectory.setResourceValues(values) + } catch let error { + DDLogError("EPCStorageManager: Error creating Event Platform Client directory: \(error)") + } + + let storageURL = storageDirectory.appendingPathComponent("EventPlatformEvents.sqlite") + DDLogDebug("EPC StorageManager: Events persistent store: \(storageURL)") + return StorageManager(storageURL: storageURL) + }() + + private init?(storageURL: URL) { + guard let modelURL = Bundle.wmf.url(forResource: "EventPlatformEvents", withExtension: "momd"), let model = NSManagedObjectModel(contentsOf: modelURL) else { + return nil + } + + let psc = NSPersistentStoreCoordinator(managedObjectModel: model) + let options = [NSMigratePersistentStoresAutomaticallyOption: NSNumber(booleanLiteral: true), NSInferMappingModelAutomaticallyOption: NSNumber(booleanLiteral: true)] + + do { + try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storageURL, options: options) + } catch { + do { + try FileManager.default.removeItem(at: storageURL) + } catch { + + } + do { + try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storageURL, options: options) + } catch { + DDLogError("EPC: Event Platform StorageManager: adding persistent store to coordinator: \(error)") + return nil + } + } + + let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + managedObjectContext.persistentStoreCoordinator = psc + self.managedObjectContext = managedObjectContext + } + + func push(data: Data, stream: EventPlatformClient.Stream) { + let now = Date() + perform { moc in + if let record = NSEntityDescription.insertNewObject(forEntityName: "WMFEPEventRecord", into: moc) as? EPEventRecord { + record.data = data + record.stream = stream.rawValue + record.recorded = now + + DDLogDebug("EPC StorageManager: \(record.objectID) recorded!") + + self.save(moc) + } + } + } + + func popAll() -> [PersistedEvent] { + var events: [PersistedEvent] = [] + performAndWait { moc in + let fetch: NSFetchRequest = EPEventRecord.fetchRequest() + fetch.sortDescriptors = [NSSortDescriptor(keyPath: \EPEventRecord.recorded, ascending: true)] + fetch.predicate = NSPredicate(format: "(purgeable == FALSE)") + + do { + var count = 0 + let records = try moc.fetch(fetch) + for record in records { + guard let stream = EventPlatformClient.Stream(rawValue: record.stream) else { + continue + } + events.append(PersistedEvent(data: record.data, stream: stream, managedObjectURI: record.objectID.uriRepresentation())) + count += 1 + } + if count > 0 { + DDLogDebug("EPC: Found \(count) events awaiting submission") + } + } catch let error { + DDLogError(error.localizedDescription) + } + } + return events + } + + func markPurgeable(event: PersistedEvent) { + perform { moc in + do { + guard let psc = moc.persistentStoreCoordinator else { + DDLogWarn("EPC: Error getting persistent store coordinator") + return + } + guard let moid = psc.managedObjectID(forURIRepresentation: event.managedObjectURI) else { + DDLogWarn("EPC: Error getting managed object ID for URI \(event.managedObjectURI)") + return + } + guard let record = try moc.existingObject(with: moid) as? EPEventRecord else { + DDLogWarn("EPC: Tried to mark managed object \(moid) as purgeable, but it was not found") + return + } + record.purgeable = true + self.save(moc) + } catch let error { + DDLogError(error.localizedDescription) + } + + } + } + + func pruneStaleEvents(completion: @escaping (() -> Void)) { + perform { moc in + defer { + completion() + } + + let pruneFetch = NSFetchRequest(entityName: "WMFEPEventRecord") + pruneFetch.returnsObjectsAsFaults = false + + let pruneDate = Date().addingTimeInterval(-(self.pruningAge)) as NSDate + pruneFetch.predicate = NSPredicate(format: "(recorded < %@) OR (purgeable == TRUE)", pruneDate) + + let delete = NSBatchDeleteRequest(fetchRequest: pruneFetch) + delete.resultType = .resultTypeCount + + do { + let result = try moc.execute(delete) + guard let deleteResult = result as? NSBatchDeleteResult else { + DDLogError("EPC StorageManager: Could not read NSBatchDeleteResult") + return + } + + guard let count = deleteResult.result as? Int else { + DDLogError("EPC StorageManager: Could not read NSBatchDeleteResult count") + return + } + + if count > 0 { + DDLogInfo("EPC StorageManager: Pruned \(count) events") + } + + } catch let error { + DDLogError("EPC StorageManager: Error pruning events: \(error.localizedDescription)") + } + } + } + + private func save(_ moc: NSManagedObjectContext) { + guard moc.hasChanges else { + return + } + do { + try moc.save() + } catch let error { + DDLogError("EPC: Error saving StorageManager managedObjectContext: \(error)") + } + } + + private func performAndWait(_ block: (_ moc: NSManagedObjectContext) -> Void) { + let moc = self.managedObjectContext + moc.performAndWait { + block(moc) + } + } + + private func perform(_ block: @escaping (_ moc: NSManagedObjectContext) -> Void) { + let moc = self.managedObjectContext + moc.perform { + block(moc) + } + } +} + +struct PersistedEvent: Codable { + let data: Data + let stream: EventPlatformClient.Stream + let managedObjectURI: URL +} + +#if TEST + +extension StorageManager { + var managedObjectContextToTest: NSManagedObjectContext { return managedObjectContext } + func testSave(_ moc: NSManagedObjectContext) { + save(moc) + } +} + +#endif diff --git a/Apps/Wikipedia/WMF Framework/EventLogging.xcdatamodeld/.xccurrentversion b/Apps/Wikipedia/WMF Framework/EventLogging.xcdatamodeld/.xccurrentversion new file mode 100644 index 0000000..35de543 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/EventLogging.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ + + + + + _XCCurrentVersionName + EventLogging 2.xcdatamodel + + diff --git a/Apps/Wikipedia/WMF Framework/EventLogging.xcdatamodeld/EventLogging 2.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/EventLogging.xcdatamodeld/EventLogging 2.xcdatamodel/contents new file mode 100644 index 0000000..7ee9ae2 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/EventLogging.xcdatamodeld/EventLogging 2.xcdatamodel/contents @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/EventLogging.xcdatamodeld/EventLogging.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/EventLogging.xcdatamodeld/EventLogging.xcdatamodel/contents new file mode 100644 index 0000000..d597aee --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/EventLogging.xcdatamodeld/EventLogging.xcdatamodel/contents @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/EventLoggingService.swift b/Apps/Wikipedia/WMF Framework/EventLoggingService.swift new file mode 100644 index 0000000..3b8fdb7 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/EventLoggingService.swift @@ -0,0 +1,468 @@ +import Foundation +import CocoaLumberjackSwift + +enum EventLoggingError { + case generic + case network +} + +@objc(WMFEventLoggingService) +public class EventLoggingService : NSObject, URLSessionDelegate { + private struct Key { + static let isEnabled = "SendUsageReports" + static let appInstallID = "WMFAppInstallID" + static let lastLoggedSnapshot = "WMFLastLoggedSnapshot" + static let appInstallDate = "AppInstallDate" + static let loggedDaysInstalled = "DailyLoggingStatsDaysInstalled" + static let lastSuccessfulPost = "LastSuccessfulPost" + } + + private var pruningAge: TimeInterval = 60*60*24*30 // 30 days + private var sendOnWWANThreshold: TimeInterval = 24 * 60 * 60 + private var postBatchSize = 32 + private var postTimeout: TimeInterval = 60*2 // 2 minutes + private var postInterval: TimeInterval = 60*10 // 10 minutes + + private var debugDisableImmediateSend = false + + private let session: Session + + private let persistentStoreCoordinator: NSPersistentStoreCoordinator + private let managedObjectContext: NSManagedObjectContext + private let operationQueue: OperationQueue + + @objc(sharedInstance) public static let shared: EventLoggingService? = { + let fileManager = FileManager.default + var permanentStorageDirectory = fileManager.wmf_containerURL().appendingPathComponent("Event Logging", isDirectory: true) + var didGetDirectoryExistsError = false + do { + try fileManager.createDirectory(at: permanentStorageDirectory, withIntermediateDirectories: true, attributes: nil) + } catch let error { + DDLogError("EventLoggingService: Error creating permanent cache: \(error)") + } + do { + var values = URLResourceValues() + values.isExcludedFromBackup = true + try permanentStorageDirectory.setResourceValues(values) + } catch let error { + DDLogError("EventLoggingService: Error excluding from backup: \(error)") + } + + let permanentStorageURL = permanentStorageDirectory.appendingPathComponent("Events.sqlite") + DDLogDebug("EventLoggingService: Events persistent store: \(permanentStorageURL)") + + // SINGLETONTODO + let eventLoggingService = EventLoggingService(session: MWKDataStore.shared().session, permanentStorageURL: permanentStorageURL) + if let eventLoggingService = eventLoggingService { + MWKDataStore.shared().setupAbTestsController(withPersistenceService: eventLoggingService) + } + + return eventLoggingService + }() + + @objc + public func log(event: [String: Any], schema: String, revision: Int, wiki: String) { + let event: NSDictionary = ["event": event, "schema": schema, "revision": revision, "wiki": wiki] + logEvent(event) + } + + public init?(session: Session, permanentStorageURL: URL?) { + let bundle = Bundle.wmf + let modelURL = bundle.url(forResource: "EventLogging", withExtension: "momd")! + let model = NSManagedObjectModel(contentsOf: modelURL)! + let psc = NSPersistentStoreCoordinator(managedObjectModel: model) + let options = [NSMigratePersistentStoresAutomaticallyOption: NSNumber(booleanLiteral: true), NSInferMappingModelAutomaticallyOption: NSNumber(booleanLiteral: true)] + operationQueue = OperationQueue() + operationQueue.maxConcurrentOperationCount = 1 + self.session = session + if let storeURL = permanentStorageURL { + do { + try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options) + } catch { + do { + try FileManager.default.removeItem(at: storeURL) + } catch { + + } + do { + try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options) + } catch { + return nil + } + } + } else { + do { + try psc.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: options) + } catch { + return nil + } + } + + self.persistentStoreCoordinator = psc + self.managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + self.managedObjectContext.persistentStoreCoordinator = psc + super.init() + } + + + @objc + public func reset() { + self.resetSession() + self.resetInstall() + } + + @objc + func migrateShareUsageAndInstallIDToUserDefaults() { + let enabledNumber = libraryValue(for: Key.isEnabled) as? NSNumber + if enabledNumber != nil { + UserDefaults.standard.wmf_sendUsageReports = enabledNumber!.boolValue + } else { + UserDefaults.standard.wmf_sendUsageReports = false + } + + UserDefaults.standard.wmf_appInstallId = libraryValue(for: Key.appInstallID) as? String + } + + @objc + private func logEvent(_ event: NSDictionary) { + let now = NSDate() + perform { moc in + let record = NSEntityDescription.insertNewObject(forEntityName: "WMFEventRecord", into: self.managedObjectContext) as! EventRecord + record.event = event + record.recorded = now + record.userAgent = WikipediaAppUtils.versionedUserAgent() + + DDLogDebug("EventLoggingService: \(record.objectID) recorded!") + + self.save(moc) + } + } + + @objc + private func tryPostEvents(_ completion: (() -> Void)? = nil) { + let operation = AsyncBlockOperation { (operation) in + self.perform { moc in + let pruneFetch = NSFetchRequest(entityName: "WMFEventRecord") + pruneFetch.returnsObjectsAsFaults = false + + let pruneDate = Date().addingTimeInterval(-(self.pruningAge)) as NSDate + pruneFetch.predicate = NSPredicate(format: "(recorded < %@) OR (posted != nil) OR (failed == TRUE)", pruneDate) + let delete = NSBatchDeleteRequest(fetchRequest: pruneFetch) + delete.resultType = .resultTypeCount + + do { + let result = try self.managedObjectContext.execute(delete) + guard let deleteResult = result as? NSBatchDeleteResult else { + DDLogError("EventLoggingService: Could not read NSBatchDeleteResult") + return + } + + guard let count = deleteResult.result as? Int else { + DDLogError("EventLoggingService: Could not read NSBatchDeleteResult count") + return + } + if count > 0 { + DDLogInfo("EventLoggingService: Pruned \(count) events") + } + + } catch let error { + DDLogError("EventLoggingService: Error pruning events: \(error.localizedDescription)") + } + + let fetch: NSFetchRequest = EventRecord.fetchRequest() + fetch.sortDescriptors = [NSSortDescriptor(keyPath: \EventRecord.recorded, ascending: true)] + fetch.predicate = NSPredicate(format: "(posted == nil) AND (failed != TRUE)") + fetch.fetchLimit = self.postBatchSize + + var eventRecords: [EventRecord] = [] + + do { + eventRecords = try moc.fetch(fetch) + } catch let error { + DDLogError(error.localizedDescription) + } + + var wifiOnly = true + if let lastSuccessNumber = moc.wmf_keyValue(forKey: Key.lastSuccessfulPost)?.value as? NSNumber { + let now = CFAbsoluteTimeGetCurrent() + let interval = now - CFAbsoluteTime(lastSuccessNumber.doubleValue) + if interval > self.sendOnWWANThreshold { + wifiOnly = false + } + } + + if !eventRecords.isEmpty { + self.postEvents(eventRecords, onlyWiFi: wifiOnly, completion: { + operation.finish() + }) + } else { + operation.finish() + } + } + } + operationQueue.addOperation(operation) + guard let completion = completion else { + return + } + let completionBlockOp = BlockOperation(block: completion) + completionBlockOp.addDependency(operation) + operationQueue.addOperation(completion) + } + + private func perform(_ block: @escaping (_ moc: NSManagedObjectContext) -> Void) { + let moc = self.managedObjectContext + moc.perform { + block(moc) + } + } + + private func performAndWait(_ block: (_ moc: NSManagedObjectContext) -> Void) { + let moc = self.managedObjectContext + moc.performAndWait { + block(moc) + } + } + + private func asyncSave() { + perform { (moc) in + self.save(moc) + } + } + + private func postEvents(_ eventRecords: [EventRecord], onlyWiFi: Bool, completion: @escaping () -> Void) { + DDLogDebug("EventLoggingService: Posting \(eventRecords.count) events!") + + let taskGroup = WMFTaskGroup() + + var completedRecordIDs = Set() + var failedRecordIDs = Set() + + for record in eventRecords { + let moid = record.objectID + guard let payload = record.event else { + failedRecordIDs.insert(moid) + continue + } + taskGroup.enter() + let userAgent = record.userAgent ?? WikipediaAppUtils.versionedUserAgent() + submit(payload: payload, userAgent: userAgent, onlyWiFi: onlyWiFi) { (error) in + if let error = error { + if error != .network { + failedRecordIDs.insert(moid) + } + } else { + completedRecordIDs.insert(moid) + } + taskGroup.leave() + } + } + + + taskGroup.waitInBackground { + self.perform { moc in + let postDate = NSDate() + for moid in completedRecordIDs { + let mo = try? self.managedObjectContext.existingObject(with: moid) + guard let record = mo as? EventRecord else { + continue + } + record.posted = postDate + } + + for moid in failedRecordIDs { + let mo = try? self.managedObjectContext.existingObject(with: moid) + guard let record = mo as? EventRecord else { + continue + } + record.failed = true + } + if completedRecordIDs.count == eventRecords.count { + self.managedObjectContext.wmf_setValue(NSNumber(value: CFAbsoluteTimeGetCurrent()), forKey: Key.lastSuccessfulPost) + DDLogDebug("EventLoggingService: All records succeeded") + } else { + DDLogDebug("EventLoggingService: Some records failed") + } + self.save(moc) + completion() + } + } + } + + private func submit(payload: NSObject, userAgent: String, onlyWiFi: Bool, completion: @escaping (EventLoggingError?) -> Void) { + guard let url = Configuration.current.eventLoggingAPIURL(with: payload) else { + DDLogError("EventLoggingService: Could not create URL") + completion(EventLoggingError.generic) + return + } + + var request = URLRequest(url: url) + request.setValue(userAgent, forHTTPHeaderField: "User-Agent") + let session = onlyWiFi ? self.session.wifiOnlyURLSession : self.session.defaultURLSession + let task = session.dataTask(with: request, completionHandler: { (_, response, error) in + guard error == nil, + let httpResponse = response as? HTTPURLResponse, + httpResponse.statusCode / 100 == 2 else { + if let error = error as NSError?, error.domain == NSURLErrorDomain { + completion(EventLoggingError.network) + } else { + completion(EventLoggingError.generic) + } + return + } + completion(nil) + // DDLogDebug("EventLoggingService: event \(eventRecord.objectID) posted!") + }) + task.resume() + } + + // mark stored values + + private func save(_ moc: NSManagedObjectContext) { + guard moc.hasChanges else { + return + } + do { + try moc.save() + } catch let error { + DDLogError("Error saving EventLoggingService managedObjectContext: \(error)") + } + } + + private var semaphore = DispatchSemaphore(value: 1) + + private var libraryValueCache: [String: NSCoding] = [:] + public func libraryValue(for key: String) -> NSCoding? { + semaphore.wait() + defer { + semaphore.signal() + } + var value = libraryValueCache[key] + if value != nil { + return value + } + + performAndWait { moc in + value = managedObjectContext.wmf_keyValue(forKey: key)?.value + if value != nil { + libraryValueCache[key] = value + return + } + + if let legacyValue = UserDefaults.standard.object(forKey: key) as? NSCoding { + value = legacyValue + libraryValueCache[key] = legacyValue + managedObjectContext.wmf_setValue(legacyValue, forKey: key) + UserDefaults.standard.removeObject(forKey: key) + save(moc) + } + } + + return value + } + + public func setLibraryValue(_ value: NSCoding?, for key: String) { + semaphore.wait() + defer { + semaphore.signal() + } + libraryValueCache[key] = value + perform { moc in + self.managedObjectContext.wmf_setValue(value, forKey: key) + self.save(moc) + } + } + + @objc public var isEnabled: Bool { + return UserDefaults.standard.wmf_sendUsageReports + } + + @objc public var lastLoggedSnapshot: NSCoding? { + get { + return libraryValue(for: Key.lastLoggedSnapshot) + } + set { + setLibraryValue(newValue, for: Key.lastLoggedSnapshot) + } + } + + @objc public var appInstallDate: Date? { + get { + var value = libraryValue(for: Key.appInstallDate) as? Date + if value == nil { + value = Date() + setLibraryValue(value as NSDate?, for: Key.appInstallDate) + } + return value + } + set { + setLibraryValue(newValue as NSDate?, for: Key.appInstallDate) + } + } + + @objc public var loggedDaysInstalled: NSNumber? { + get { + return libraryValue(for: Key.loggedDaysInstalled) as? NSNumber + } + set { + setLibraryValue(newValue, for: Key.loggedDaysInstalled) + } + } + + private var _sessionID: String? + @objc public var sessionID: String? { + semaphore.wait() + defer { + semaphore.signal() + } + if _sessionID == nil { + _sessionID = UUID().uuidString + } + return _sessionID + } + + private var _sessionStartDate: Date? + @objc public var sessionStartDate: Date? { + semaphore.wait() + defer { + semaphore.signal() + } + if _sessionStartDate == nil { + _sessionStartDate = Date() + } + return _sessionStartDate + } + + @objc public func resetSession() { + semaphore.wait() + defer { + semaphore.signal() + } + _sessionID = nil + _sessionStartDate = Date() + } + + private func resetInstall() { + UserDefaults.standard.wmf_appInstallId = nil + lastLoggedSnapshot = nil + loggedDaysInstalled = nil + appInstallDate = nil + } +} + +extension EventLoggingService: PeriodicWorker { + public func doPeriodicWork(_ completion: @escaping () -> Void) { + tryPostEvents(completion) + } +} + +extension EventLoggingService: BackgroundFetcher { + public func performBackgroundFetch(_ completion: @escaping (UIBackgroundFetchResult) -> Void) { + doPeriodicWork { + completion(.noData) + } + } +} + +extension EventLoggingService: ABTestsPersisting { + +} diff --git a/Apps/Wikipedia/WMF Framework/EventLoggingStandardEventProviding.swift b/Apps/Wikipedia/WMF Framework/EventLoggingStandardEventProviding.swift new file mode 100644 index 0000000..3e828d6 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/EventLoggingStandardEventProviding.swift @@ -0,0 +1,29 @@ +@objc public protocol EventLoggingSearchSourceProviding { + var searchSource: String { get } +} + +@objc public protocol EventLoggingEventValuesProviding { + var eventLoggingCategory: EventLoggingCategory { get } + var eventLoggingLabel: EventLoggingLabel? { get } +} + +public protocol EventLoggingStandardEventProviding { + var standardEvent: [String: Any] { get } +} + +public extension EventLoggingStandardEventProviding where Self: EventLoggingFunnel { + var standardEvent: [String: Any] { + guard let aii = appInstallID, let si = sessionID else { + return ["event_dt": timestamp] + } + return ["app_install_id": aii, "session_id": si, "event_dt": timestamp] + } + + func wholeEvent(with event: [AnyHashable: Any]) -> [String: Any] { + guard let event = event as? [String: Any] else { + assertionFailure("Expected dictionary with keys of type String") + return [:] + } + return standardEvent.merging(event, uniquingKeysWith: { (first, _) in first }) + } +} diff --git a/Apps/Wikipedia/WMF Framework/EventRecord+CoreDataClass.swift b/Apps/Wikipedia/WMF Framework/EventRecord+CoreDataClass.swift new file mode 100644 index 0000000..3995d60 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/EventRecord+CoreDataClass.swift @@ -0,0 +1,7 @@ +import Foundation +import CoreData + +@objc(WMFEventRecord) +public class EventRecord: NSManagedObject { + +} diff --git a/Apps/Wikipedia/WMF Framework/EventRecord+CoreDataProperties.swift b/Apps/Wikipedia/WMF Framework/EventRecord+CoreDataProperties.swift new file mode 100644 index 0000000..6450c79 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/EventRecord+CoreDataProperties.swift @@ -0,0 +1,19 @@ +import Foundation +import CoreData + + +extension EventRecord { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "WMFEventRecord") + } + + @NSManaged public var event: NSObject? + @NSManaged public var userAgent: String? + @NSManaged public var recorded: NSDate? + @NSManaged public var posted: NSDate? + @NSManaged public var postAttempts: Int16 + @NSManaged public var failed: Bool + + +} diff --git a/Apps/Wikipedia/WMF Framework/ExploreCardCollectionViewCell.swift b/Apps/Wikipedia/WMF Framework/ExploreCardCollectionViewCell.swift new file mode 100644 index 0000000..8271960 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ExploreCardCollectionViewCell.swift @@ -0,0 +1,391 @@ +import UIKit + +public protocol CardContent { + var view: UIView! { get } + func contentHeight(forWidth: CGFloat) -> CGFloat +} + +// Allows the card background view to communicate with the cell to detect taps in the title area +// A Random article card navigates to different destinations depending on whether the area +// above or below the card content is tapped. +fileprivate protocol CardBackgroundViewDelegate: UIView { + func titleAreaYThreshold(for cardBackgroundView: CardBackgroundView) -> CGFloat // Return value in the coordinate system of the card background view + var titleAreaTapped: Bool { get set } +} +private class CardBackgroundView: UIView { + fileprivate weak var delegate: CardBackgroundViewDelegate? + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let delegate = delegate { + let yThreshold = delegate.titleAreaYThreshold(for: self) + delegate.titleAreaTapped = point.y < yThreshold + } + return super.hitTest(point, with: event) + } +} + +public protocol ExploreCardCollectionViewCellDelegate: AnyObject { + func exploreCardCollectionViewCellWantsCustomization(_ cell: ExploreCardCollectionViewCell) + func exploreCardCollectionViewCellWantsToUndoCustomization(_ cell: ExploreCardCollectionViewCell) +} + +public class ExploreCardCollectionViewCell: CollectionViewCell, CardBackgroundViewDelegate, Themeable { + private let titleLabel = UILabel() + private let subtitleLabel = UILabel() + public let customizationButton = UIButton() + private let undoButton = UIButton() + private let undoLabel = UILabel() + private let footerButton = AlignedImageButton() + public weak var delegate: ExploreCardCollectionViewCellDelegate? + private let cardBackgroundView = CardBackgroundView() + private let cardCornerRadius = Theme.exploreCardCornerRadius + private let cardShadowRadius = CGFloat(10) + private let cardShadowOffset = CGSize(width: 0, height: 2) + public var titleAreaTapped: Bool = false + + static let overflowImage = UIImage(named: "overflow") + + public override func setup() { + super.setup() + titleLabel.numberOfLines = 0 + contentView.addSubview(titleLabel) + subtitleLabel.numberOfLines = 0 + contentView.addSubview(subtitleLabel) + customizationButton.setImage(ExploreCardCollectionViewCell.overflowImage, for: .normal) + customizationButton.contentEdgeInsets = .zero + customizationButton.imageEdgeInsets = .zero + customizationButton.titleEdgeInsets = .zero + customizationButton.titleLabel?.textAlignment = .center + customizationButton.addTarget(self, action: #selector(customizationButtonPressed), for: .touchUpInside) + cardBackgroundView.layer.cornerRadius = cardCornerRadius + cardBackgroundView.layer.shadowOffset = cardShadowOffset + cardBackgroundView.layer.shadowRadius = cardShadowRadius + cardBackgroundView.layer.shadowColor = cardShadowColor.cgColor + cardBackgroundView.layer.shadowOpacity = cardShadowOpacity + cardBackgroundView.layer.masksToBounds = false + cardBackgroundView.isOpaque = true + cardBackgroundView.delegate = self + contentView.addSubview(cardBackgroundView) + contentView.addSubview(customizationButton) + footerButton.imageIsRightAligned = true + let image = #imageLiteral(resourceName: "places-more").imageFlippedForRightToLeftLayoutDirection() + footerButton.setImage(image, for: .normal) + footerButton.isUserInteractionEnabled = false + footerButton.titleLabel?.numberOfLines = 0 + footerButton.titleLabel?.textAlignment = .right + contentView.addSubview(footerButton) + undoLabel.numberOfLines = 0 + contentView.addSubview(undoLabel) + undoButton.titleLabel?.numberOfLines = 0 + undoButton.setTitle(CommonStrings.undo, for: .normal) + undoButton.addTarget(self, action: #selector(undoButtonPressed), for: .touchUpInside) + undoButton.isUserInteractionEnabled = true + undoButton.titleLabel?.textAlignment = .right + contentView.addSubview(undoButton) + } + + // This method is called to reset the cell to the default configuration. It is called on initial setup and prepareForReuse. Subclassers should call super. + override open func reset() { + super.reset() + layoutMargins = UIEdgeInsets(top: 15, left: 13, bottom: 15, right: 13) + footerButton.isHidden = true + undoButton.isHidden = true + undoLabel.isHidden = true + } + + public var cardContent: (CardContent & Themeable)? = nil { + didSet { + oldValue?.view?.removeFromSuperview() + guard let view = cardContent?.view else { + return + } + view.layer.cornerRadius = cardCornerRadius + contentView.addSubview(view) + } + } + + fileprivate func titleAreaYThreshold(for cardBackgroundView: CardBackgroundView) -> CGFloat { + // The title area is defined to include card background from its top down to the bottom of the card content + // This registers taps on the side margins of the card content as in the title area + let yThreshold = cardContent?.view?.frame.maxY ?? 0.0 + let convertedPoint = convert(CGPoint(x: 0.0, y: yThreshold), to: cardBackgroundView) + return convertedPoint.y + } + + private var undoTitle: String? { + didSet { + undoLabel.text = undoTitle + } + } + + public var footerTitle: String? { + get { + return footerButton.title(for: .normal) + } + set { + footerButton.setTitle(newValue, for: .normal) + footerButton.isHidden = newValue == nil + setNeedsLayout() + } + } + + public var title: String? { + get { + return titleLabel.text + } + set { + titleLabel.text = newValue + setNeedsLayout() + } + } + + public var subtitle: String? { + get { + return subtitleLabel.text + } + set { + subtitleLabel.text = newValue + setNeedsLayout() + } + } + + public var isCustomizationButtonHidden: Bool { + get { + return customizationButton.isHidden + } + set { + customizationButton.isHidden = newValue + setNeedsLayout() + } + } + + public var undoType: WMFContentGroupUndoType = .none { + didSet { + switch undoType { + case .contentGroup: + undoTitle = WMFLocalizedString("explore-feed-preferences-card-hidden-title", value: "Card hidden", comment: "Title for button that appears in place of feed card hidden by user via the overflow button") + isCollapsed = true + case .contentGroupKind: + guard let title = title else { + return + } + undoTitle = String.localizedStringWithFormat(WMFLocalizedString("explore-feed-preferences-feed-cards-hidden-title", value: "All %@ cards hidden", comment: "Title for cell that appears in place of feed card hidden by user via the overflow button - %@ is replaced with feed card type"), title) + isCollapsed = true + default: + isCollapsed = false + } + } + } + + private var isCollapsed: Bool = false { + didSet { + if isCollapsed { + undoLabel.isHidden = false + customizationButton.isHidden = true + undoButton.isHidden = false + cardContent?.view.isHidden = true + titleLabel.isHidden = true + subtitleLabel.isHidden = true + footerButton.isHidden = true + UIAccessibility.post(notification: UIAccessibility.Notification.layoutChanged, argument: undoButton) + } else { + cardContent?.view.isHidden = false + undoLabel.isHidden = true + undoButton.isHidden = true + titleLabel.isHidden = title == nil + subtitleLabel.isHidden = subtitle == nil + footerButton.isHidden = footerTitle == nil + } + setNeedsLayout() + } + } + + override public func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let size = super.sizeThatFits(size, apply: apply) // intentionally shade size + var origin = CGPoint(x: layoutMargins.left, y: layoutMargins.top) + let widthMinusMargins = size.width - layoutMargins.left - layoutMargins.right + let isRTL = traitCollection.layoutDirection == .rightToLeft + let labelHorizontalAlignment: HorizontalAlignment = isRTL ? .right : .left + let buttonHorizontalAlignment: HorizontalAlignment = isRTL ? .left : .right + + var customizationButtonDeltaWidthMinusMargins: CGFloat = 0 + if !customizationButton.isHidden { + let customizationButtonSize = CGSize(width: 50, height: 50) + let customizationButtonNudgeWidth = round(0.55 * customizationButtonSize.width) + customizationButtonDeltaWidthMinusMargins = customizationButtonNudgeWidth + if apply { + let originX = isRTL ? layoutMargins.left - customizationButtonSize.width + customizationButtonNudgeWidth : size.width - layoutMargins.right - customizationButtonNudgeWidth + let originY = origin.y - round(0.25 * customizationButtonSize.height) + let customizationButtonOrigin = CGPoint(x: originX, y: originY) + customizationButton.frame = CGRect(origin: customizationButtonOrigin, size: customizationButtonSize) + } + } + + var labelOrigin = origin + if isRTL { + labelOrigin.x += customizationButtonDeltaWidthMinusMargins + } + + if !titleLabel.isHidden { + origin.y += titleLabel.wmf_preferredHeight(at: labelOrigin, maximumWidth: widthMinusMargins - customizationButtonDeltaWidthMinusMargins, horizontalAlignment: labelHorizontalAlignment, spacing: 4, apply: apply) + labelOrigin.y = origin.y + } + if !subtitleLabel.isHidden { + origin.y += subtitleLabel.wmf_preferredHeight(at: labelOrigin, maximumWidth: widthMinusMargins - customizationButtonDeltaWidthMinusMargins, horizontalAlignment: labelHorizontalAlignment, spacing: 20, apply: apply) + } + + if let cardContent = cardContent, !cardContent.view.isHidden { + let view = cardContent.view + let height = cardContent.contentHeight(forWidth: widthMinusMargins) + let cardContentViewFrame = CGRect(origin: origin, size: CGSize(width: widthMinusMargins, height: height)) + if apply { + view?.frame = cardContentViewFrame + cardBackgroundView.frame = cardContentViewFrame.insetBy(dx: -cardBorderWidth, dy: -cardBorderWidth) + } + origin.y += cardContentViewFrame.height + } + + if isCollapsed, !undoLabel.isHidden, !undoButton.isHidden { + let undoOffset: UIOffset = UIOffset(horizontal: 15, vertical: 16) + labelOrigin.x += undoOffset.horizontal + labelOrigin.y += undoOffset.vertical + + let undoButtonMaxWidthPercentage: CGFloat = 0.25 + + let undoLabelMaxWidth = widthMinusMargins - (widthMinusMargins * undoButtonMaxWidthPercentage) + let undoLabelMinWidth = widthMinusMargins * 0.5 + let undoLabelX = isRTL ? widthMinusMargins - undoLabelMaxWidth : labelOrigin.x + let undoLabelFrameHeight = undoLabel.wmf_preferredHeight(at: CGPoint(x: undoLabelX, y: labelOrigin.y), maximumWidth: undoLabelMaxWidth, minimumWidth: undoLabelMinWidth, horizontalAlignment: labelHorizontalAlignment, spacing: 0, apply: apply) + + let undoButtonMaxWidth = widthMinusMargins * undoButtonMaxWidthPercentage + let undoButtonX = isRTL ? labelOrigin.x : widthMinusMargins - undoButtonMaxWidth + let undoButtonMinSize = CGSize(width: UIView.noIntrinsicMetric, height: undoLabelFrameHeight) + let undoButtonMaxSize = CGSize(width: undoButtonMaxWidth, height: UIView.noIntrinsicMetric) + let undoButtonFrame = undoButton.wmf_preferredFrame(at: CGPoint(x: undoButtonX, y: labelOrigin.y), maximumSize: undoButtonMaxSize, minimumSize: undoButtonMinSize, horizontalAlignment: buttonHorizontalAlignment, apply: apply) + let undoHeight = max(undoLabelFrameHeight, undoButtonFrame.height) + + // If cardBackgroundView metrics change double check the hitTest() override in CardBackgroundView + let cardBackgroundViewHeight = undoHeight + undoOffset.vertical * 2 + let cardBackgroundViewFrame = CGRect(x: layoutMargins.left, y: layoutMargins.top, width: widthMinusMargins, height: cardBackgroundViewHeight) + if apply { + cardBackgroundView.frame = cardBackgroundViewFrame + } + + origin.y += cardBackgroundViewFrame.height + } + + if !footerButton.isHidden { + origin.y += layoutMargins.bottom + origin.y += footerButton.wmf_preferredHeight(at: origin, maximumWidth: widthMinusMargins, horizontalAlignment: buttonHorizontalAlignment, spacing: 0, apply: apply) + } + + origin.y += layoutMargins.bottom + + let totalSize = CGSize(width: size.width, height: ceil(origin.y)) + + if apply { + cardBackgroundView.layer.shadowPath = UIBezierPath(roundedRect: cardBackgroundView.bounds, cornerRadius: cardBackgroundView.layer.cornerRadius).cgPath + } + + return totalSize + } + + public override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + titleLabel.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection) + subtitleLabel.font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection) + footerButton.titleLabel?.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection) + undoLabel.font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection) + undoButton.titleLabel?.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection) + customizationButton.titleLabel?.font = UIFont.wmf_font(.boldTitle1, compatibleWithTraitCollection: traitCollection) + } + + private var cardShadowColor: UIColor = .black { + didSet { + cardBackgroundView.layer.shadowColor = cardShadowColor.cgColor + } + } + + private var cardShadowOpacity: Float = 0 { + didSet { + guard cardBackgroundView.layer.shadowOpacity != cardShadowOpacity else { + return + } + cardBackgroundView.layer.shadowOpacity = cardShadowOpacity + } + } + + private var cardBorderWidth: CGFloat = 1 { + didSet { + cardBackgroundView.layer.borderWidth = cardBorderWidth + } + } + + public override func updateBackgroundColorOfLabels() { + super.updateBackgroundColorOfLabels() + titleLabel.backgroundColor = labelBackgroundColor + subtitleLabel.backgroundColor = labelBackgroundColor + footerButton.backgroundColor = labelBackgroundColor + undoLabel.backgroundColor = labelBackgroundColor + undoButton.backgroundColor = labelBackgroundColor + customizationButton.backgroundColor = labelBackgroundColor + } + + public func apply(theme: Theme) { + contentView.tintColor = theme.colors.link + let backgroundColor = isCollapsed ? theme.colors.cardButtonBackground : theme.colors.paperBackground + let selectedBackgroundColor = isCollapsed ? theme.colors.cardButtonBackground : theme.colors.midBackground + let cardBackgroundViewBorderColor = isCollapsed ? backgroundColor.cgColor : theme.colors.cardBorder.cgColor + cardBackgroundView.layer.borderColor = cardBackgroundViewBorderColor + setBackgroundColors(.clear, selected: selectedBackgroundColor) + titleLabel.textColor = theme.colors.primaryText + subtitleLabel.textColor = theme.colors.secondaryText + customizationButton.setTitleColor(theme.colors.link, for: .normal) + footerButton.setTitleColor(theme.colors.link, for: .normal) + undoLabel.textColor = theme.colors.primaryText + undoButton.setTitleColor(theme.colors.link, for: .normal) + updateSelectedOrHighlighted() + cardBackgroundView.backgroundColor = backgroundColor + cardShadowOpacity = theme.cardShadowOpacity + cardShadowColor = theme.colors.cardShadow + cardContent?.apply(theme: theme) + let displayScale = max(1, traitCollection.displayScale) + cardBorderWidth = CGFloat(theme.cardBorderWidthInPixels) / displayScale + } + + @objc func customizationButtonPressed() { + delegate?.exploreCardCollectionViewCellWantsCustomization(self) + } + + @objc func undoButtonPressed() { + delegate?.exploreCardCollectionViewCellWantsToUndoCustomization(self) + } + + // MARK: - Accessibility + + override open func updateAccessibilityElements() { + var updatedAccessibilityElements: [Any] = [] + + if isCollapsed { + updatedAccessibilityElements.append(undoLabel) + updatedAccessibilityElements.append(undoButton) + } else { + let groupedLabels = [titleLabel, subtitleLabel] + let customizeActionTitle = WMFLocalizedString("explore-feed-customize-accessibility-title", value: "Customize", comment: "Accessibility title for feed customization") + let customizeAction = UIAccessibilityCustomAction(name: customizeActionTitle, target: self, selector: #selector(customizationButtonPressed)) + updatedAccessibilityElements.append(LabelGroupAccessibilityElement(view: self, labels: groupedLabels, actions: [customizeAction])) + if let contentView = cardContent?.view { + updatedAccessibilityElements.append(contentView) + } + if !footerButton.isHidden, let label = footerButton.titleLabel { + let footerElement = UIAccessibilityElement(accessibilityContainer: self) + footerElement.isAccessibilityElement = true + footerElement.accessibilityLabel = label.text + footerElement.accessibilityTraits = UIAccessibilityTraits.link + footerElement.accessibilityFrameInContainerSpace = footerButton.frame + updatedAccessibilityElements.append(footerElement) + } + } + + accessibilityElements = updatedAccessibilityElements + } +} diff --git a/Apps/Wikipedia/WMF Framework/ExploreFeedPreferencesUpdateCoordinator.swift b/Apps/Wikipedia/WMF Framework/ExploreFeedPreferencesUpdateCoordinator.swift new file mode 100644 index 0000000..dff837b --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ExploreFeedPreferencesUpdateCoordinator.swift @@ -0,0 +1,110 @@ +@objc public class ExploreFeedPreferencesUpdateCoordinator: NSObject { + private unowned let feedContentController: WMFExploreFeedContentController + private var oldExploreFeedPreferences = [String: Any]() + private var newExploreFeedPreferences = [String: Any]() + private var willTurnOnContentGroupOrLanguage = false + private var updateFeed: Bool = true + + @objc public init(feedContentController: WMFExploreFeedContentController) { + self.feedContentController = feedContentController + } + + @objc public func configure(oldExploreFeedPreferences: [String: Any], newExploreFeedPreferences: [String: Any], willTurnOnContentGroupOrLanguage: Bool, updateFeed: Bool) { + self.oldExploreFeedPreferences = oldExploreFeedPreferences + self.newExploreFeedPreferences = newExploreFeedPreferences + self.willTurnOnContentGroupOrLanguage = willTurnOnContentGroupOrLanguage + self.updateFeed = updateFeed + } + + @objc public func coordinateUpdate(from viewController: UIViewController) { + if willTurnOnContentGroupOrLanguage { + guard UserDefaults.standard.defaultTabType == .settings else { + feedContentController.saveNewExploreFeedPreferences(newExploreFeedPreferences, apply: true, updateFeed: updateFeed) + return + } + guard areAllLanguagesTurnedOff(in: oldExploreFeedPreferences) else { + feedContentController.saveNewExploreFeedPreferences(newExploreFeedPreferences, apply: true, updateFeed: updateFeed) + return + } + guard areGlobalCardsTurnedOff(in: oldExploreFeedPreferences) else { + feedContentController.saveNewExploreFeedPreferences(newExploreFeedPreferences, apply: true, updateFeed: updateFeed) + return + } + present(turnOnExploreAlertController, from: viewController) + } else { + guard UserDefaults.standard.defaultTabType == .explore else { + feedContentController.saveNewExploreFeedPreferences(newExploreFeedPreferences, apply: true, updateFeed: updateFeed) + return + } + guard areAllLanguagesTurnedOff(in: newExploreFeedPreferences) else { + feedContentController.saveNewExploreFeedPreferences(newExploreFeedPreferences, apply: true, updateFeed: updateFeed) + return + } + guard areGlobalCardsTurnedOff(in: newExploreFeedPreferences) else { + feedContentController.saveNewExploreFeedPreferences(newExploreFeedPreferences, apply: true, updateFeed: updateFeed) + return + } + present(turnOffExploreAlertController, from: viewController) + } + } + + private lazy var turnOffExploreAlertController: UIAlertController = { + let alertController = UIAlertController(title: WMFLocalizedString("explore-feed-preferences-turn-off-explore-feed-alert-title", value: "Turn off Explore feed?", comment: "Title for alert that allows user to decide whether they want to turn off Explore feed"), message: WMFLocalizedString("explore-feed-preferences-turn-off-explore-feed-alert-message", value: "Hiding all Explore feed cards will turn off the Explore tab and replace it with a Settings tab", comment: "Message for alert that allows user to decide whether they want to turn off Explore feed"), preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: WMFLocalizedString("explore-feed-preferences-turn-off-explore-feed-alert-action-title", value: "Turn off Explore feed", comment: "Title for action alert that allows user to turn off Explore feed"), style: .destructive, handler: { _ in + UserDefaults.standard.defaultTabType = .settings + self.feedContentController.saveNewExploreFeedPreferences(self.newExploreFeedPreferences, apply: true, updateFeed: self.updateFeed) + })) + alertController.addAction(UIAlertAction(title: CommonStrings.cancelActionTitle, style: .cancel, handler: { _ in + self.feedContentController.rejectNewExploreFeedPreferences() + })) + return alertController + }() + + private lazy var turnOnExploreAlertController: UIAlertController = { + let alertController = UIAlertController(title: CommonStrings.turnOnExploreTabTitle, message: WMFLocalizedString("explore-feed-preferences-turn-on-explore-feed-alert-message", value: "By choosing to show Explore feed cards you are turning on the Explore tab", comment: "Message for alert that allows user to decide whether they want to turn on Explore feed"), preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: CommonStrings.turnOnExploreActionTitle, style: .default, handler: { _ in + self.feedContentController.saveNewExploreFeedPreferences(self.newExploreFeedPreferences, apply: true, updateFeed: self.updateFeed) + UserDefaults.standard.defaultTabType = .explore + })) + alertController.addAction(UIAlertAction(title: CommonStrings.cancelActionTitle, style: .cancel, handler: { _ in + self.feedContentController.rejectNewExploreFeedPreferences() + })) + return alertController + }() + + private func present(_ alertController: UIAlertController, from presenter: UIViewController) { + if let presenter = presenter.presentedViewController { + if presenter is UINavigationController { + presenter.present(alertController, animated: true) + } + } else { + presenter.present(alertController, animated: true) + } + } + + private func areAllLanguagesTurnedOff(in exploreFeedPreferences: [String: Any]) -> Bool { + guard exploreFeedPreferences.count == 1 else { + return false + } + guard exploreFeedPreferences.first?.key == WMFExploreFeedPreferencesGlobalCardsKey else { + assertionFailure("Expected value with key WMFExploreFeedPreferencesGlobalCardsKey") + return false + } + return true + } + + private func globalCardPreferences(in exploreFeedPreferences: [String: Any]) -> [NSNumber: NSNumber]? { + guard let globalCardPreferences = exploreFeedPreferences[WMFExploreFeedPreferencesGlobalCardsKey] as? [NSNumber: NSNumber] else { + assertionFailure("Expected value of type Dictionary") + return nil + } + return globalCardPreferences + } + + private func areGlobalCardsTurnedOff(in exploreFeedPreferences: [String: Any]) -> Bool { + guard let globalCardPreferences = globalCardPreferences(in: exploreFeedPreferences) else { + return false + } + return globalCardPreferences.values.filter { $0.boolValue == true }.isEmpty + } +} diff --git a/Apps/Wikipedia/WMF Framework/ExtensionViewController.swift b/Apps/Wikipedia/WMF Framework/ExtensionViewController.swift new file mode 100644 index 0000000..5dad440 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ExtensionViewController.swift @@ -0,0 +1,45 @@ +import UIKit + +open class ExtensionViewController: UIViewController, Themeable { + public final var theme: Theme = Theme.widgetLight + + open func apply(theme: Theme) { + self.theme = theme + } + + var isFirstLayout = true + + open override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + updateThemeFromTraitCollection(force: isFirstLayout) + isFirstLayout = false + } + + private func updateThemeFromTraitCollection(force: Bool = false) { + let compatibleTheme = Theme.widgetThemeCompatible(with: traitCollection) + guard theme !== compatibleTheme else { + if force { + apply(theme: theme) + } + return + } + apply(theme: compatibleTheme) + } + + override open func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateThemeFromTraitCollection() + } + + public func openAppInActivity(with activityType: WMFUserActivityType) { + self.extensionContext?.open(NSUserActivity.wmf_baseURLForActivity(of: activityType)) + } + + public func openApp(with url: URL?, fallback fallbackURL: URL? = nil) { + guard let wikipediaSchemeURL = url?.replacingSchemeWithWikipediaScheme ?? fallbackURL else { + openAppInActivity(with: .explore) + return + } + self.extensionContext?.open(wikipediaSchemeURL) + } +} diff --git a/Apps/Wikipedia/WMF Framework/FakeProgressController.swift b/Apps/Wikipedia/WMF Framework/FakeProgressController.swift new file mode 100644 index 0000000..bc78e8a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/FakeProgressController.swift @@ -0,0 +1,76 @@ +import UIKit + +public protocol FakeProgressReceiving { + var progress: Float { get } + func setProgress(_ progress: Float, animated: Bool) +} + +public protocol FakeProgressDelegate: AnyObject { + func setProgressHidden(_ hidden: Bool, animated: Bool) +} + +public class FakeProgressController: NSObject { + private let progress: FakeProgressReceiving + weak var delegate: FakeProgressDelegate? + public var minVisibleDuration: TimeInterval = 0.7 + public var delay: TimeInterval = 1.0 + + public init(progress: FakeProgressReceiving, delegate: FakeProgressDelegate?) { + self.progress = progress + self.delegate = delegate + } + + deinit { + NSObject.cancelPreviousPerformRequests(withTarget: self) + } + + // MARK: - Progress + + @objc private func incrementProgress() { + guard !isProgressHidden && progress.progress <= 0.69 else { + return + } + + let rand = 0.15 + Float(arc4random_uniform(15))/100 + progress.setProgress(progress.progress + rand, animated: true) + perform(#selector(incrementProgress), with: nil, afterDelay: 0.3) + } + + fileprivate var isProgressHidden: Bool = true + + @objc private func hideProgress() { + isProgressHidden = true + NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(incrementProgress), object: nil) + self.delegate?.setProgressHidden(true, animated: true) + } + + @objc private func showProgress() { + isProgressHidden = false + self.delegate?.setProgressHidden(false, animated: false) + } + + private func cancelPreviousShowsAndHides() { + NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(showProgress), object: nil) + NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(hideProgress), object: nil) + } + + public func start() { + assert(Thread.isMainThread) + cancelPreviousShowsAndHides() + perform(#selector(showProgress), with: nil, afterDelay: delay) + progress.setProgress(0, animated: false) + perform(#selector(incrementProgress), with: nil, afterDelay: 0.3) + } + + public func stop() { + assert(Thread.isMainThread) + cancelPreviousShowsAndHides() + perform(#selector(hideProgress), with: nil, afterDelay: minVisibleDuration) + } + + public func finish() { + progress.setProgress(1.0, animated: true) + stop() + } + +} diff --git a/Apps/Wikipedia/WMF Framework/FeatureFlags.swift b/Apps/Wikipedia/WMF Framework/FeatureFlags.swift new file mode 100644 index 0000000..5accde9 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/FeatureFlags.swift @@ -0,0 +1,9 @@ +import Foundation + +public struct FeatureFlags { + + public static var needsNewTalkPage: Bool { + return true + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Fetcher.swift b/Apps/Wikipedia/WMF Framework/Fetcher.swift new file mode 100644 index 0000000..7a599c8 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Fetcher.swift @@ -0,0 +1,514 @@ +import Foundation + +// Base class for combining a Session and Configuration to make network requests +// Session handles constructing and making requests, Configuration handles url structure for the current target + +// TODO: Standardize on returning CancellationKey or URLSessionTask +// TODO: Centralize cancellation and remove other cancellation implementations (ReadingListsAPI) +// TODO: Think about utilizing a request buildler instead of so many separate functions +// TODO: Utilize Result type where possible (Swift only) + +@objc(WMFFetcher) +open class Fetcher: NSObject { + @objc public let configuration: Configuration + @objc public let session: Session + + public typealias CancellationKey = String + + private var tasks = [String: URLSessionTask]() + private let semaphore = DispatchSemaphore.init(value: 1) + + @objc override public convenience init() { + let dataStore = MWKDataStore.shared() + self.init(session: dataStore.session, configuration: dataStore.configuration) + } + + @objc required public init(session: Session, configuration: Configuration) { + self.session = session + self.configuration = configuration + } + + @discardableResult public func requestMediaWikiAPIAuthToken(for URL: URL?, type: TokenType, cancellationKey: CancellationKey? = nil, completionHandler: @escaping (Result) -> Swift.Void) -> URLSessionTask? { + let parameters = [ + "action": "query", + "meta": "tokens", + "type": type.stringValue, + "format": "json" + ] + return performMediaWikiAPIPOST(for: URL, with: parameters) { (result, response, error) in + if let error = error { + completionHandler(Result.failure(error)) + return + } + guard + let query = result?["query"] as? [String: Any], + let tokens = query["tokens"] as? [String: Any], + let tokenValue = tokens[type.stringValue + "token"] as? String + else { + completionHandler(Result.failure(RequestError.unexpectedResponse)) + return + } + guard !tokenValue.isEmpty else { + completionHandler(Result.failure(RequestError.unexpectedResponse)) + return + } + completionHandler(Result.success(Token(value: tokenValue, type: type))) + } + } + + + @objc(requestMediaWikiAPIAuthToken:withType:cancellationKey:completionHandler:) + @discardableResult public func requestMediaWikiAPIAuthToken(for URL: URL?, with type: TokenType, cancellationKey: CancellationKey? = nil, completionHandler: @escaping (Token?, Error?) -> Swift.Void) -> URLSessionTask? { + return requestMediaWikiAPIAuthToken(for: URL, type: type, cancellationKey: cancellationKey) { (result) in + switch result { + case .failure(let error): + completionHandler(nil, error) + case .success(let token): + completionHandler(token, nil) + } + } + } + + @objc(performTokenizedMediaWikiAPIPOSTWithTokenType:toURL:withBodyParameters:cancellationKey:reattemptLoginOn401Response:completionHandler:) + @discardableResult public func performTokenizedMediaWikiAPIPOST(tokenType: TokenType = .csrf, to URL: URL?, with bodyParameters: [String: String]?, cancellationKey: CancellationKey? = nil, reattemptLoginOn401Response: Bool = true, completionHandler: @escaping ([String: Any]?, HTTPURLResponse?, Error?) -> Swift.Void) -> CancellationKey? { + let key = cancellationKey ?? UUID().uuidString + let task = requestMediaWikiAPIAuthToken(for: URL, type: tokenType, cancellationKey: key) { (result) in + switch result { + case .failure(let error): + completionHandler(nil, nil, error) + self.untrack(taskFor: key) + case .success(let token): + var mutableBodyParameters = bodyParameters ?? [:] + mutableBodyParameters[tokenType.parameterName] = token.value + self.performMediaWikiAPIPOST(for: URL, with: mutableBodyParameters, cancellationKey: key, reattemptLoginOn401Response: reattemptLoginOn401Response, completionHandler: completionHandler) + } + } + track(task: task, for: key) + return key + } + + @objc(performMediaWikiAPIPOSTForURL:withBodyParameters:cancellationKey:reattemptLoginOn401Response:completionHandler:) + @discardableResult public func performMediaWikiAPIPOST(for URL: URL?, with bodyParameters: [String: String]?, cancellationKey: CancellationKey? = nil, reattemptLoginOn401Response: Bool = true, completionHandler: @escaping ([String: Any]?, HTTPURLResponse?, Error?) -> Swift.Void) -> URLSessionTask? { + let url = configuration.mediaWikiAPIURLForURL(URL, with: nil) + let key = cancellationKey ?? UUID().uuidString + let task = session.postFormEncodedBodyParametersToURL(to: url, bodyParameters: bodyParameters, reattemptLoginOn401Response:reattemptLoginOn401Response) { (result, response, error) in + completionHandler(result, response, error) + self.untrack(taskFor: key) + } + track(task: task, for: key) + return task + } + + @objc(performMediaWikiAPIGETForURL:withQueryParameters:cancellationKey:completionHandler:) + @discardableResult public func performMediaWikiAPIGET(for URL: URL?, with queryParameters: [String: Any]?, cancellationKey: CancellationKey?, completionHandler: @escaping ([String: Any]?, HTTPURLResponse?, Error?) -> Swift.Void) -> URLSessionTask? { + let url = configuration.mediaWikiAPIURLForURL(URL, with: queryParameters) + let key = cancellationKey ?? UUID().uuidString + let task = session.getJSONDictionary(from: url) { (result, response, error) in + let returnError = error ?? RequestError.from(result?["error"] as? [String : Any]) + completionHandler(result, response, returnError) + self.untrack(taskFor: key) + } + track(task: task, for: key) + return task + } + + @objc(performMediaWikiAPIGETForURLRequest:cancellationKey:completionHandler:) + @discardableResult public func performMediaWikiAPIGET(for urlRequest: URLRequest, cancellationKey: CancellationKey?, completionHandler: @escaping ([String: Any]?, HTTPURLResponse?, Error?) -> Swift.Void) -> URLSessionTask? { + + let key = cancellationKey ?? UUID().uuidString + let task = session.getJSONDictionary(from: urlRequest) { (result, response, error) in + let returnError = error ?? RequestError.from(result?["error"] as? [String : Any]) + completionHandler(result, response, returnError) + self.untrack(taskFor: key) + } + track(task: task, for: key) + return task + } + +// MARK: Resolving MediaWiki Errors For Display + + /// Chain from MediaWiki API response if you want to resolve a set of error messages into a full html string for display. Use this method for raw dictionary responses. For Swift Codable responses, use resolveMediaWikiBlockedError(from apiErrors: [MediaWikiAPIError]...). + /// - Parameters: + /// - result: Serialized dictionary from MediaWiki API response + /// - completionHandler: Completion handler called when full html is determined, which is packaged up in a MediaWikiAPIDisplayError object. + @objc(resolveMediaWikiApiErrorFromResult:siteURL:completionHandler:) + func resolveMediaWikiApiErrorFromResult(_ result: [String: Any], siteURL: URL, completionHandler: @escaping (MediaWikiAPIDisplayError?) -> Void) { + + var apiErrors: [MediaWikiAPIError] = [] + + guard let errorsDict = result["errors"] as? [[String: Any]] else { + completionHandler(nil) + return + } + + for errorDict in errorsDict { + if let error = MediaWikiAPIError(dict: errorDict) { + apiErrors.append(error) + } + } + + resolveMediaWikiError(from: apiErrors, siteURL: siteURL, completion: completionHandler) + } + + /// Chain from MediaWiki API response if you want to resolve a set of error messages into a full html string for display. Use from Swift Codable responses that capture a collection of [MediaWikiAPIError] items. + /// - Parameters: + /// - apiErrors: Decoded MediaWikiAPIError items from API response + /// - completion: Called when full html is determined, which is packaged up in a MediaWikiAPIDisplayError object. + public func resolveMediaWikiError(from apiErrors: [MediaWikiAPIError], siteURL: URL, completion: @escaping (MediaWikiAPIDisplayError?) -> Void) { + + let protectedPageError = apiErrors.filter { $0.code.contains("protectedpage") } + let blockedApiErrors = apiErrors.filter { $0.code.contains("block") } + let firstBlockedApiErrorWithInfo = blockedApiErrors.first(where: { $0.data?.blockInfo != nil }) + let fallbackBlockedApiError = blockedApiErrors.first(where: { !$0.html.isEmpty }) + + let firstAbuseFilterError = apiErrors.first(where: { $0.code.contains("abusefilter") && !$0.html.isEmpty }) + + let fallbackCompletion: () -> Void = { + + guard let fallbackBlockedApiError else { + + guard let firstAbuseFilterError else { + completion(nil) + return + } + + let displayError = MediaWikiAPIDisplayError(messageHtml: firstAbuseFilterError.html, linkBaseURL: siteURL, code: firstAbuseFilterError.code) + completion(displayError) + return + } + + let displayError = MediaWikiAPIDisplayError(messageHtml: fallbackBlockedApiError.html, linkBaseURL: siteURL, code: fallbackBlockedApiError.code) + completion(displayError) + return + } + + if let blockedApiError = firstBlockedApiErrorWithInfo, + let blockedApiInfo = blockedApiError.data?.blockInfo { + resolveMediaWikiApiBlockError(siteURL: siteURL, code: blockedApiError.code, html: blockedApiError.html, blockInfo: blockedApiInfo) { displayError in + + guard let displayError = displayError else { + fallbackCompletion() + return + } + completion(displayError) + } + + } else if let protectedPageError = protectedPageError.first(where: {!$0.html.isEmpty}) { + let displayError = MediaWikiAPIDisplayError(messageHtml: protectedPageError.html, linkBaseURL: siteURL, code: protectedPageError.code) + completion(displayError) + + } else { + fallbackCompletion() + } + } + + private func resolveMediaWikiApiBlockError(siteURL: URL, code: String, html: String, blockInfo: MediaWikiAPIError.Data.BlockInfo, completionHandler: @escaping (MediaWikiAPIDisplayError?) -> Void) { + + // First turn blockReason into html, if needed + let group = DispatchGroup() + + var blockReasonHtml: String? + var templateHtml: String? + var templateSiteURL: URL? + + group.enter() + parseBlockReason(siteURL: siteURL, blockReason: blockInfo.blockReason) { text in + blockReasonHtml = text + group.leave() + } + + group.enter() + fetchBlockedTextTemplate(isPartial: blockInfo.blockPartial, siteURL: siteURL) { text, siteURL in + templateHtml = text + templateSiteURL = siteURL + group.leave() + } + + group.notify(queue: DispatchQueue.global(qos: .default)) { + + guard var templateHtml = templateHtml else { + completionHandler(nil) + return + } + + let linkBaseURL = templateSiteURL ?? siteURL + + // Replace encoded placeholders first, before replacing them with blocked text. + templateHtml = templateHtml.replacingOccurrences(of: "%241", with: "$1") + templateHtml = templateHtml.replacingOccurrences(of: "%242", with: "$2") + templateHtml = templateHtml.replacingOccurrences(of: "%243", with: "") // stripped out below + templateHtml = templateHtml.replacingOccurrences(of: "%244", with: "") // stripped out below + templateHtml = templateHtml.replacingOccurrences(of: "%245", with: "$5") + templateHtml = templateHtml.replacingOccurrences(of: "%246", with: "$6") + templateHtml = templateHtml.replacingOccurrences(of: "%247", with: "$7") + templateHtml = templateHtml.replacingOccurrences(of: "%248", with: "$8") + + // Replace placeholders with blocked text + templateHtml = templateHtml.replacingOccurrences(of: "$1", with: blockInfo.blockedBy) + + if let blockReasonHtml { + templateHtml = templateHtml.replacingOccurrences(of: "$2", with: blockReasonHtml) + } + + templateHtml = templateHtml.replacingOccurrences(of: "$3", with: "") // IP Address + templateHtml = templateHtml.replacingOccurrences(of: "$4", with: "") // unknown parameter (unused?) + + templateHtml = templateHtml.replacingOccurrences(of: "$5", with: String(blockInfo.blockID)) + + let blockExpiryDisplayDate = self.blockedDateForDisplay(iso8601DateString: blockInfo.blockExpiry, siteURL: linkBaseURL) + templateHtml = templateHtml.replacingOccurrences(of: "$6", with: blockExpiryDisplayDate) + + let username = MWKDataStore.shared().authenticationManager.loggedInUsername ?? "" + templateHtml = templateHtml.replacingOccurrences(of: "$7", with: username) + + let blockedTimestampDisplayDate = self.blockedDateForDisplay(iso8601DateString: blockInfo.blockedTimestamp, siteURL: linkBaseURL) + templateHtml = templateHtml.replacingOccurrences(of: "$8", with: blockedTimestampDisplayDate) + + let displayError = MediaWikiAPIDisplayError(messageHtml: templateHtml, linkBaseURL: linkBaseURL, code: code) + completionHandler(displayError) + } + + } + + private func blockedDateForDisplay(iso8601DateString: String, siteURL: URL) -> String { + var formattedDateString: String? = nil + if let date = (iso8601DateString as NSString).wmf_iso8601Date() { + + let dateFormatter = DateFormatter.wmf_localCustomShortDateFormatterWithTime(for: NSLocale.wmf_locale(for: siteURL.wmf_languageCode)) + + formattedDateString = dateFormatter?.string(from: date) + } + + return formattedDateString ?? "" + } + + private func parseBlockReason(attempt: Int = 1, siteURL: URL, blockReason: String, completion: @escaping (String?) -> Void) { + + let params: [String: Any] = [ + "action": "parse", + "prop": "text", + "mobileformat": 1, + "text": blockReason, + "errorformat": "html", + "erroruselocal": 1, + "format": "json", + "formatversion": 2 + ] + + performMediaWikiAPIGET(for: siteURL, with: params, cancellationKey: nil) { [weak self] result, response, error in + + + guard let parse = result?["parse"] as? [String: Any], + let text = parse["text"] as? String else { + + // If unable to find, try app language once. Otherwise return nil. + guard attempt == 1 else { + completion(nil) + return + } + + guard let appLangSiteURL = MWKDataStore.shared().languageLinkController.appLanguage?.siteURL else { + completion(nil) + return + } + + self?.parseBlockReason(attempt: attempt + 1, siteURL: appLangSiteURL, blockReason: blockReason, completion: completion) + return + } + + completion(text) + } + } + + private func fetchBlockedTextTemplate(isPartial: Bool = false, attempt: Int = 1, siteURL: URL, completion: @escaping (String?, URL) -> Void) { + + // Note: Not enough languages seem to have MediaWiki:Blockedtext-partial, so forcing MediaWiki:Blockedtext for now. + + let templateName = "MediaWiki:Blockedtext" + if let parseText = templateName.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { + let params: [String: Any] = [ + "action": "parse", + "prop": "text", + "mobileformat": 1, + "page": parseText, + "errorformat": "html", + "erroruselocal": 1, + "format": "json", + "formatversion": 2 + ] + + performMediaWikiAPIGET(for: siteURL, with: params, cancellationKey: nil) { [weak self] result, response, error in + + guard let parse = result?["parse"] as? [String: Any], + let text = parse["text"] as? String else { + + // If unable to find, try app language once. Otherwise return nil. + guard attempt == 1 else { + completion(nil, siteURL) + return + } + + guard let appLangSiteURL = MWKDataStore.shared().languageLinkController.appLanguage?.siteURL else { + completion(nil, siteURL) + return + } + + self?.fetchBlockedTextTemplate(isPartial: isPartial, attempt: attempt + 1, siteURL: appLangSiteURL, completion: completion) + return + } + + completion(text, siteURL) + } + } + } + +// MARK: Decodable + + @discardableResult public func performDecodableMediaWikiAPIGET(for URL: URL?, with queryParameters: [String: Any]?, cancellationKey: CancellationKey? = nil, completionHandler: @escaping (Result) -> Swift.Void) -> CancellationKey? { + let url = configuration.mediaWikiAPIURLForURL(URL, with: queryParameters) + let key = cancellationKey ?? UUID().uuidString + let task = session.jsonDecodableTask(with: url) { (result: T?, response: URLResponse?, error: Error?) in + guard let result = result else { + let error = error ?? RequestError.unexpectedResponse + completionHandler(.failure(error)) + return + } + completionHandler(.success(result)) + self.untrack(taskFor: key) + } + track(task: task, for: key) + return key + } + + /// Creates and kicks off a URLSessionTask, tracking it in case the session needs to cancel it later. From fetchers, prefer using this method over calling session.jsonDecodableTask directly as it ensures the task is tracked and uses the result type + @discardableResult public func trackedJSONDecodableTask(with urlRequest: URLRequest, completionHandler: @escaping (Result, HTTPURLResponse?) -> Swift.Void) -> URLSessionTask? { + + let key = UUID().uuidString + let task = session.jsonDecodableTask(with: urlRequest) { (result: T?, response: URLResponse?, error: Error?) in + defer { + self.untrack(taskFor: key) + } + guard let result = result else { + completionHandler(.failure(error ?? RequestError.unexpectedResponse), response as? HTTPURLResponse) + return + } + completionHandler(.success(result), response as? HTTPURLResponse) + } + + track(task: task, for: key) + return task + } + +// MARK: Tracking + + @objc(trackTask:forKey:) + public func track(task: URLSessionTask?, for key: String) { + guard let task = task else { + return + } + semaphore.wait() + tasks[key] = task + semaphore.signal() + } + + @objc(untrackTaskForKey:) + public func untrack(taskFor key: String) { + semaphore.wait() + tasks.removeValue(forKey: key) + semaphore.signal() + } + + @objc(cancelTaskForKey:) + public func cancel(taskFor key: String) { + semaphore.wait() + tasks[key]?.cancel() + tasks.removeValue(forKey: key) + semaphore.signal() + } + + @objc(cancelAllTasks) + public func cancelAllTasks() { + semaphore.wait() + for (_, task) in tasks { + task.cancel() + } + tasks.removeAll(keepingCapacity: true) + semaphore.signal() + } +} + +// MARK: Modern Swift Concurrency APIs + +extension Fetcher { + + public func performDecodableMediaWikiAPIGet(for URL: URL, with queryParameters: [String: Any]?) async throws -> T { + guard let url = configuration.mediaWikiAPIURLForURL(URL, with: queryParameters) else { + throw RequestError.invalidParameters + } + + let (data, response) = try await session.data(for: url) + + guard let httpResponse = (response as? HTTPURLResponse) else { + throw RequestError.unexpectedResponse + } + + guard HTTPStatusCode.isSuccessful(httpResponse.statusCode) else { + throw RequestError.http(httpResponse.statusCode) + } + + return try JSONDecoder().decode(T.self, from: data) + } +} + +// These are for bridging to Obj-C only +@objc public extension Fetcher { + @objc class var unexpectedResponseError: NSError { + return RequestError.unexpectedResponse as NSError + } + @objc class var invalidParametersError: NSError { + return RequestError.invalidParameters as NSError + } + @objc class var noNewDataError: NSError { + return RequestError.noNewData as NSError + } + @objc class var cancelledError: NSError { + return NSError(domain: NSCocoaErrorDomain, code: NSUserCancelledError, userInfo: [NSLocalizedDescriptionKey: RequestError.unexpectedResponse.localizedDescription]) + } +} + +@objc(WMFTokenType) +public enum TokenType: Int { + case csrf, login, createAccount + var stringValue: String { + switch self { + case .login: + return "login" + case .createAccount: + return "createaccount" + case .csrf: + return "csrf" + } + } + var parameterName: String { + switch self { + case .login: + return "logintoken" + case .createAccount: + return "createtoken" + default: + return "token" + } + } +} + +@objc(WMFToken) +public class Token: NSObject { + @objc public var value: String + @objc public var type: TokenType + public var isAuthorized: Bool + @objc init(value: String, type: TokenType) { + self.value = value + self.type = type + self.isAuthorized = value != "+\\" + } +} diff --git a/Apps/Wikipedia/WMF Framework/FileManager+CacheExtensions.swift b/Apps/Wikipedia/WMF Framework/FileManager+CacheExtensions.swift new file mode 100644 index 0000000..d85fc40 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/FileManager+CacheExtensions.swift @@ -0,0 +1,21 @@ +import Foundation + +extension FileManager { + /// DEPRECATED: This should only be used for migrating legacy data to the new cache format. The app should no longer need to store information in extended file attributes. Previously, this was used to store the MIME type of a cached file so the approrpriate Content-Type could be set when serving it as a cached response. Now, the response headers are cached in a separate file, removing the need to attach MIME type (or any other info) to the file itself. This function can be removed when the corresponding migration is removed. + /// - Parameter attributeName: Extended file attribute name + /// - Parameter path: Path of the file + /// - Returns: the attribute value for the given attributeName or nil if no such attribute exists on the file + func getValueForExtendedFileAttributeNamed(_ attributeName: String, forFileAtPath path: String) -> String? { + let name = (attributeName as NSString).utf8String + let path = (path as NSString).fileSystemRepresentation + + let bufferLength = getxattr(path, name, nil, 0, 0, 0) + + guard bufferLength != -1, let buffer = malloc(bufferLength) else { + return nil + } + + let readLen = getxattr(path, name, buffer, bufferLength, 0, 0) + return String(bytesNoCopy: buffer, length: readLen, encoding: .utf8, freeWhenDone: true) + } +} diff --git a/Apps/Wikipedia/WMF Framework/Gradient.swift b/Apps/Wikipedia/WMF Framework/Gradient.swift new file mode 100644 index 0000000..ebadc47 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Gradient.swift @@ -0,0 +1,29 @@ +import UIKit + +@objc(WMFGradient) +public class Gradient: NSObject { + fileprivate var r1: CGFloat = 0 + fileprivate var g1: CGFloat = 0 + fileprivate var b1: CGFloat = 0 + fileprivate var a1: CGFloat = 0 + + fileprivate var r2: CGFloat = 0 + fileprivate var g2: CGFloat = 0 + fileprivate var b2: CGFloat = 0 + fileprivate var a2: CGFloat = 0 + + init(startColor: UIColor, endColor: UIColor) { + startColor.getRed(&r1, green: &g1, blue: &b1, alpha: &a1) + endColor.getRed(&r2, green: &g2, blue: &b2, alpha: &a2) + super.init() + } + + @objc(colorAtPercentage:) + public final func color(at percentage: CGFloat) -> UIColor { + let r = r1 + percentage * (r2 - r1) + let g = g1 + percentage * (g2 - g1) + let b = b1 + percentage * (b2 - b1) + let a = a1 + percentage * (a2 - a1) + return UIColor(red: r, green: g, blue: b, alpha: a) + } +} diff --git a/Apps/Wikipedia/WMF Framework/HTTPStatusCode.swift b/Apps/Wikipedia/WMF Framework/HTTPStatusCode.swift new file mode 100644 index 0000000..d792731 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/HTTPStatusCode.swift @@ -0,0 +1,7 @@ +import Foundation + +public struct HTTPStatusCode { + public static func isSuccessful(_ statusCode: Int) -> Bool { + return statusCode >= 200 && statusCode <= 299 + } +} diff --git a/Apps/Wikipedia/WMF Framework/HasText.swift b/Apps/Wikipedia/WMF Framework/HasText.swift new file mode 100644 index 0000000..95cdde1 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/HasText.swift @@ -0,0 +1,140 @@ +extension String { + public var wmf_hasText: Bool { + return !isEmpty + } + public var wmf_hasNonWhitespaceText: Bool { + return wmf_hasText && !self.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + } + public var wmf_hasAlphanumericText: Bool { + return wmf_hasText && (self.components(separatedBy: .alphanumerics).count > 1) + } +} + +extension NSAttributedString { + public var wmf_hasText: Bool { + return string.wmf_hasText + } + public var wmf_hasNonWhitespaceText: Bool { + return string.wmf_hasNonWhitespaceText + } +} + +extension UILabel { + public var wmf_hasText: Bool { + guard let text = text else { + return false + } + return text.wmf_hasText + } + + @objc public var wmf_hasNonWhitespaceText: Bool { + guard let text = text else { + return false + } + return text.wmf_hasNonWhitespaceText + } + + public var wmf_hasAttributedText: Bool { + guard let attributedText = attributedText else { + return false + } + return attributedText.wmf_hasText + } + + public var wmf_hasNonWhitespaceAttributedText: Bool { + guard let attributedText = attributedText else { + return false + } + return attributedText.wmf_hasNonWhitespaceText + } + + public var wmf_hasAnyText: Bool { + return wmf_hasText || wmf_hasAttributedText + } + + public var wmf_hasAnyNonWhitespaceText: Bool { + return wmf_hasNonWhitespaceText || wmf_hasNonWhitespaceAttributedText + } +} + +extension UITextView { + public var wmf_hasAnyNonWhitespaceText: Bool { + return text?.wmf_hasNonWhitespaceText ?? false || attributedText?.wmf_hasNonWhitespaceText ?? false + } +} + +extension UIButton { + public var wmf_hasText: Bool { + guard let label = titleLabel else { + return false + } + return label.wmf_hasText + } + + public var wmf_hasNonWhitespaceText: Bool { + guard let label = titleLabel else { + return false + } + return label.wmf_hasNonWhitespaceText + } + + public var wmf_hasAttributedText: Bool { + guard let label = titleLabel else { + return false + } + return label.wmf_hasText + } + + public var wmf_hasNonWhitespaceAttributedText: Bool { + guard let label = titleLabel else { + return false + } + return label.wmf_hasNonWhitespaceText + } + + public var wmf_hasAnyText: Bool { + return wmf_hasText || wmf_hasAttributedText + } + + public var wmf_hasAnyNonWhitespaceText: Bool { + return wmf_hasNonWhitespaceText || wmf_hasNonWhitespaceAttributedText + } +} + +extension UITextField { + public var wmf_hasText: Bool { + guard let text = text else { + return false + } + return text.wmf_hasText + } + + public var wmf_hasNonWhitespaceText: Bool { + guard let text = text else { + return false + } + return text.wmf_hasNonWhitespaceText + } + + public var wmf_hasAttributedText: Bool { + guard let attributedText = attributedText else { + return false + } + return attributedText.wmf_hasText + } + + public var wmf_hasNonWhitespaceAttributedText: Bool { + guard let attributedText = attributedText else { + return false + } + return attributedText.wmf_hasNonWhitespaceText + } + + public var wmf_hasAnyText: Bool { + return wmf_hasText || wmf_hasAttributedText + } + + public var wmf_hasAnyNonWhitespaceText: Bool { + return wmf_hasNonWhitespaceText || wmf_hasNonWhitespaceAttributedText + } +} diff --git a/Apps/Wikipedia/WMF Framework/ImageCacheController.swift b/Apps/Wikipedia/WMF Framework/ImageCacheController.swift new file mode 100644 index 0000000..fa03cc2 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ImageCacheController.swift @@ -0,0 +1,312 @@ +import Foundation + +enum ImageCacheControllerError: Error { + case failureGeneratingUniqueKey +} + +public final class ImageCacheController: CacheController { + // MARK: Permanent Cache + + // batch inserts to db and selectively decides which file variant to download. Used when inserting multiple image urls from media-list endpoint via ArticleCacheController. + func add(urls: [URL], groupKey: GroupKey, individualCompletion: @escaping IndividualCompletionBlock, groupCompletion: @escaping GroupCompletionBlock) { + + // todo: could use DRY gatekeeper logic with superclass. this will likely get refactored out so holding off. + if gatekeeper.shouldQueueAddCompletion(groupKey: groupKey) { + gatekeeper.queueAddCompletion(groupKey: groupKey) { + self.add(urls: urls, groupKey: groupKey, individualCompletion: individualCompletion, groupCompletion: groupCompletion) + return + } + } else { + gatekeeper.addCurrentlyAddingGroupKey(groupKey) + } + + if gatekeeper.numberOfQueuedGroupCompletions(for: groupKey) > 0 { + gatekeeper.queueGroupCompletion(groupKey: groupKey, groupCompletion: groupCompletion) + return + } + + gatekeeper.queueGroupCompletion(groupKey: groupKey, groupCompletion: groupCompletion) + + dbWriter.add(urls: urls, groupKey: groupKey) { [weak self] (result) in + self?.finishDBAdd(groupKey: groupKey, individualCompletion: individualCompletion, groupCompletion: groupCompletion, result: result) + } + } + + // MARK: Logic taken from ImageController + + private let imageFetcher: ImageFetcher + fileprivate let memoryCache: NSCache + + init(moc: NSManagedObjectContext, session: Session, configuration: Configuration) { + self.imageFetcher = ImageFetcher(session: session, configuration: configuration) + memoryCache = NSCache() + memoryCache.totalCostLimit = 10000000 // pixel count + let fileWriter = CacheFileWriter(fetcher: imageFetcher) + let dbWriter = ImageCacheDBWriter(imageFetcher: imageFetcher, cacheBackgroundContext: moc) + super.init(dbWriter: dbWriter, fileWriter: fileWriter) + } + + struct FetchResult { + let data: Data + let response: URLResponse + } + + private var dataCompletionManager = ImageControllerCompletionManager() + + // called when saving an image to persistent cache has completed. Hook here to allow additional saving into memoryCache. + override func finishFileSave(data: Data, mimeType: String?, uniqueKey: CacheController.UniqueKey, url: URL) { + + guard let image = self.createImage(data: data, mimeType: mimeType) else { + return + } + + addToMemoryCache(image, url: url) + } + + // MARK: Errors + public enum FetchError: Error { + case dataNotFound + case invalidOrEmptyURL + case invalidImageCache + case invalidResponse + case duplicateRequest + case fileError + case dbError + case `deinit` + } + + // MARK: Fetching + + /// Fetches data from a given URL. Coalesces completion blocks so the same data isn't requested multiple times. + + func fetchData(withURL url: URL, priority: Float, failure: @escaping (Error) -> Void, success: @escaping (Data, URLResponse) -> Void) -> String? { + + guard let uniqueKey = imageFetcher.uniqueKeyForURL(url, type: .image) else { + failure(ImageCacheControllerError.failureGeneratingUniqueKey) + return nil + } + + let token = UUID().uuidString + let completion = ImageControllerDataCompletion(success: success, failure: failure) + dataCompletionManager.add(completion, priority: priority, forIdentifier: uniqueKey, token: token) { [weak self] (isFirst) in + guard let self = self else { + return + } + guard isFirst else { + return + } + let schemedURL = (url as NSURL).wmf_urlByPrependingSchemeIfSchemeless() as URL + let acceptAnyContentType = ["Accept": "*/*"] + let task = self.imageFetcher.dataForURL(schemedURL, persistType: .image, headers: acceptAnyContentType) { [weak self] (result) in + guard let self = self else { + return + } + switch result { + case .failure(let error): + guard !self.isCancellationError(error) else { + return + } + default: + break + } + + self.dataCompletionManager.complete(uniqueKey, enumerator: { (completion) in + // DDLogDebug("complete: \(url) \(token)") + switch result { + case .success(let result): + completion.success(result.data, result.response) + case .failure(let error): + completion.failure(error) + } + }) + } + + if let task = task { + task.priority = priority + self.dataCompletionManager.add(task, forIdentifier: uniqueKey) + task.resume() + } + + } + return token + } + + func fetchData(withURL url: URL, failure: @escaping (Error) -> Void, success: @escaping (Data, URLResponse) -> Void) { + _ = fetchData(withURL: url, priority: URLSessionTask.defaultPriority, failure: failure, success: success) + } + + /// Fetches an image from a given URL. Coalesces completion blocks so the same data isn't requested multiple times. + public func fetchImage(withURL url: URL?, priority: Float, failure: @escaping (Error) -> Void, success: @escaping (ImageDownload) -> Void) -> String? { + assert(Thread.isMainThread) + guard let url = url else { + failure(FetchError.invalidOrEmptyURL) + return nil + } + if let memoryCachedImage = memoryCachedImage(withURL: url) { + success(ImageDownload(url: url, image: memoryCachedImage, origin: .memory)) + return nil + } + return fetchData(withURL: url, priority: priority, failure: failure) { (data, response) in + guard let image = self.createImage(data: data, mimeType: response.mimeType) else { + DispatchQueue.main.async { + failure(FetchError.invalidResponse) + } + return + } + self.addToMemoryCache(image, url: url) + DispatchQueue.main.async { + success(ImageDownload(url: url, image: image, origin: .unknown)) + } + } + } + + public func fetchImage(withURL url: URL?, failure: @escaping (Error) -> Void, success: @escaping (ImageDownload) -> Void) { + _ = fetchImage(withURL: url, priority: URLSessionTask.defaultPriority, failure: failure, success: success) + } + + public func cancelFetch(withURL url: URL?, token: String?) { + + guard let url = url, + let uniqueKey = imageFetcher.uniqueKeyForURL(url, type: .image), + let token = token else { + return + } + + dataCompletionManager.cancel(uniqueKey, token: token) + } + + /// Populate the cache for a given URL + public func prefetch(withURL url: URL?) { + prefetch(withURL: url) { } + } + + /// Populate the cache for a given URL + public func prefetch(withURL url: URL?, completion: @escaping () -> Void) { + _ = fetchImage(withURL: url, priority: URLSessionTask.lowPriority, failure: { (error) in }) { (download) in } + } + + // MARK: Cache + + /// Retrieves the image with the given url from the memory or file cache + public func cachedImage(withURL url: URL?) -> Image? { + guard let url = url else { + return nil + } + + if let memoryCachedImage = memoryCachedImage(withURL: url) { + return memoryCachedImage + } + + guard let typedImageData = data(withURL: url), + let data = typedImageData.data, + let image = createImage(data: data, mimeType: typedImageData.MIMEType) else { + return nil + } + + addToMemoryCache(image, url: url) + return image + } + + /// Retrieves the image data from the file cache + func data(withURL url: URL) -> TypedImageData? { + guard let response = imageFetcher.cachedResponseForURL(url, type: .image) else { + return TypedImageData(data: nil, MIMEType: nil) + } + + return TypedImageData(data: response.data, MIMEType: response.response.mimeType) + } + + // MARK: Memory Cache + + /// Retrieves the image with the given url from the memory cache + public func memoryCachedImage(withURL url: URL) -> Image? { + + guard let uniqueKey = imageFetcher.uniqueKeyForURL(url, type: .image) else { + return nil + } + + return memoryCache.object(forKey: uniqueKey as NSString) + } + + func addToMemoryCache(_ image: Image, url: URL) { + + guard let uniqueKey = imageFetcher.uniqueKeyForURL(url, type: .image) else { + return + } + + memoryCache.setObject(image, forKey: (uniqueKey as NSString), cost: Int(image.staticImage.size.width * image.staticImage.size.height)) + } + + // MARK: Utilities + + private func createImage(data: Data, mimeType: String?) -> Image? { + if mimeType == "image/gif", let animatedImage = FLAnimatedImage.wmf_animatedImage(with: data), let staticImage = animatedImage.wmf_staticImage { + return Image(staticImage: staticImage, animatedImage: animatedImage) + } + guard let source = CGImageSourceCreateWithData(data as CFData, nil), CGImageSourceGetCount(source) > 0 else { + return nil + } + let options = [kCGImageSourceShouldCache as String: NSNumber(value: true)] as CFDictionary + guard let cgImage = CGImageSourceCreateImageAtIndex(source, 0, options) else { + return nil + } + let image: UIImage + if let orientation = getUIImageOrientation(from: source, options: options) { + image = UIImage(cgImage: cgImage, scale: 1, orientation: orientation) + } else { + image = UIImage(cgImage: cgImage) + } + return Image(staticImage: image, animatedImage: nil) + } + + private func getUIImageOrientation(from imageSource: CGImageSource, options: CFDictionary) -> UIImage.Orientation? { + guard + let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, options) as? [String: Any], + let orientationRawValue = properties[kCGImagePropertyOrientation as String] as? UInt32, + let cgOrientation = CGImagePropertyOrientation(rawValue: orientationRawValue) + else { + return nil + } + switch cgOrientation { + case .up: return .up + case .upMirrored: return .upMirrored + case .down: return .down + case .downMirrored: return .downMirrored + case .left: return .left + case .leftMirrored: return .leftMirrored + case .right: return .right + case .rightMirrored: return .rightMirrored + } + } + + public override func cancelAllTasks() { + super.cancelAllTasks() + dataCompletionManager.cancelAll() + } +} + +private extension ImageCacheController { + func isCancellationError(_ error: Error?) -> Bool { + return error?.isCancellationError ?? false + } +} + +fileprivate extension Error { + var isCancellationError: Bool { + let potentialCancellationError = self as NSError + return potentialCancellationError.domain == NSURLErrorDomain && potentialCancellationError.code == NSURLErrorCancelled + } +} + +@objc(WMFTypedImageData) +open class TypedImageData: NSObject { + @objc public let data:Data? + @objc public let MIMEType:String? + + @objc public init(data data_: Data?, MIMEType type_: String?) { + data = data_ + MIMEType = type_ + } +} + +let WMFExtendedFileAttributeNameMIMEType = "org.wikimedia.MIMEType" diff --git a/Apps/Wikipedia/WMF Framework/ImageCacheDBWriter.swift b/Apps/Wikipedia/WMF Framework/ImageCacheDBWriter.swift new file mode 100644 index 0000000..dabc64a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ImageCacheDBWriter.swift @@ -0,0 +1,183 @@ +import Foundation + +enum ImageCacheDBWriterError: Error { + case batchURLInsertFailure + case missingExpectedItemsOutOfRequestHeader + case unableToDetermineURLRequest +} + +final class ImageCacheDBWriter: CacheDBWriting { + + let context: NSManagedObjectContext + private let imageFetcher: ImageFetcher + + var fetcher: CacheFetching { + return imageFetcher + } + + var groupedTasks: [String : [IdentifiedTask]] = [:] + + init(imageFetcher: ImageFetcher, cacheBackgroundContext: NSManagedObjectContext) { + self.imageFetcher = imageFetcher + self.context = cacheBackgroundContext + } + + func add(url: URL, groupKey: CacheController.GroupKey, completion: @escaping (CacheDBWritingResultWithURLRequests) -> Void) { + + let acceptAnyContentType = ["Accept": "*/*"] + guard let urlRequest = imageFetcher.urlRequestFromPersistence(with: url, persistType: .image, headers: acceptAnyContentType) else { + completion(.failure(ImageCacheDBWriterError.unableToDetermineURLRequest)) + return + } + + cacheImages(groupKey: groupKey, urlRequests: [urlRequest], completion: completion) + } + + func add(urls: [URL], groupKey: CacheController.GroupKey, completion: @escaping (CacheDBWritingResultWithURLRequests) -> Void) { + + let acceptAnyContentType = ["Accept": "*/*"] + let urlRequests = urls.compactMap { imageFetcher.urlRequestFromPersistence(with: $0, persistType: .image, headers: acceptAnyContentType) } + + cacheImages(groupKey: groupKey, urlRequests: urlRequests, completion: completion) + } + + func markDownloaded(urlRequest: URLRequest, response: HTTPURLResponse?, completion: @escaping (CacheDBWritingResult) -> Void) { + + guard let itemKey = fetcher.itemKeyForURLRequest(urlRequest) else { + completion(.failure(CacheDBWritingMarkDownloadedError.unableToDetermineItemKey)) + return + } + + let variant = fetcher.variantForURLRequest(urlRequest) + + context.perform { + guard let cacheItem = CacheDBWriterHelper.cacheItem(with: itemKey, variant: variant, in: self.context) else { + completion(.failure(CacheDBWritingMarkDownloadedError.cannotFindCacheItem)) + return + } + cacheItem.isDownloaded = true + CacheDBWriterHelper.save(moc: self.context) { (result) in + switch result { + case .success: + completion(.success) + case .failure(let error): + completion(.failure(error)) + } + } + } + } + + func shouldDownloadVariant(itemKey: CacheController.ItemKey, variant: String?) -> Bool { + + guard let variant = variant else { + return true + } + + var result: Bool = false + context.performAndWait { + + let allVariantCacheItems = CacheDBWriterHelper.allVariantItems(itemKey: itemKey, in: self.context) + let allVariantItems = allVariantCacheItems.compactMap { return CacheController.ItemKeyAndVariant(itemKey: $0.key, variant: $0.variant) } + + result = shouldDownloadVariantForAllVariantItems(variant: variant, allVariantItems) + } + + return result + } + + func shouldDownloadVariantForAllVariantItems(variant: String?, _ allVariantItems: [CacheController.ItemKeyAndVariant]) -> Bool { + + guard let variant = variant else { + return true + } + + var sortableVariantItems = allVariantItems + + sortableVariantItems.sortAsImageItemKeyAndVariants() + + switch (UIScreen.main.scale, sortableVariantItems.count) { + case (1.0, _), (_, 1): + guard let firstVariant = sortableVariantItems.first?.variant else { + return true + } + return variant == firstVariant + case (2.0, _): + guard let secondVariant = sortableVariantItems[safeIndex: 1]?.variant else { + return true + } + return variant == secondVariant + case (3.0, _): + guard let lastVariant = sortableVariantItems.last?.variant else { + return true + } + return variant == lastVariant + default: + return false + } + } +} + + +private extension ImageCacheDBWriter { + func cacheImages(groupKey: String, urlRequests: [URLRequest], completion: @escaping (CacheDBWritingResultWithURLRequests) -> Void) { + context.perform { + + let dispatchGroup = DispatchGroup() + var successRequests: [URLRequest] = [] + var errorRequests: [URLRequest] = [] + + for urlRequest in urlRequests { + + dispatchGroup.enter() + + guard let url = urlRequest.url, + let itemKey = self.imageFetcher.itemKeyForURLRequest(urlRequest) else { + errorRequests.append(urlRequest) + dispatchGroup.leave() + continue + } + + let variant = self.imageFetcher.variantForURLRequest(urlRequest) + + guard let group = CacheDBWriterHelper.fetchOrCreateCacheGroup(with: groupKey, in: self.context) else { + errorRequests.append(urlRequest) + dispatchGroup.leave() + continue + } + + guard let item = CacheDBWriterHelper.fetchOrCreateCacheItem(with: url, itemKey: itemKey, variant: variant, in: self.context) else { + errorRequests.append(urlRequest) + dispatchGroup.leave() + continue + } + + item.variant = variant + group.addToCacheItems(item) + + CacheDBWriterHelper.save(moc: self.context) { (result) in + + defer { + dispatchGroup.leave() + } + + switch result { + case .success: + successRequests.append(urlRequest) + case .failure: + errorRequests.append(urlRequest) + } + } + } + + dispatchGroup.notify(queue: DispatchQueue.global(qos: .userInitiated)) { + + if errorRequests.count > 0 && successRequests.count == 0 { + completion(.failure(ImageCacheDBWriterError.batchURLInsertFailure)) + return + } + + completion(.success(successRequests)) + } + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/ImageFetcher.swift b/Apps/Wikipedia/WMF Framework/ImageFetcher.swift new file mode 100644 index 0000000..2591acb --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ImageFetcher.swift @@ -0,0 +1,5 @@ +import Foundation + +public final class ImageFetcher: Fetcher, CacheFetching { + +} diff --git a/Apps/Wikipedia/WMF Framework/Info.plist b/Apps/Wikipedia/WMF Framework/Info.plist new file mode 100644 index 0000000..c588027 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 7.3.0 + CFBundleVersion + 0 + NSPrincipalClass + + + diff --git a/Apps/Wikipedia/WMF Framework/LabelGroupAccessibilityElement.swift b/Apps/Wikipedia/WMF Framework/LabelGroupAccessibilityElement.swift new file mode 100644 index 0000000..5f0f754 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/LabelGroupAccessibilityElement.swift @@ -0,0 +1,42 @@ +import UIKit + +public class LabelGroupAccessibilityElement: UIAccessibilityElement { + let labels: [UILabel] + weak var view: UIView? + + public init(view: UIView, labels: [UILabel], actions: [UIAccessibilityCustomAction]) { + self.labels = labels + self.view = view + super.init(accessibilityContainer: view) + isAccessibilityElement = true + accessibilityTraits = UIAccessibilityTraits.link + accessibilityCustomActions = actions + update() + } + + func update() { + guard let firstLabel = labels.first else { + return + } + var combinedLabel: String = "" + if let accessibilityLabel = firstLabel.accessibilityLabel { + combinedLabel = accessibilityLabel + } else if let text = firstLabel.text { + combinedLabel = text + } + var combinedFrame = firstLabel.frame + for label in labels[1.. 0 { + combinedLabel.append("\n") + combinedLabel.append(labelLine) + } + } + self.accessibilityFrameInContainerSpace = combinedFrame + self.accessibilityLabel = combinedLabel + } +} diff --git a/Apps/Wikipedia/WMF Framework/Licenses.swift b/Apps/Wikipedia/WMF Framework/Licenses.swift new file mode 100644 index 0000000..1e2cd09 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Licenses.swift @@ -0,0 +1,14 @@ +import Foundation + +@objc(WMFLicenses) +@objcMembers public class Licenses: NSObject { + public static let localizedCCZEROTitle = WMFLocalizedString("cc-zero", value: "Creative Commons CC0", comment: "Name of the CC Zero license - https://creativecommons.org/publicdomain/zero/1.0/") + public static let localizedSaveTermsTitle = WMFLocalizedString("wikitext-upload-save-terms-name", value: "Terms of Use", comment: "This message is used in the message [[Wikimedia:Wikipedia-ios-wikitext-upload-save-terms-and-license]].") + + + public static let CCBYSA3URL = URL(string: "https://creativecommons.org/licenses/by-sa/3.0/") + public static let CCZEROURL = URL(string: "https://creativecommons.org/publicdomain/zero/1.0/") + public static let GFDLURL = URL(string: "https://www.gnu.org/licenses/fdl.html") + public static let saveTermsURL = URL(string:"https://foundation.wikimedia.org/wiki/Terms_of_Use") +} + diff --git a/Apps/Wikipedia/WMF Framework/Localization/Localization.swift b/Apps/Wikipedia/WMF Framework/Localization/Localization.swift new file mode 100644 index 0000000..357f714 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Localization/Localization.swift @@ -0,0 +1,5 @@ +import Foundation + +public func WMFLocalizedString(_ key: String, languageCode wikipediaLanguageCode: String? = nil, bundle: Bundle = Bundle.wmf_localization, value: String, comment: String) -> String { + return WMFLocalizedStringWithDefaultValue(key, wikipediaLanguageCode, bundle, value, comment) +} diff --git a/Apps/Wikipedia/WMF Framework/Localization/README.md b/Apps/Wikipedia/WMF Framework/Localization/README.md new file mode 100644 index 0000000..3c7ebe0 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Localization/README.md @@ -0,0 +1 @@ +This folder exists so these files aren't run through the localization script. They generate errors that can be ignored, but trip up the build system. \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/Localization/WMFLocalization.h b/Apps/Wikipedia/WMF Framework/Localization/WMFLocalization.h new file mode 100644 index 0000000..82a892c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Localization/WMFLocalization.h @@ -0,0 +1,12 @@ +@import Foundation; + +NS_ASSUME_NONNULL_BEGIN + +// Don't rename this function. It'll break the script that generates the strings. https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/LoadingResources/Strings/Strings.html +NSString *WMFLocalizedStringWithDefaultValue(NSString *key, NSString *_Nullable wikipediaLanguageCode, NSBundle *_Nullable bundle, NSString *value, NSString *comment); + +@interface NSBundle (WMFLocalization) +@property (class, readonly, strong) NSBundle *wmf_localizationBundle; +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/Localization/WMFLocalization.m b/Apps/Wikipedia/WMF Framework/Localization/WMFLocalization.m new file mode 100644 index 0000000..b8cdc51 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Localization/WMFLocalization.m @@ -0,0 +1,125 @@ +#import +#import + +@implementation NSBundle (WMFLocalization) + +// A mapping of language variant content codes to available native `NSLocale` bundle identifiers. The values map to existing .lproj folders. +NSDictionary *variantContentCodeToLocalizationBundleMapping = @{ + // Chinese variants + @"zh-hk": @"zh-hant", + @"zh-mo": @"zh-hant", + @"zh-my": @"zh-hans", + @"zh-sg": @"zh-hans", + @"zh-tw": @"zh-hant", + + // Serbian variants + // no-op - both variants are natively available iOS localizations + + // Kurdish variants + @"ku-arab": @"ckb", + + // Tajik variants + @"tg-latn": @"tg", + + // Uzbek variants + @"uz-cyrl": @"uz", +}; + ++ (NSBundle *)wmf_localizationBundle { + return [NSBundle wmf]; +} + ++ (nonnull NSMutableDictionary *)wmf_languageBundles { + static dispatch_once_t onceToken; + static NSMutableDictionary *wmf_languageBundles; + dispatch_once(&onceToken, ^{ + wmf_languageBundles = [NSMutableDictionary new]; + }); + return wmf_languageBundles; +} + +- (nonnull NSString *)wmf_languageBundleNameForWikipediaLanguageCode:(nonnull NSString *)languageCode { + NSString *bundleName = languageCode; + if ([variantContentCodeToLocalizationBundleMapping valueForKey:languageCode]) { + bundleName = [variantContentCodeToLocalizationBundleMapping valueForKey:languageCode]; + } else if ([languageCode isEqualToString:@"zh"]) { + bundleName = @"zh-hans"; + for (NSString *code in [NSLocale wmf_preferredLanguageCodes]) { + if (![code hasPrefix:@"zh"]) { + continue; + } + NSArray *components = [code componentsSeparatedByString:@"-"]; + if ([components count] == 2) { + bundleName = [code lowercaseString]; + break; + } + } + } else if ([languageCode isEqualToString:@"sr"]) { + bundleName = @"sr-ec"; + } else if ([languageCode isEqualToString:@"no"]) { + bundleName = @"nb"; + } + return bundleName; +} + +- (nullable NSBundle *)wmf_languageBundleForWikipediaLanguageCode:(nonnull NSString *)languageCode { + NSMutableDictionary *bundles = [NSBundle wmf_languageBundles]; + NSBundle *bundle = bundles[languageCode]; + if (!bundle) { + NSString *languageBundleName = [self wmf_languageBundleNameForWikipediaLanguageCode:languageCode]; + NSArray *paths = [self pathsForResourcesOfType:@"lproj" inDirectory:nil]; + NSString *filename = [[languageBundleName lowercaseString] stringByAppendingPathExtension:@"lproj"]; + NSString *path = nil; + for (NSString *possiblePath in paths) { + if (![[possiblePath lowercaseString] hasSuffix:filename]) { + continue; + } + path = possiblePath; + break; + } + if (!path) { + return nil; + } + bundle = [NSBundle bundleWithPath:path]; + if (bundle) { + bundles[languageCode] = bundle; + } + } + return bundle; +} + +- (nullable NSBundle *)wmf_fallbackLanguageBundle { + static dispatch_once_t onceToken; + static NSBundle *wmf_fallbackLanguageBundle; + dispatch_once(&onceToken, ^{ + NSString *path = [self pathForResource:@"en" ofType:@"lproj"]; + wmf_fallbackLanguageBundle = [NSBundle bundleWithPath:path]; + }); + return wmf_fallbackLanguageBundle; +} + +@end + +NSString *WMFLocalizedStringWithDefaultValue(NSString *key, NSString *_Nullable wikipediaLanguageCode, NSBundle *_Nullable bundle, NSString *value, NSString *comment) { + if (bundle == nil) { + bundle = NSBundle.wmf_localizationBundle; + } + + NSString *translation = nil; + if (wikipediaLanguageCode == nil) { + translation = [bundle localizedStringForKey:key value:nil table:nil]; + } else { + NSBundle *languageBundle = [bundle wmf_languageBundleForWikipediaLanguageCode:wikipediaLanguageCode]; + translation = [languageBundle localizedStringForKey:key value:nil table:nil]; + + if (!translation || [translation isEqualToString:key] || (translation.length == 0)) { + translation = [bundle localizedStringForKey:key value:nil table:nil]; + } + } + + if (!translation || [translation isEqualToString:key] || (translation.length == 0)) { + translation = [[bundle wmf_fallbackLanguageBundle] localizedStringForKey:key value:value table:nil]; + } + + return translation ? translation : @""; +} diff --git a/Apps/Wikipedia/WMF Framework/LocationManager.swift b/Apps/Wikipedia/WMF Framework/LocationManager.swift new file mode 100644 index 0000000..ec23bbd --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/LocationManager.swift @@ -0,0 +1,253 @@ +import UIKit +import CoreLocation +import CocoaLumberjackSwift + +public struct LocationManagerConfiguration { + let accuracy: CLLocationAccuracy + let filter: CLLocationDistance + var activityType: CLActivityType = .fitness +} + +extension LocationManagerConfiguration { + /// A configuration preset for fine location updates. + public static let fine = LocationManagerConfiguration( + accuracy: kCLLocationAccuracyBest, + filter: 1 + ) + + /// A configuration preset for coarse location updates. + public static let coarse = LocationManagerConfiguration( + accuracy: kCLLocationAccuracyKilometer, + filter: 1000 + ) +} + +// MARK: - LocationManagerDelegate + +@objc public protocol LocationManagerDelegate: AnyObject { + @objc(locationManager:didUpdateLocation:) optional + func locationManager(_ locationManager: LocationManagerProtocol, didUpdate location: CLLocation) + @objc(locationManager:didUpdateHeading:) optional + func locationManager(_ locationManager: LocationManagerProtocol, didUpdate heading: CLHeading) + @objc(locationManager:didReceiveError:) optional + func locationManager(_ locationManager: LocationManagerProtocol, didReceive error: Error) + @objc(locationManager:didUpdateAuthorizedState:) optional + func locationManager(_ locationManager: LocationManagerProtocol, didUpdateAuthorized authorized: Bool) +} + +final public class LocationManager: NSObject { + + /// The latest known location. + public private(set) lazy var location: CLLocation? = self.locationManager.location + + /// The latest known heading. + public private(set) lazy var heading: CLHeading? = self.locationManager.heading + + /// Returns `true` when the location and heading monitoring is active. + public private(set) var isUpdating = false + + /// Set the delegate if you want to receive location and heading updates. + public weak var delegate: LocationManagerDelegate? + + /// Returns the current locationManager permission state. + public var authorizationStatus: CLAuthorizationStatus { locationManager.authorizationStatus } + + /// Starts monitoring location and heading updates. + public func startMonitoringLocation() { + + guard isUpdating == false else { + return + } + + guard authorizationStatus != .notDetermined else { + authorize(success: startMonitoringLocation) + DDLogVerbose("LocationManager - skip monitoring location because status is \(authorizationStatus.rawValue).") + return + } + + guard authorizationStatus.isAuthorized else { + // Start monitoring location in case the authorization status ever changes to authorized. + authorizedCompletion = startMonitoringLocation + return + } + + locationManager.startUpdatingLocation() + startUpdatingHeading() + isUpdating = true + DDLogDebug("LocationManager - did start updating location & heading.") + } + + /// Stops monitoring location and heading updates. + public func stopMonitoringLocation() { + locationManager.stopUpdatingLocation() + stopUpdatingHeading() + isUpdating = false + DDLogDebug("LocationManager - did stop updating location & heading.") + } + + + /// Creates a new instance of `LocationManager`. + /// + /// - Parameters: + /// - locationManager: If needed, provide a custom `CLLocationManager` instance. The default + /// value creates a new instance. + /// - device: If needed, provide a custom `UIDevice` instance. The default value is `UIDevice.current`. + /// - configuration: The configuration used for configuring `CLLocationManager`. The default value + /// is the `.fine` preset. + /// + public init( + locationManager: CLLocationManager = .init(), + device: UIDevice = .current, + configuration: LocationManagerConfiguration = .fine + ) { + assert(Thread.isMainThread, "Location managers must be created on the main thread") + + locationManager.distanceFilter = configuration.filter + locationManager.desiredAccuracy = configuration.accuracy + locationManager.activityType = configuration.activityType + self.locationManager = locationManager + self.device = device + super.init() + locationManager.delegate = self + } + + deinit { + stopMonitoringLocation() + locationManager.delegate = nil + } + + // MARK: - Private + + private let locationManager: CLLocationManager + private let device: UIDevice + + // MARK: - Authorization + + /// The completion closure invoked when `authorizationStatus` changes to `authorizedAlways` + /// or `authorizedWhenInUse`. + private var authorizedCompletion: (() -> Void)? + + private func authorize(success: (() -> Void)? = nil) { + authorizedCompletion = success + locationManager.requestWhenInUseAuthorization() + DDLogInfo("LocationManager - requesting authorization to access location when in use.") + } + + // MARK: - Heading + + /// The observer token for `UIDevice.orientationDidChangeNotification`. + private var orientationObserver: NSObjectProtocol? + + private func startUpdatingHeading() { + guard !isUpdating else { return } + device.beginGeneratingDeviceOrientationNotifications() + updateHeadingOrientation() + locationManager.startUpdatingHeading() + + orientationObserver = NotificationCenter.default.addObserver( + forName: UIDevice.orientationDidChangeNotification, + object: nil, + queue: nil + ) { [weak self] _ in + self?.updateHeadingOrientation() + } + } + + private func stopUpdatingHeading() { + guard isUpdating else { return } + device.endGeneratingDeviceOrientationNotifications() + locationManager.stopUpdatingHeading() + + if let observer = orientationObserver { + NotificationCenter.default.removeObserver(observer) + orientationObserver = nil + } + } + + private func updateHeadingOrientation() { + locationManager.headingOrientation = device.orientation.clOrientation + } +} + +// MARK: - LocationManagerDelegate + +extension LocationManager: CLLocationManagerDelegate { + + public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + guard isUpdating, let location = locations.last else { return } + + self.location = location + delegate?.locationManager?(self, didUpdate: location) + DDLogVerbose("LocationManager - did update location: \(location).") + } + + public func locationManager(_ manager: CLLocationManager, didUpdateHeading heading: CLHeading) { + guard isUpdating, heading.headingAccuracy > 0 else { return } + + self.heading = heading + delegate?.locationManager?(self, didUpdate: heading) + DDLogVerbose("LocationManager - did update heading: \(heading).") + } + + public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { + guard isUpdating else { + DDLogVerbose("LocationManager - suppressing error received after call to stop monitoring location: \(error)") + return + } + + #if targetEnvironment(simulator) + let nsError = error as NSError + guard !(nsError.domain == kCLErrorDomain && nsError.code == CLError.locationUnknown.rawValue) else { + return + } + #endif + + delegate?.locationManager?(self, didReceive: error) + DDLogError("LocationManager - encountered error: \(error).") + } + + public func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { + let status = manager.authorizationStatus + DDLogInfo("LocationManager - did change authorization status \(status.rawValue).") + delegate?.locationManager?(self, didUpdateAuthorized: status.isAuthorized) + + if status.isAuthorized { + authorizedCompletion?() + authorizedCompletion = nil + } + } +} + +// MARK: LocationManagerProtocol + +extension LocationManager: LocationManagerProtocol { + + public var isAuthorized: Bool { authorizationStatus.isAuthorized } +} + +public extension CLAuthorizationStatus { + var isAuthorized: Bool { + self == .authorizedAlways || self == .authorizedWhenInUse + } +} + +private extension UIDeviceOrientation { + var clOrientation: CLDeviceOrientation { + switch self { + case .portrait: + return .portrait + case .portraitUpsideDown: + return .portraitUpsideDown + case .landscapeLeft: + return .landscapeLeft + case .landscapeRight: + return .landscapeRight + case .faceUp: + return .faceUp + case .faceDown: + return .faceDown + default: + return .unknown + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/LocationManagerFactory.swift b/Apps/Wikipedia/WMF Framework/LocationManagerFactory.swift new file mode 100644 index 0000000..5555338 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/LocationManagerFactory.swift @@ -0,0 +1,7 @@ +import Foundation + +@objc public final class LocationManagerFactory: NSObject { + @objc static func coarseLocationManager() -> LocationManagerProtocol { + return LocationManager(configuration: .coarse) + } +} diff --git a/Apps/Wikipedia/WMF Framework/LocationManagerProtocol.swift b/Apps/Wikipedia/WMF Framework/LocationManagerProtocol.swift new file mode 100644 index 0000000..b34ebc4 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/LocationManagerProtocol.swift @@ -0,0 +1,25 @@ +import CoreLocation + +// TODO: Remove these helpers after `WMFNearbyContentSource` is refactored to Swift and `LocationManager` +// is used only from Swift. It will also be possible to remove all @objc names from the +// `LocationManagerDelegate` declaration. + +@objc public protocol LocationManagerProtocol { + /// Last known location + var location: CLLocation? { get } + /// Last known heading + var heading: CLHeading? { get } + /// Return `true` in case when monitoring location, in other case return `false` + var isUpdating: Bool { get } + /// Delegate for update location manager + var delegate: LocationManagerDelegate? { get set } + /// Get current locationManager permission state + var authorizationStatus: CLAuthorizationStatus { get } + /// Return `true` if user is aurthorized or authorized always + var isAuthorized: Bool { get } + + /// Start monitoring location and heading updates. + func startMonitoringLocation() + /// Stop monitoring location and heading updates. + func stopMonitoringLocation() +} diff --git a/Apps/Wikipedia/WMF Framework/MWKDataStore+LanguageVariantMigration.swift b/Apps/Wikipedia/WMF Framework/MWKDataStore+LanguageVariantMigration.swift new file mode 100644 index 0000000..60d1b27 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/MWKDataStore+LanguageVariantMigration.swift @@ -0,0 +1,202 @@ +import Foundation +import CocoaLumberjackSwift + +/* Whenever a language that previously did not have variants becomes a language with variants, a migration must happen. + * + * There are two parts to the migration: + * 1. Updating persisted items like settings and Core Data records + * 2. Presenting alerts to the user to make them aware of the new variants + * + * 1. Migrating persistent items + * The first part of this process updates the various settings and user defaults that reference languages and ensure + * that the correct language variant is set. So, a value such as "zh" for Chinese is replaced with a variant such as "zh-hans". + * + * Note that once a language is converted, the 'plain' language code is a valid value meaning to use the 'mixed' + * content for that site. This is the content as entered into that site without converting to any variant. + * Because the plain language code means one thing before migration (the language itself) and another thing after + * migration (the mixed or untransformed variant of the language), migration should only happen once for a given + * language. + * + * If additional languages add variants in the future, a new library version should be used and a new entry mapping the + * library version to the newly variant-aware languages should be added to -newlyAddedVariantLanguageCodes(for:). + * The migration code itself should call migrateToLanguageVariants(for:in:) with the new library version. + * + * + * 2. Presenting informational alerts + * When migrating to use language variants, if the user's preferred languages include languages which have + * received variant support, an alert is presented to tell the user about variants. An alert is only presented for + * newly variant-aware languges that also are preferred languages of the user. Multiple alerts shown + * sequentially are possible, but expected to be rare. + * + * The method languageCodesNeedingVariantAlerts(since:) returns all language codes requiring a variant alert since the + * provided library version. Note that while migrating data is done library version by library version, this API can handle + * multiple library version updates at once. Also note that the method only returns those language codes that are also + * in the user's list of preferred languages. So, it is expected that this method will return an empty array for a user + * with no variant-aware languages in their list of preferred languages. + */ + +extension MWKDataStore { + @objc(migrateToLanguageVariantsForLibraryVersion:inManagedObjectContext:) + public func migrateToLanguageVariants(for libraryVersion: Int, in moc: NSManagedObjectContext) { + let languageCodes = newlyAddedVariantLanguageCodes(for: libraryVersion) + + // Map all languages with variants being migrated to the user's preferred variant + // Note that even if the user does not have any preferred languages that match, + // the user could have chosen to read or save an article in any language. + // The variant is therefore determined for all langauges being migrated. + let migrationMapping = languageCodes.reduce(into: [String:String]()) { (result, languageCode) in + guard let languageVariantCode = NSLocale.wmf_bestLanguageVariantCodeForLanguageCode(languageCode) else { + assertionFailure("No variant found for language code \(languageCode). Every language migrating to use language variants should return a language variant code") + return + } + result[languageCode] = languageVariantCode + } + + // Ensure any settings that currently use 'nb' are updated to use 'no' + var languageCodeMigrationMapping = migrationMapping + languageCodeMigrationMapping["nb"] = "no" + + languageLinkController.migratePreferredLanguages(toLanguageVariants: languageCodeMigrationMapping, in: moc) + feedContentController.migrateExploreFeedSettings(toLanguageVariants: languageCodeMigrationMapping, in: moc) + migrateSearchLanguageSetting(toLanguageVariants: languageCodeMigrationMapping) + migrateWikipediaEntities(toLanguageVariants: migrationMapping, in: moc) + } + + private func migrateSearchLanguageSetting(toLanguageVariants languageMapping: [String:String]) { + let defaults = UserDefaults.standard + if let url = defaults.url(forKey: WMFSearchURLKey), + let languageCode = url.wmf_languageCode { + let searchLanguageCode = languageMapping[languageCode] ?? languageCode + defaults.wmf_setCurrentSearchContentLanguageCode(searchLanguageCode) + defaults.removeObject(forKey: WMFSearchURLKey) + } + } + + private func migrateWikipediaEntities(toLanguageVariants languageMapping: [String:String], in moc: NSManagedObjectContext) { + for (languageCode, languageVariantCode) in languageMapping { + + guard let siteURLString = NSURL.wmf_URL(withDefaultSiteAndLanguageCode: languageCode)?.wmf_databaseKey else { + assertionFailure("Could not create URL from language code: '\(languageCode)'") + continue + } + + do { + // Update ContentGroups + let contentGroupFetchRequest: NSFetchRequest = WMFContentGroup.fetchRequest() + contentGroupFetchRequest.predicate = NSPredicate(format: "siteURLString == %@", siteURLString) + let groups = try moc.fetch(contentGroupFetchRequest) + for group in groups { + group.variant = languageVariantCode + } + + // Update Talk Pages + let talkPageFetchRequest: NSFetchRequest = TalkPage.fetchRequest() + talkPageFetchRequest.predicate = NSPredicate(format: "key BEGINSWITH %@", siteURLString) + let talkPages = try moc.fetch(talkPageFetchRequest) + for talkPage in talkPages { + talkPage.variant = languageVariantCode + talkPage.forceRefresh = true + } + + // Update Articles and Gather Keys + var articleKeys: Set = [] + let articleFetchRequest: NSFetchRequest = WMFArticle.fetchRequest() + articleFetchRequest.predicate = NSPredicate(format: "key BEGINSWITH %@", siteURLString) + let articles = try moc.fetch(articleFetchRequest) + for article in articles { + article.variant = languageVariantCode + if let key = article.key { + articleKeys.insert(key) + } + } + + // Update Reading List Entries + let entryFetchRequest: NSFetchRequest = ReadingListEntry.fetchRequest() + entryFetchRequest.predicate = NSPredicate(format: "articleKey IN %@", articleKeys) + let entries = try moc.fetch(entryFetchRequest) + for entry in entries { + entry.variant = languageVariantCode + } + } catch let error { + DDLogError("Error migrating articles to variant '\(languageVariantCode)': \(error)") + } + } + + if moc.hasChanges { + do { + try moc.save() + } catch let error { + DDLogError("Error saving articles and readling list entry variant migrations: \(error)") + } + } + } + + // Returns any array of language codes of any of the user's preferred languages that have + // added variant support since the indicated library version. For each language, the user + // will be informed of variant support for that language via an alert + @objc public func languageCodesNeedingVariantAlerts(since libraryVersion: Int) -> [String] { + let addedVariantLanguageCodes = allAddedVariantLanguageCodes(since: libraryVersion) + guard !addedVariantLanguageCodes.isEmpty else { + return [] + } + var uniqueLanguageCodes: Set = [] + return languageLinkController.preferredLanguages + .map { $0.languageCode } + .filter { addedVariantLanguageCodes.contains($0) } + .filter { uniqueLanguageCodes.insert($0).inserted } + } + + // Returns an array of language codes for all languages that have added variant support + // since the indicated library version. Used to determine all language codes that might + // need to have an alert presented to inform the user about the added variant support + private func allAddedVariantLanguageCodes(since libraryVersion: Int) -> [String] { + guard libraryVersion < MWKDataStore.currentLibraryVersion else { + return [] + } + + var languageCodes: [String] = [] + for version in libraryVersion...MWKDataStore.currentLibraryVersion { + languageCodes.append(contentsOf: newlyAddedVariantLanguageCodes(for: version)) + } + return languageCodes + } + + // Returns the language codes for any languages that have added variant support in that library version. + // Returns an empty array if no languages added variant support + private func newlyAddedVariantLanguageCodes(for libraryVersion: Int) -> [String] { + switch libraryVersion { + case 12: return ["crh", "gan", "iu", "kk", "ku", "sr", "tg", "uz", "zh"] + default: return [] + } + } +} + +public extension TalkPage { + var forceRefresh: Bool { + get { + + guard let revisionID = self.revisionId?.intValue else { + return false + } + + return UserDefaults.standard.talkPageForceRefreshRevisionIDs?.contains(revisionID) ?? false + } + set { + + guard let revisionID = self.revisionId?.intValue else { + assertionFailure("Attempting to set forceRefresh on a talk page that has no revisionID.") + return + } + + var revisionIDs = UserDefaults.standard.talkPageForceRefreshRevisionIDs ?? Set() + + if newValue == true { + revisionIDs.insert(revisionID) + } else { + revisionIDs.remove(revisionID) + } + + UserDefaults.standard.talkPageForceRefreshRevisionIDs = revisionIDs + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/MWKSearchResult+PageNamespace.swift b/Apps/Wikipedia/WMF Framework/MWKSearchResult+PageNamespace.swift new file mode 100644 index 0000000..af40944 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/MWKSearchResult+PageNamespace.swift @@ -0,0 +1,7 @@ +import Foundation + +extension MWKSearchResult { + public var pageNamespace: PageNamespace? { + return PageNamespace(namespaceValue: titleNamespace?.intValue) + } +} diff --git a/Apps/Wikipedia/WMF Framework/MediaWikiAcceptLanguageMapping.json b/Apps/Wikipedia/WMF Framework/MediaWikiAcceptLanguageMapping.json new file mode 100644 index 0000000..1882d12 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/MediaWikiAcceptLanguageMapping.json @@ -0,0 +1,88 @@ +{ + "zh": { + "default": { + "default": "zh-hans", + "hk": "zh-hk", + "mo": "zh-mo", + "my": "zh-my", + "sg": "zh-sg", + "tw": "zh-tw" + }, + "hans": { + "default": "zh-hans", + "hk": "zh-hk", + "mo": "zh-mo", + "my": "zh-my", + "sg": "zh-sg", + "tw": "zh-tw" + }, + "hant": { + "default": "zh-hk", + "hk": "zh-hk", + "mo": "zh-mo", + "my": "zh-my", + "sg": "zh-sg", + "tw": "zh-tw" + } + }, + "sr": { + "default": { + "default": "sr-ec" + }, + "cyrl": { + "default": "sr-ec" + }, + "latn": { + "default": "sr-el" + } + }, + "crh": { + "default": { + "default": "crh-latn" + } + }, + "gan": { + "default": { + "default": "gan-hans" + } + }, + "ku": { + "default": { + "default": "ku-latn", + } + }, + "kk": { + "default": { + "default": "kk-cyrl" + } + }, + "tg": { + "default": { + "default": "tg-cyrl", + } + }, + "iu": { + "default": { + "default": "ike-cans", + } + }, + "uz": { + "cyrl": { + "default": "uz-cyrl", + }, + "default": { + "default": "uz-latin", + }, + "arab": { + "default": "uz-latin", + }, + "latn": { + "default": "uz-latin", + } + }, + "nb": { + "default": { + "default": "no", + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/MediaWikiApiErrors.swift b/Apps/Wikipedia/WMF Framework/MediaWikiApiErrors.swift new file mode 100644 index 0000000..e164137 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/MediaWikiApiErrors.swift @@ -0,0 +1,104 @@ +import Foundation + +/// An object that is passed through from fetchers to view controllers, for reference when displaying errors in a panel. +@objc public class MediaWikiAPIDisplayError: NSObject { + + // Fully resolved html to display in the blocked panel. + @objc public let messageHtml: String + + // Base url to be referenced when user taps a relative link within the messageHtml in the blocked panel. + public let linkBaseURL: URL + + // Error code, passed through from original MediaWikiAPIError. Currently used for logging. + @objc public let code: String + + public init(messageHtml: String, linkBaseURL: URL, code: String) { + self.messageHtml = messageHtml + self.linkBaseURL = linkBaseURL + self.code = code + } +} + + +/// Represents errors that come in the MediaWiki API response. +/// See https://www.mediawiki.org/wiki/API:Errors_and_warnings +public struct MediaWikiAPIError: Codable { + + public struct Data: Codable { + public struct BlockInfo: Codable { + let blockReason: String + let blockPartial: Bool + let blockedBy: String + let blockID: Int64 + let blockExpiry: String + let blockedTimestamp: String + + enum CodingKeys: String, CodingKey { + case blockReason = "blockreason" + case blockPartial = "blockpartial" + case blockedBy = "blockedby" + case blockID = "blockid" + case blockExpiry = "blockexpiry" + case blockedTimestamp = "blockedtimestamp" + } + + init?(dict: [String: Any]) { + + guard let blockReason = dict["blockreason"] as? String, + let blockPartial = dict["blockpartial"] as? Bool, + let blockedBy = dict["blockedby"] as? String, + let blockID = dict["blockid"] as? Int64, + let blockExpiry = dict["blockexpiry"] as? String, + let blockedTimestamp = dict["blockedtimestamp"] as? String else { + return nil + } + + self.blockReason = blockReason + self.blockPartial = blockPartial + self.blockedBy = blockedBy + self.blockID = blockID + self.blockExpiry = blockExpiry + self.blockedTimestamp = blockedTimestamp + } + } + + let blockInfo: BlockInfo? + + enum CodingKeys: String, CodingKey { + case blockInfo = "blockinfo" + } + + init?(dict: [String: Any]) { + + guard let blockInfoDict = dict["blockinfo"] as? [String: Any] else { + self.blockInfo = nil + return + } + + self.blockInfo = BlockInfo(dict: blockInfoDict) + } + } + + public let code: String + let html: String + let data: Data? + + init?(dict: [String: Any]) { + + guard let code = dict["code"] as? String, + let html = dict["html"] as? String + else { + return nil + } + + self.code = code + self.html = html + + guard let dataDict = dict["data"] as? [String: Any] else { + self.data = nil + return + } + + self.data = Data(dict: dataDict) + } +} diff --git a/Apps/Wikipedia/WMF Framework/NSArray+WMFMapping.h b/Apps/Wikipedia/WMF Framework/NSArray+WMFMapping.h new file mode 100644 index 0000000..4c30763 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSArray+WMFMapping.h @@ -0,0 +1,33 @@ +@import Foundation; + +NS_ASSUME_NONNULL_BEGIN + +@interface NSArray <__covariant ObjectType> +(WMFMapping) + + /** + * Map the array using the provided block. + * If nil is returned by the block an assertion will be thrown in DEBUG + * If not in debug, then [NSNull null] will be added to the array + * + * @param block The block to map + * + * @return The new array of mapped objects + */ + - (NSArray *)wmf_strictMap : (id (^)(id obj))block; + +/** + * Transform the elements in the receiver, returning @c nil for those that should be excluded. + * + * Reduces a common pattern of mapping/filtering in one step. + * + * @param flatMap Block which takes a single parameter of the type of elements in the receiver, and returns + * another object or @c nil if the object should be excluded from the result. + * + * @return A new array with the objects transformed by @c flatMap, excluding the @c nil results. + */ +- (NSArray *)wmf_mapAndRejectNil:(id _Nullable (^_Nonnull)(ObjectType _Nonnull obj))flatMap; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/NSArray+WMFMapping.m b/Apps/Wikipedia/WMF Framework/NSArray+WMFMapping.m new file mode 100644 index 0000000..0639897 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSArray+WMFMapping.m @@ -0,0 +1,41 @@ +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@implementation NSArray (WMFMapping) + +- (NSArray *)wmf_strictMap:(id (^)(id obj))block { + NSParameterAssert(block != nil); + + NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.count]; + + [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + id value = block(obj); + NSParameterAssert(value != nil); + if (!value) { + value = [NSNull null]; + } + [result addObject:value]; + }]; + + return result; +} + +- (NSArray *)wmf_mapAndRejectNil:(id _Nullable (^_Nonnull)(id _Nonnull obj))flatMap { + if (!flatMap) { + return self; + } + return [self wmf_reduce:[[NSMutableArray alloc] initWithCapacity:self.count] + withBlock:^id(NSMutableArray *sum, id obj) { + id result = flatMap(obj); + if (result) { + [sum addObject:result]; + } + return sum; + }]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/NSCharacterSet+WMFLinkParsing.h b/Apps/Wikipedia/WMF Framework/NSCharacterSet+WMFLinkParsing.h new file mode 100644 index 0000000..7ebf85a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSCharacterSet+WMFLinkParsing.h @@ -0,0 +1,8 @@ +#import + +@interface NSCharacterSet (WMFLinkParsing) + ++ (NSCharacterSet *)wmf_encodeURIComponentAllowedCharacterSet; ++ (NSCharacterSet *)wmf_relativePathAndFragmentAllowedCharacterSet; + +@end diff --git a/Apps/Wikipedia/WMF Framework/NSCharacterSet+WMFLinkParsing.m b/Apps/Wikipedia/WMF Framework/NSCharacterSet+WMFLinkParsing.m new file mode 100644 index 0000000..0f21b6f --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSCharacterSet+WMFLinkParsing.m @@ -0,0 +1,30 @@ +#import + +@implementation NSCharacterSet (WMFLinkParsing) + ++ (NSCharacterSet *)wmf_encodeURIComponentAllowedCharacterSet { + static dispatch_once_t onceToken; + static NSCharacterSet *wmf_encodeURIComponentAllowedCharacterSet; + dispatch_once(&onceToken, ^{ + // Match the functionality of encodeURIComponent() in JavaScript, using this Mozilla reference: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#Description + // as per this comment: https://phabricator.wikimedia.org/T249284#6113747 + NSString *encodeURIComponentAllowedCharacters = @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()"; + NSCharacterSet *encodeURIComponentAllowedCharacterSet = [NSCharacterSet characterSetWithCharactersInString:encodeURIComponentAllowedCharacters]; + wmf_encodeURIComponentAllowedCharacterSet = [encodeURIComponentAllowedCharacterSet copy]; + }); + return wmf_encodeURIComponentAllowedCharacterSet; +} + ++ (NSCharacterSet *)wmf_relativePathAndFragmentAllowedCharacterSet { + static dispatch_once_t onceToken; + static NSCharacterSet *wmf_relativePathAndFragmentAllowedCharacterSet; + dispatch_once(&onceToken, ^{ + NSMutableCharacterSet *pathAllowedCharacterSet = [[NSCharacterSet URLPathAllowedCharacterSet] mutableCopy]; + [pathAllowedCharacterSet addCharactersInString:@"#?"]; + wmf_relativePathAndFragmentAllowedCharacterSet = [pathAllowedCharacterSet copy]; + }); + return wmf_relativePathAndFragmentAllowedCharacterSet; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/NSError+Utilities.swift b/Apps/Wikipedia/WMF Framework/NSError+Utilities.swift new file mode 100644 index 0000000..c4d2e47 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSError+Utilities.swift @@ -0,0 +1,11 @@ +import Foundation + +public extension NSError { + func alertMessage() -> String { + if self.wmf_isNetworkConnectionError() { + return CommonStrings.noInternetConnection + } else { + return self.localizedDescription + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/NSFileManager+DirectorySize.swift b/Apps/Wikipedia/WMF Framework/NSFileManager+DirectorySize.swift new file mode 100644 index 0000000..23ac066 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSFileManager+DirectorySize.swift @@ -0,0 +1,36 @@ +import Foundation + +@objc public extension FileManager { + @objc func sizeOfDirectory(at url: URL) -> Int64 { + var size: Int64 = 0 + let prefetchedProperties: [URLResourceKey] = [.isRegularFileKey, .fileAllocatedSizeKey, .totalFileAllocatedSizeKey] + if let enumerator = self.enumerator(at: url, includingPropertiesForKeys: prefetchedProperties) { + for item in enumerator { + guard let itemURL = item as? NSURL else { + continue + } + let resourceValueForKey: (URLResourceKey) -> NSNumber? = { key in + var value: AnyObject? = nil + try? itemURL.getResourceValue(&value, forKey: key) + return value as? NSNumber + } + + guard let isRegularFile = resourceValueForKey(URLResourceKey.isRegularFileKey)?.boolValue else { + continue + } + + guard isRegularFile else { + continue + } + + let fileSize = resourceValueForKey(URLResourceKey.totalFileAllocatedSizeKey) ?? resourceValueForKey(URLResourceKey.fileAllocatedSizeKey) + guard let allocatedSize = fileSize?.int64Value else { + assertionFailure("URLResourceKey.fileAllocatedSizeKey should always return a value") + return size + } + size += allocatedSize + } + } + return size + } +} diff --git a/Apps/Wikipedia/WMF Framework/NSManagedObject+Extensions.swift b/Apps/Wikipedia/WMF Framework/NSManagedObject+Extensions.swift new file mode 100644 index 0000000..eb5483b --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSManagedObject+Extensions.swift @@ -0,0 +1,8 @@ +import Foundation + +extension NSManagedObject { + public func hasChangedValuesForCurrentEventForKeys(_ keys: Set) -> Bool { + let changedKeys = Set(changedValuesForCurrentEvent().keys) + return !changedKeys.intersection(keys).isEmpty + } +} diff --git a/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+History.swift b/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+History.swift new file mode 100644 index 0000000..1cf69a2 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+History.swift @@ -0,0 +1,54 @@ +import Foundation + + +extension NSManagedObjectContext { + public func clearReadHistory() throws { + let request = WMFArticle.readHistoryFetchRequest + request.fetchLimit = 500 + request.propertiesToFetch = [] + var articles = try fetch(request) + while articles.count > 0 { + try autoreleasepool { () -> Void in + for article in articles { + article.removeFromReadHistoryWithoutSaving() + } + try save() + reset() + } + articles = try fetch(request) + } + } + + @objc public var mostRecentlyReadArticle: WMFArticle? { + let request = WMFArticle.readHistoryFetchRequest + request.fetchLimit = 1 + return try? fetch(request).first + } +} + + +extension WMFArticle { + static var readHistoryFetchRequest: NSFetchRequest { + let request = self.fetchRequest() + request.predicate = NSPredicate(format: "viewedDate != NULL") + request.sortDescriptors = [NSSortDescriptor(key: "viewedDate", ascending: false)] + return request + } + + public func addToReadHistory() throws { + viewedDate = Date() + updateViewedDateWithoutTime() + try managedObjectContext?.save() + } + + fileprivate func removeFromReadHistoryWithoutSaving() { + viewedDate = nil + wasSignificantlyViewed = false + updateViewedDateWithoutTime() + } + + public func removeFromReadHistory() throws { + removeFromReadHistoryWithoutSaving() + try managedObjectContext?.save() + } +} diff --git a/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+NavigationState.swift b/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+NavigationState.swift new file mode 100644 index 0000000..1d3f793 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+NavigationState.swift @@ -0,0 +1,33 @@ +import CoreData + +public extension NSManagedObjectContext { + var navigationState: NavigationState? { + get { + let keyValue = wmf_keyValue(forKey: NavigationState.libraryKey) + guard let value = keyValue?.value as? Data else { + return nil + } + let decoder = PropertyListDecoder() + guard let navigationState = try? decoder.decode(NavigationState.self, from: value) else { + return nil + } + return navigationState + } + + set { + let encoder = PropertyListEncoder() + let value = try? encoder.encode(newValue) as NSData + wmf_setValue(value, forKey: NavigationState.libraryKey) + } + } + + @objc(wmf_openArticleURL) + var openArticleURL: URL? { + guard let key = navigationState?.viewControllers.last(where: { (vc) -> Bool in + return vc.info?.articleKey != nil + })?.info?.articleKey else { + return nil + } + return URL(string: key) + } +} diff --git a/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+WMFKeyValue.h b/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+WMFKeyValue.h new file mode 100644 index 0000000..2b3ec9e --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+WMFKeyValue.h @@ -0,0 +1,18 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@class WMFKeyValue; + +@interface NSManagedObjectContext (WMFKeyValue) + +- (nullable WMFKeyValue *)wmf_keyValueForKey:(NSString *)key; +- (nullable NSNumber *)wmf_numberValueForKey:(NSString *)key; +- (nullable NSString *)wmf_stringValueForKey:(NSString *)key; +- (nullable NSArray *)wmf_arrayValueForKey:(NSString *)key; + +- (WMFKeyValue *)wmf_setValue:(nullable id)value forKey:(NSString *)key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+WMFKeyValue.m b/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+WMFKeyValue.m new file mode 100644 index 0000000..dfe37e5 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+WMFKeyValue.m @@ -0,0 +1,65 @@ +#import +#import +#import + +@implementation NSManagedObjectContext (WMFKeyValue) + +- (nullable NSArray *)wmf_keyValuesForKey:(NSString *)key fetchLimit:(NSInteger)fetchLimit { + NSFetchRequest *request = [WMFKeyValue fetchRequest]; + request.predicate = [NSPredicate predicateWithFormat:@"key == %@", key]; + if (fetchLimit > 0) { + request.fetchLimit = fetchLimit; + } + NSError *keyValueFetchError = nil; + NSArray *results = [self executeFetchRequest:request error:&keyValueFetchError]; + if (keyValueFetchError) { + DDLogError(@"Error fetching key value: %@", keyValueFetchError); + } + return results; +} +- (nullable WMFKeyValue *)wmf_keyValueForKey:(NSString *)key { + NSArray *results = [self wmf_keyValuesForKey:key fetchLimit:1]; + return results.firstObject; +} + +- (nullable id)wmf_valueOfClass:(Class) class forKey:(NSString *)key { + WMFKeyValue *keyValue = [self wmf_keyValueForKey:key]; + id value = keyValue.value; + if ([value isKindOfClass:class]) { + return value; + } else { + return nil; + } +} + + - (nullable NSNumber *)wmf_numberValueForKey : (NSString *)key { + return [self wmf_valueOfClass:[NSNumber class] forKey:key]; +} + +- (nullable NSString *)wmf_stringValueForKey:(NSString *)key { + return [self wmf_valueOfClass:[NSString class] forKey:key]; +} + +- (nullable NSArray *)wmf_arrayValueForKey:(NSString *)key { + return [self wmf_valueOfClass:[NSArray class] forKey:key]; +} + +- (WMFKeyValue *)wmf_setValue:(nullable id)value forKey:(NSString *)key { + NSArray *results = [self wmf_keyValuesForKey:key fetchLimit:0]; + if (results.count > 1) { + // failsafe to delete extra key value objects + NSArray *subarray = [results subarrayWithRange:NSMakeRange(1, results.count - 1)]; + for (WMFKeyValue *value in subarray) { + [self deleteObject:value]; + } + } + WMFKeyValue *keyValue = results.firstObject; + if (!keyValue) { + keyValue = [NSEntityDescription insertNewObjectForEntityForName:@"WMFKeyValue" inManagedObjectContext:self]; + keyValue.key = key; + } + keyValue.value = value; + return keyValue; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+WMFUtilities.swift b/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+WMFUtilities.swift new file mode 100644 index 0000000..a6fac4a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSManagedObjectContext+WMFUtilities.swift @@ -0,0 +1,115 @@ +import CocoaLumberjackSwift + +public extension NSManagedObjectContext { + func wmf_create(entityNamed entityName: String, withValue value: Any, forKey key: String) -> T? { + let object = NSEntityDescription.insertNewObject(forEntityName: entityName, into: self) as? T + object?.setValue(value, forKey: key) + return object + } + + @discardableResult func wmf_create(entityNamed entityName: String, withKeysAndValues dictionary: [String: Any?]) -> T? { + let object = NSEntityDescription.insertNewObject(forEntityName: entityName, into: self) as? T + for (key, value) in dictionary { + object?.setValue(value, forKey: key) + } + return object + } + + + func wmf_fetch(objectForEntityName entityName: String, withValue value: Any, forKey key: String) -> T? { + let fetchRequest = NSFetchRequest(entityName: entityName) + fetchRequest.predicate = NSPredicate(format: "\(key) == %@", argumentArray: [value]) + fetchRequest.fetchLimit = 1 + var results: [T] = [] + do { + results = try fetch(fetchRequest) + } catch let error { + DDLogError("Error fetching: \(error)") + } + + return results.first + } + + func wmf_fetchOrCreate(objectForEntityName entityName: String, withValue value: Any, forKey key: String) -> T? { + return wmf_fetch(objectForEntityName: entityName, withValue: value, forKey: key) ?? wmf_create(entityNamed: entityName, withValue: value, forKey: key) + } + + func wmf_fetch(objectsForEntityName entityName: String, withValues values: [V], forKey key: String) throws -> [T]? { + let fetchRequest = NSFetchRequest(entityName: entityName) + fetchRequest.predicate = NSPredicate(format: "\(key) IN %@", argumentArray: [values]) + fetchRequest.fetchLimit = values.count + return try fetch(fetchRequest) + } + + func wmf_fetchOrCreate(objectsForEntityName entityName: String, withValues values: [V], forKey key: String) throws -> [T]? { + var results = try wmf_fetch(objectsForEntityName: entityName, withValues: values, forKey: key) as? [T] ?? [] + var missingValues = Set(values) + for result in results { + guard let value = result.value(forKey: key) as? V else { + continue + } + missingValues.remove(value) + } + for value in missingValues { + guard let object = wmf_create(entityNamed: entityName, withValue: value, forKey: key) as? T else { + continue + } + results.append(object) + } + return results + } + + func wmf_batchProcessObjects(matchingPredicate: NSPredicate? = nil, resetAfterSave: Bool = false, handler: (T) throws -> Void) throws { + let fetchRequest = T.fetchRequest() + let batchSize = 500 + fetchRequest.predicate = matchingPredicate + fetchRequest.fetchBatchSize = batchSize + let results = try fetch(fetchRequest) + + for (index, result) in results.enumerated() { + if let result = result as? T { + try handler(result) + } + let count = index + 1 + if count % batchSize == 0 || count == results.count { + if hasChanges { + try save() + } + if resetAfterSave { + reset() + } + } + } + } + + func wmf_batchProcess(matchingPredicate: NSPredicate? = nil, resetAfterSave: Bool = false, handler: ([T]) throws -> Void) throws { + let fetchRequest = T.fetchRequest() + let batchSize = 500 + fetchRequest.predicate = matchingPredicate + fetchRequest.fetchBatchSize = batchSize + let results = try fetch(fetchRequest) as? [T] ?? [] + + var start: Int = 0 + var end: Int = 0 + while start < results.count { + end = min(start + batchSize, results.count) + try handler([T](results[start..(_ block: () -> T?) -> T? { + var result: T? = nil + performAndWait { + result = block() + } + return result + } +} + diff --git a/Apps/Wikipedia/WMF Framework/NSMutableAttributedString+Mutations.swift b/Apps/Wikipedia/WMF Framework/NSMutableAttributedString+Mutations.swift new file mode 100644 index 0000000..5dfe2ca --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSMutableAttributedString+Mutations.swift @@ -0,0 +1,18 @@ +import Foundation + +extension NSMutableAttributedString { + /// Applies a bold font to the first portion of the string that matches the given string + /// - Parameter matchingString: the string to search for and bold + /// - Parameter textStyle: The text style to use for the bolded string + /// - Parameter weight: The font weight to use for the bolded string + /// - Parameter traitCollection: The trait collection used for generating the font + public func applyBoldFont(to matchingString: String, textStyle: DynamicTextStyle, weight: UIFont.Weight = .semibold, matching traitCollection: UITraitCollection) { + // NSString and NSRange are used here for better compatability with NSAttributedString + let range = (string as NSString).range(of: matchingString, options: .caseInsensitive) + guard range.location != NSNotFound else { + return + } + let boldFont = UIFont.wmf_font(textStyle.with(weight: weight), compatibleWithTraitCollection: traitCollection) + addAttribute(.font, value: boldFont, range: range) + } +} diff --git a/Apps/Wikipedia/WMF Framework/NSRegularExpression+HTML.h b/Apps/Wikipedia/WMF Framework/NSRegularExpression+HTML.h new file mode 100644 index 0000000..18beb84 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSRegularExpression+HTML.h @@ -0,0 +1,18 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSRegularExpression (HTML) + +// Matches HTML tags ++ (NSRegularExpression *)wmf_HTMLTagRegularExpression; + +// Matches HTML entities &   etc ++ (NSRegularExpression *)wmf_HTMLEntityRegularExpression; + +// Matches ' " ; ' ++ (NSRegularExpression *)wmf_charactersToEscapeForJSRegex; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/NSRegularExpression+HTML.m b/Apps/Wikipedia/WMF Framework/NSRegularExpression+HTML.m new file mode 100644 index 0000000..19d6489 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSRegularExpression+HTML.m @@ -0,0 +1,41 @@ +#import "NSRegularExpression+HTML.h" + +@implementation NSRegularExpression (HTML) + ++ (NSRegularExpression *)wmf_HTMLTagRegularExpression { + static NSRegularExpression *tagRegex; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSString *pattern = @"(?:<)([\\/a-z0-9]*)(?:\\s?)([^>]*)(?:>)"; + tagRegex = [NSRegularExpression regularExpressionWithPattern:pattern + options:NSRegularExpressionCaseInsensitive + error:nil]; + }); + return tagRegex; +} + ++ (NSRegularExpression *)wmf_HTMLEntityRegularExpression { + static NSRegularExpression *tagRegex; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSString *pattern = @"&([^\\s;]+);"; + tagRegex = [NSRegularExpression regularExpressionWithPattern:pattern + options:NSRegularExpressionCaseInsensitive + error:nil]; + }); + return tagRegex; +} + ++ (NSRegularExpression *)wmf_charactersToEscapeForJSRegex { + static NSRegularExpression *wmf_charactersToEscapeForJSRegex; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSString *pattern = @"[\"'\\n]"; + wmf_charactersToEscapeForJSRegex = [NSRegularExpression regularExpressionWithPattern:pattern + options:NSRegularExpressionCaseInsensitive + error:nil]; + }); + return wmf_charactersToEscapeForJSRegex; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/NSRegularExpression+Utilities.swift b/Apps/Wikipedia/WMF Framework/NSRegularExpression+Utilities.swift new file mode 100644 index 0000000..c9a7356 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSRegularExpression+Utilities.swift @@ -0,0 +1,13 @@ +public extension NSRegularExpression { + func firstMatch(in string: String) -> NSTextCheckingResult? { + return firstMatch(in: string, options: [], range: string.fullRange) + } + + func firstReplacementString(in string: String, template: String = "$1") -> String? { + guard let result = firstMatch(in: string) + else { + return nil + } + return replacementString(for: result, in: string, offset: 0, template: template) + } +} diff --git a/Apps/Wikipedia/WMF Framework/NSString+SHA256.h b/Apps/Wikipedia/WMF Framework/NSString+SHA256.h new file mode 100644 index 0000000..abc3d31 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSString+SHA256.h @@ -0,0 +1,7 @@ +#import + +@interface NSString (SHA256) + +@property (nonatomic, readonly) NSString *SHA256; + +@end diff --git a/Apps/Wikipedia/WMF Framework/NSString+SHA256.m b/Apps/Wikipedia/WMF Framework/NSString+SHA256.m new file mode 100644 index 0000000..487f2d0 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSString+SHA256.m @@ -0,0 +1,35 @@ +#import "NSString+SHA256.h" +#import + +@implementation NSData (SHA256) + +- (NSString *)SHA256 { + CC_SHA256_CTX hashObject; + CC_SHA256_Init(&hashObject); + + NSUInteger length = [self length]; + const void *buffer = [self bytes]; + CC_SHA256_Update(&hashObject, + (const void *)buffer, + (CC_LONG)length); + + unsigned char digest[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256_Final(digest, &hashObject); + + char hash[2 * sizeof(digest) + 1]; + for (size_t i = 0; i < sizeof(digest); ++i) { + snprintf(hash + (2 * i), 3, "%02x", (int)(digest[i])); + } + + return [[NSString alloc] initWithUTF8String:hash]; +} + +@end + +@implementation NSString (SHA256) + +- (NSString *)SHA256 { + return [[self dataUsingEncoding:NSUTF8StringEncoding] SHA256]; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/NSTextAttachment+WMFExtras.swift b/Apps/Wikipedia/WMF Framework/NSTextAttachment+WMFExtras.swift new file mode 100644 index 0000000..bfb9f69 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSTextAttachment+WMFExtras.swift @@ -0,0 +1,10 @@ +public extension NSTextAttachment { + func setImageHeight(_ height: CGFloat, font: UIFont) { + guard let image = image else { return } + + let ratio = image.size.width / image.size.height + let mid = font.descender + font.capHeight + + bounds = CGRect(x: bounds.origin.x, y: font.descender - height / 2 + mid + 2, width: ratio * height, height: height) + } +} diff --git a/Apps/Wikipedia/WMF Framework/NSUserActivity+Extensions.swift b/Apps/Wikipedia/WMF Framework/NSUserActivity+Extensions.swift new file mode 100644 index 0000000..dc5252e --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NSUserActivity+Extensions.swift @@ -0,0 +1,16 @@ +import Foundation + +public extension NSUserActivity { + @objc var shouldSkipOnboarding: Bool { + guard let path = webpageURL?.wikiResourcePath, + let languageCode = webpageURL?.wmf_languageCode else { + return false + } + + let namespaceAndTitle = path.namespaceAndTitleOfWikiResourcePath(with: languageCode) + let namespace = namespaceAndTitle.0 + let title = namespaceAndTitle.1 + + return namespace == .special && title == "ReadingLists" + } +} diff --git a/Apps/Wikipedia/WMF Framework/NavigationBar.swift b/Apps/Wikipedia/WMF Framework/NavigationBar.swift new file mode 100644 index 0000000..aebdb79 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NavigationBar.swift @@ -0,0 +1,858 @@ +@objc public enum NavigationBarDisplayType: Int { + case backVisible + case largeTitle + case centeredLargeTitle // If left, title, and right bar button items exist, center title. Otherwise, revert to `largeTitle` behavior. + case modal + case hidden +} + + +// This class works in concert w/ `NavigationBarHider` to implement our custom Navigation Bar. +// This `NavigationBar` class holds the properties and subviews, and `NavigationBarHider` implements the hiding logic when `isInteractiveHidingEnabled` is true. + +/* + Navigation bars can have a top spacing, a title, an UnderBarView, and an ExtendedBarView. + Example of the SavedViewController: The title is "Saved", the underNavigationBarView is the "All articles / Reading lists" picker, and the extendedNavigationBarView is the search bar (when on "all articles") or "+ Create a new list" buton (when on "reading lists"). + + _Main subviews_ + var barTopSpacing: CGFloat + var title: String? + func addExtendedNavigationBarView(_ view: UIView) + func removeExtendedNavigationBarView() + func addUnderNavigationBarView(_ view: UIView, shouldIgnoreSafeArea: Bool) + func removeUnderNavigationBarView() + var topSpacingPercentHidden, navigationBarPercentHidden, underBarViewPercentHidden, extendedViewPercentHidden // Four CGFloats, each setting the percent hidden for the component + + _Main properties_ + var displayType: NavigationBarDisplayType // see enum above for options + var isBarHidingEnabled: Bool // Does the NavigationBar scroll away. + var isUnderBarViewHidingEnabled: Bool // Does the UnderBarView scroll away. + var isUnderBarFadingEnabled: Bool // Fade out UnderBarView as it hides + var isExtendedViewHidingEnabled: Bool // Does the extendedView scroll away. + var isExtendedViewFadingEnabled: Bool // fade out extended view as it hides + var underBarViewPercentHiddenForShowingTitle: CGFloat // amount of fading before showing title in bar + var isAdjustingHidingFromContentInsetChangesEnabled: Bool + var isShadowHidingEnabled: Bool // turn on/off shadow alpha adjusment + var isTitleShrinkingEnabled: Bool // turn on/off title shrinking + var isInteractiveHidingEnabled: Bool // turn on/off any interactive adjustment of bar or view visibility + var isTopSpacingHidingEnabled: Bool + var shouldTransformUnderBarViewWithBar: Bool = false // hide/show underbar view when bar is hidden/shown + var delegate: UIViewController? // Set by WMFViewController when this NavBar is initially set up + + _Detailed styling_ + var backgroundAlpha: CGFloat + var isShadowBelowUnderBarView: Bool + var isShadowShowing: Bool + var shadowAlpha: CGFloat + var shadowColorKeyPath: KeyPath + + _Used only by FakeProgressController_ + func setProgress(_ progress: Float, animated: Bool) + func setProgressHidden(_ hidden: Bool, animated: Bool) + var progress: Float + + _Related to tap targets_ + var allowsUnderbarHitsFallThrough: Bool //if true, this only considers underBarView's subviews for hitTest, not self. Use if you need underlying view controller's scroll view to capture scrolling. + var allowsExtendedHitsFallThrough: Bool //if true, this only considers extendedView's subviews for hitTest, not self. Use if you need underlying view controller's scroll view to capture scrolling. + + _See comments near `updateHackyConstraint` for details_ + var needsUnderBarHack: Bool + func updateHackyConstraint() + + _Read by other classes to help with their layouts_ + var visibleHeight: CGFloat + var hiddenHeight: CGFloat + */ +@objc(WMFNavigationBar) +public class NavigationBar: SetupView, FakeProgressReceiving, FakeProgressDelegate { + fileprivate let statusBarUnderlay: UIView = UIView() + fileprivate let titleBar: UIToolbar = UIToolbar() + fileprivate let bar: UINavigationBar = UINavigationBar() + fileprivate let underBarView: UIView = UIView() // this is always visible below the navigation bar + fileprivate let extendedView: UIView = UIView() + fileprivate let shadow: UIView = UIView() + fileprivate let progressView: UIProgressView = UIProgressView() + fileprivate let backgroundView: UIView = UIView() + public var underBarViewPercentHiddenForShowingTitle: CGFloat? + public var title: String? + + convenience public init() { + self.init(frame: CGRect(x: 0, y: 0, width: 320, height: 44)) + } + + override init(frame: CGRect) { + super.init(frame: frame) + assert(frame.size != .zero, "Non-zero frame size required to prevent iOS 13 constraint breakage") + titleBar.frame = bounds + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public var isAdjustingHidingFromContentInsetChangesEnabled: Bool = true + + public var isShadowHidingEnabled: Bool = false // turn on/off shadow alpha adjusment + public var isTitleShrinkingEnabled: Bool = false + public var isInteractiveHidingEnabled: Bool = true // turn on/off any interactive adjustment of bar or view visibility + @objc public var isShadowBelowUnderBarView: Bool = false { + didSet { + updateShadowConstraints() + } + } + public var isShadowShowing: Bool = true { + didSet { + updateShadowHeightConstraintConstant() + } + } + + @objc public var isTopSpacingHidingEnabled: Bool = true + @objc public var isBarHidingEnabled: Bool = true + @objc public var isUnderBarViewHidingEnabled: Bool = false + public var isUnderBarFadingEnabled: Bool = true + @objc public var isExtendedViewHidingEnabled: Bool = false + @objc public var isExtendedViewFadingEnabled: Bool = true // fade out extended view as it hides + public var shouldTransformUnderBarViewWithBar: Bool = false // hide/show underbar view when bar is hidden/shown // TODO: change this stupid name + public var allowsUnderbarHitsFallThrough: Bool = false // if true, this only considers underBarView's subviews for hitTest, not self. Use if you need underlying view controller's scroll view to capture scrolling. + public var allowsExtendedHitsFallThrough: Bool = false // if true, this only considers extendedView's subviews for hitTest, not self. Use if you need underlying view controller's scroll view to capture scrolling. + + private var theme = Theme.standard + + public var shadowColorKeyPath: KeyPath = \Theme.colors.chromeShadow + + /// back button presses will be forwarded to this nav controller + @objc public weak var delegate: UIViewController? { + didSet { + updateNavigationItems() + } + } + + private var _displayType: NavigationBarDisplayType = .backVisible + @objc public var displayType: NavigationBarDisplayType { + get { + return _displayType + } + set { + guard newValue != _displayType else { + return + } + _displayType = newValue + isTitleShrinkingEnabled = _displayType == .largeTitle || _displayType == .centeredLargeTitle + updateTitleBarConstraints() + updateNavigationItems() + updateAccessibilityElements() + } + } + + private func updateAccessibilityElements() { + let titleElement = (displayType == .largeTitle || displayType == .centeredLargeTitle) ? titleBar : bar + accessibilityElements = [titleElement, underBarView, extendedView] + } + + @objc public func updateNavigationItems() { + var items: [UINavigationItem] = [] + if displayType == .backVisible, let vc = delegate, let nc = vc.navigationController { + nc.viewControllers.forEach({ items.append($0.navigationItem) }) + } else if let item = delegate?.navigationItem { + items.append(item) + } + + if (displayType == .largeTitle || displayType == .centeredLargeTitle), let navigationItem = items.last { + configureTitleBar(with: navigationItem, centerTitle: displayType == .centeredLargeTitle) + } else { + bar.setItems([], animated: false) + bar.setItems(items, animated: false) + } + apply(theme: theme) + } + + private var cachedTitleViewItem: UIBarButtonItem? + private var titleView: UIView? + + private func configureTitleBar(with navigationItem: UINavigationItem, centerTitle: Bool) { + var titleBarItems: [UIBarButtonItem] = [] + titleView = nil + + var extractedTitleBarButtonItem: UIBarButtonItem? + var extractedLeftBarButtonItem: UIBarButtonItem? + var extractedRightBarButtonItem: UIBarButtonItem? + + if let titleView = navigationItem.titleView { + if let cachedTitleViewItem = cachedTitleViewItem { + extractedTitleBarButtonItem = cachedTitleViewItem + titleBarItems.append(cachedTitleViewItem) + } else { + let titleItem = UIBarButtonItem(customView: titleView) + extractedTitleBarButtonItem = titleItem + titleBarItems.append(titleItem) + cachedTitleViewItem = titleItem + } + } else if let title = navigationItem.title { + let navigationTitleLabel = UILabel() + navigationTitleLabel.text = title + navigationTitleLabel.sizeToFit() + navigationTitleLabel.font = UIFont.wmf_font(.boldTitle1) + titleView = navigationTitleLabel + let titleItem = UIBarButtonItem(customView: navigationTitleLabel) + extractedTitleBarButtonItem = titleItem + titleBarItems.append(titleItem) + } + + titleBarItems.append(UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)) + + if let item = navigationItem.leftBarButtonItem { + let leftBarButtonItem = barButtonItem(from: item) + extractedLeftBarButtonItem = leftBarButtonItem + titleBarItems.append(leftBarButtonItem) + } + + if let item = navigationItem.rightBarButtonItem { + let rightBarButtonItem = barButtonItem(from: item) + extractedRightBarButtonItem = rightBarButtonItem + titleBarItems.append(rightBarButtonItem) + } + + // The default `largeTitle` behavior left aligns the title view, which isn't desirable for displaying the Notifications Center bar button in the Explore feed. + // Center the title element with appropriate flexible space between left and right bar button items, if they exist. + if centerTitle { + titleBarItems = [] + + if let extractedLeftBarButtonItem = extractedLeftBarButtonItem { + titleBarItems.append(extractedLeftBarButtonItem) + } + + titleBarItems.append(UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)) + + if let extractedTitleBarButtonItem = extractedTitleBarButtonItem { + titleBarItems.append(extractedTitleBarButtonItem) + } + + titleBarItems.append(UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)) + + if let extractedRightBarButtonItem = extractedRightBarButtonItem { + titleBarItems.append(extractedRightBarButtonItem) + } + } + + titleBar.setItems(titleBarItems, animated: false) + } + + // HAX: barButtonItem that we're getting from the navigationItem will not be shown on iOS 11 so we need to recreate it + private func barButtonItem(from item: UIBarButtonItem) -> UIBarButtonItem { + let barButtonItem: UIBarButtonItem + if let title = item.title { + barButtonItem = UIBarButtonItem(title: title, style: item.style, target: item.target, action: item.action) + } else if let systemBarButton = item as? SystemBarButton, let systemItem = systemBarButton.systemItem { + barButtonItem = SystemBarButton(with: systemItem, target: systemBarButton.target, action: systemBarButton.action) + } else if let customView = item.customView { + if let customViewData = try? NSKeyedArchiver.archivedData(withRootObject: customView, requiringSecureCoding: false), + let copiedView = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIView.self, from: customViewData) { + if let button = customView as? UIButton, let copiedButton = copiedView as? UIButton { + for target in button.allTargets { + guard let actions = button.actions(forTarget: target, forControlEvent: .touchUpInside) else { + continue + } + for action in actions { + copiedButton.addTarget(target, action: Selector(action), for: .touchUpInside) + } + } + } + barButtonItem = UIBarButtonItem(customView: copiedView) + } else { + assert(false, "unable to copy custom view") + barButtonItem = item + } + } else if let image = item.image { + barButtonItem = UIBarButtonItem(image: image, landscapeImagePhone: item.landscapeImagePhone, style: item.style, target: item.target, action: item.action) + } else { + assert(false, "barButtonItem must have title OR be of type SystemBarButton OR have image OR have custom view") + barButtonItem = item + } + barButtonItem.isEnabled = item.isEnabled + barButtonItem.isAccessibilityElement = item.isAccessibilityElement + barButtonItem.accessibilityLabel = item.accessibilityLabel + return barButtonItem + } + + private var titleBarHeightConstraint: NSLayoutConstraint! + + private var underBarViewHeightConstraint: NSLayoutConstraint! + + private var shadowTopUnderBarViewBottomConstraint: NSLayoutConstraint! + private var shadowTopExtendedViewBottomConstraint: NSLayoutConstraint! + + private var shadowHeightConstraint: NSLayoutConstraint! + private var extendedViewHeightConstraint: NSLayoutConstraint! + + private lazy var safeAreaUnderBarConstraints = [ + safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: underBarView.leadingAnchor), + safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: underBarView.trailingAnchor) + ] + + private lazy var fullWidthUnderBarConstraints = [ + leadingAnchor.constraint(equalTo: underBarView.leadingAnchor), + trailingAnchor.constraint(equalTo: underBarView.trailingAnchor) + ] + + private var titleBarTopConstraint: NSLayoutConstraint! + private var barTopConstraint: NSLayoutConstraint! + public var barTopSpacing: CGFloat = 0 { + didSet { + titleBarTopConstraint.constant = barTopSpacing + barTopConstraint.constant = barTopSpacing + setNeedsLayout() + } + } + + private var underBarViewTopBarBottomConstraint: NSLayoutConstraint! + private var underBarViewTopTitleBarBottomConstraint: NSLayoutConstraint! + private var underBarViewTopBottomConstraint: NSLayoutConstraint! + + /// See `updateHackyConstraint` for details + public var needsUnderBarHack: Bool = false { + didSet { + underBarViewTopBarBottomConstraint.constant = (needsUnderBarHack ? -12 : 0) + } + } + + override open func setup() { + super.setup() + translatesAutoresizingMaskIntoConstraints = false + backgroundView.translatesAutoresizingMaskIntoConstraints = false + statusBarUnderlay.translatesAutoresizingMaskIntoConstraints = false + bar.translatesAutoresizingMaskIntoConstraints = false + underBarView.translatesAutoresizingMaskIntoConstraints = false + extendedView.translatesAutoresizingMaskIntoConstraints = false + progressView.translatesAutoresizingMaskIntoConstraints = false + progressView.alpha = 0 + shadow.translatesAutoresizingMaskIntoConstraints = false + titleBar.translatesAutoresizingMaskIntoConstraints = false + + addSubview(backgroundView) + addSubview(extendedView) + addSubview(underBarView) + addSubview(bar) + addSubview(titleBar) + addSubview(progressView) + addSubview(statusBarUnderlay) + addSubview(shadow) + + updateAccessibilityElements() + + bar.delegate = self + + shadowHeightConstraint = shadow.heightAnchor.constraint(equalToConstant: 0.5) + shadowHeightConstraint.priority = .defaultHigh + shadow.addConstraint(shadowHeightConstraint) + + var updatedConstraints: [NSLayoutConstraint] = [] + + let statusBarUnderlayTopConstraint = topAnchor.constraint(equalTo: statusBarUnderlay.topAnchor) + updatedConstraints.append(statusBarUnderlayTopConstraint) + + let statusBarUnderlayBottomConstraint = safeAreaLayoutGuide.topAnchor.constraint(equalTo: statusBarUnderlay.bottomAnchor) + updatedConstraints.append(statusBarUnderlayBottomConstraint) + + let statusBarUnderlayLeadingConstraint = leadingAnchor.constraint(equalTo: statusBarUnderlay.leadingAnchor) + updatedConstraints.append(statusBarUnderlayLeadingConstraint) + let statusBarUnderlayTrailingConstraint = trailingAnchor.constraint(equalTo: statusBarUnderlay.trailingAnchor) + updatedConstraints.append(statusBarUnderlayTrailingConstraint) + + titleBarHeightConstraint = titleBar.heightAnchor.constraint(equalToConstant: 44) + titleBarHeightConstraint.priority = UILayoutPriority(rawValue: 999) + titleBar.addConstraint(titleBarHeightConstraint) + + titleBarTopConstraint = titleBar.topAnchor.constraint(equalTo: statusBarUnderlay.bottomAnchor, constant: barTopSpacing) + let titleBarLeadingConstraint = leadingAnchor.constraint(equalTo: titleBar.leadingAnchor) + let titleBarTrailingConstraint = trailingAnchor.constraint(equalTo: titleBar.trailingAnchor) + + barTopConstraint = bar.topAnchor.constraint(equalTo: statusBarUnderlay.bottomAnchor, constant: barTopSpacing) + let barLeadingConstraint = leadingAnchor.constraint(equalTo: bar.leadingAnchor) + let barTrailingConstraint = trailingAnchor.constraint(equalTo: bar.trailingAnchor) + + underBarViewHeightConstraint = underBarView.heightAnchor.constraint(equalToConstant: 0) + underBarView.addConstraint(underBarViewHeightConstraint) + + /// See `updateHackyConstraint` for explanation of the constant on the next line. + underBarViewTopBarBottomConstraint = bar.bottomAnchor.constraint(equalTo: underBarView.topAnchor, constant: needsUnderBarHack ? -12 : 0) + underBarViewTopTitleBarBottomConstraint = titleBar.bottomAnchor.constraint(equalTo: underBarView.topAnchor) + underBarViewTopBottomConstraint = topAnchor.constraint(equalTo: underBarView.topAnchor) + + extendedViewHeightConstraint = extendedView.heightAnchor.constraint(equalToConstant: 0) + extendedView.addConstraint(extendedViewHeightConstraint) + + let extendedViewTopConstraint = underBarView.bottomAnchor.constraint(equalTo: extendedView.topAnchor) + let extendedViewLeadingConstraint = leadingAnchor.constraint(equalTo: extendedView.leadingAnchor) + let extendedViewTrailingConstraint = trailingAnchor.constraint(equalTo: extendedView.trailingAnchor) + let extendedViewBottomConstraint = extendedView.bottomAnchor.constraint(equalTo: bottomAnchor) + + let backgroundViewTopConstraint = topAnchor.constraint(equalTo: backgroundView.topAnchor) + let backgroundViewLeadingConstraint = leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor) + let backgroundViewTrailingConstraint = trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor) + let backgroundViewBottomConstraint = extendedView.bottomAnchor.constraint(equalTo: backgroundView.bottomAnchor) + + let progressViewBottomConstraint = shadow.topAnchor.constraint(equalTo: progressView.bottomAnchor, constant: 1) + let progressViewLeadingConstraint = leadingAnchor.constraint(equalTo: progressView.leadingAnchor) + let progressViewTrailingConstraint = trailingAnchor.constraint(equalTo: progressView.trailingAnchor) + + let shadowLeadingConstraint = leadingAnchor.constraint(equalTo: shadow.leadingAnchor) + let shadowTrailingConstraint = trailingAnchor.constraint(equalTo: shadow.trailingAnchor) + + shadowTopExtendedViewBottomConstraint = extendedView.bottomAnchor.constraint(equalTo: shadow.topAnchor) + shadowTopUnderBarViewBottomConstraint = underBarView.bottomAnchor.constraint(equalTo: shadow.topAnchor) + + updatedConstraints.append(contentsOf: [titleBarTopConstraint, titleBarLeadingConstraint, titleBarTrailingConstraint, underBarViewTopTitleBarBottomConstraint, barTopConstraint, barLeadingConstraint, barTrailingConstraint, underBarViewTopBarBottomConstraint, underBarViewTopBottomConstraint, extendedViewTopConstraint, extendedViewLeadingConstraint, extendedViewTrailingConstraint, extendedViewBottomConstraint, backgroundViewTopConstraint, backgroundViewLeadingConstraint, backgroundViewTrailingConstraint, backgroundViewBottomConstraint, progressViewBottomConstraint, progressViewLeadingConstraint, progressViewTrailingConstraint, shadowTopUnderBarViewBottomConstraint, shadowTopExtendedViewBottomConstraint, shadowLeadingConstraint, shadowTrailingConstraint]) + updatedConstraints.append(contentsOf: safeAreaUnderBarConstraints) + addConstraints(updatedConstraints) + + updateTitleBarConstraints() + updateShadowConstraints() + updateShadowHeightConstraintConstant() + + setNavigationBarPercentHidden(0, underBarViewPercentHidden: 0, extendedViewPercentHidden: 0, topSpacingPercentHidden: 0, animated: false) + } + + public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateShadowHeightConstraintConstant() + } + + /// This function covers for a layout bug in iOS: When presenting a `pageSheet`, the navigation bar doesn't size appropriately. The top of the `underBarView` is appropriately pinned to the bottom of the navigation bar. The navigation bar's content view has a larger height than the actual navigation bar used in the constraint, however, and that causes the weird view: https://github.com/wikimedia/wikipedia-ios/pull/3683#issuecomment-693732339 + /// This is an issue that others experience as well: https://developer.apple.com/forums/thread/121861 , https://stackoverflow.com/questions/57784596/how-to-prevent-gap-between-uinavigationbar-and-view-in-ios-13 , and https://stackoverflow.com/questions/58296535/ios-13-new-pagesheet-formsheet-navigationbar-height . + /// The layout bug will clear itself if you rotate the screen to landscape, then rotate it back to portrait. This allows it to also look good when the screen loads. + /// From the links above, others have fixed this by forcing a layout pass on the navigation bar. Unfortunately that doesn't fix it in our case. (Perhaps because our nav bar is on the View Controller and not the Navigation Controller?) + /// Hopefully some day this and `needsUnderBarHack` can be removed. To test if iOS has fixed it: Set `needsUnderBarHack` to always return `false`, open a modal w/ a presentation style of `pageSheet` and an underbar view (ex: `ArticleAsLivingDocViewController`, and ensure the top of the underbar view is not hidden behind the nav bar. + public func updateHackyConstraint() { + if needsUnderBarHack && underBarViewTopBarBottomConstraint.constant != 0 { + underBarViewTopBarBottomConstraint.constant = 0 + } + } + + private func updateShadowHeightConstraintConstant() { + guard traitCollection.displayScale > 0 else { + return + } + + if !isShadowShowing { + shadowHeightConstraint.constant = 0 + } else { + shadowHeightConstraint.constant = 1.0 / traitCollection.displayScale + } + } + + + fileprivate var _topSpacingPercentHidden: CGFloat = 0 + @objc public var topSpacingPercentHidden: CGFloat { + get { + return _topSpacingPercentHidden + } + set { + setNavigationBarPercentHidden(_navigationBarPercentHidden, underBarViewPercentHidden: _underBarViewPercentHidden, extendedViewPercentHidden: _extendedViewPercentHidden, topSpacingPercentHidden: newValue, animated: false) + } + } + + fileprivate var _navigationBarPercentHidden: CGFloat = 0 + @objc public var navigationBarPercentHidden: CGFloat { + get { + return _navigationBarPercentHidden + } + set { + setNavigationBarPercentHidden(newValue, underBarViewPercentHidden: _underBarViewPercentHidden, extendedViewPercentHidden: _extendedViewPercentHidden, topSpacingPercentHidden: _topSpacingPercentHidden, animated: false) + } + } + + private var _underBarViewPercentHidden: CGFloat = 0 + @objc public var underBarViewPercentHidden: CGFloat { + get { + return _underBarViewPercentHidden + } + set { + setNavigationBarPercentHidden(_navigationBarPercentHidden, underBarViewPercentHidden: newValue, extendedViewPercentHidden: _extendedViewPercentHidden, topSpacingPercentHidden: _topSpacingPercentHidden, animated: false) + } + } + + fileprivate var _extendedViewPercentHidden: CGFloat = 0 + @objc public var extendedViewPercentHidden: CGFloat { + get { + return _extendedViewPercentHidden + } + set { + setNavigationBarPercentHidden(_navigationBarPercentHidden, underBarViewPercentHidden: _underBarViewPercentHidden, extendedViewPercentHidden: newValue, topSpacingPercentHidden: _topSpacingPercentHidden, animated: false) + } + } + + private var shouldUnderBarIgnoreSafeArea: Bool = false { + didSet { + guard shouldUnderBarIgnoreSafeArea != oldValue else { + return + } + + NSLayoutConstraint.deactivate(shouldUnderBarIgnoreSafeArea ? safeAreaUnderBarConstraints : fullWidthUnderBarConstraints) + NSLayoutConstraint.activate(shouldUnderBarIgnoreSafeArea ? fullWidthUnderBarConstraints : safeAreaUnderBarConstraints) + } + } + + @objc dynamic public var visibleHeight: CGFloat = 0 + @objc public var hiddenHeight: CGFloat = 0 + + public var shadowAlpha: CGFloat { + get { + return shadow.alpha + } + set { + shadow.alpha = newValue + } + } + + @objc public func setNavigationBarPercentHidden(_ navigationBarPercentHidden: CGFloat, underBarViewPercentHidden: CGFloat, extendedViewPercentHidden: CGFloat, topSpacingPercentHidden: CGFloat, shadowAlpha: CGFloat = -1, animated: Bool, additionalAnimations: (() -> Void)? = nil) { + if animated { + layoutIfNeeded() + } + + if isTopSpacingHidingEnabled { + _topSpacingPercentHidden = topSpacingPercentHidden + } + + if isBarHidingEnabled { + _navigationBarPercentHidden = navigationBarPercentHidden + } + + if isUnderBarViewHidingEnabled { + _underBarViewPercentHidden = underBarViewPercentHidden + } + + if isExtendedViewHidingEnabled { + _extendedViewPercentHidden = extendedViewPercentHidden + } + + setNeedsLayout() + // DDLogDebug("nb: \(navigationBarPercentHidden) ev: \(extendedViewPercentHidden)") + let applyChanges = { + let changes = { + if shadowAlpha >= 0 { + self.shadowAlpha = shadowAlpha + } + if animated { + self.layoutIfNeeded() + } + additionalAnimations?() + } + if animated { + UIView.animate(withDuration: 0.2, animations: changes) + } else { + changes() + } + } + + if let underBarViewPercentHiddenForShowingTitle = self.underBarViewPercentHiddenForShowingTitle { + UIView.animate(withDuration: 0.2, animations: { + self.delegate?.title = underBarViewPercentHidden >= underBarViewPercentHiddenForShowingTitle ? self.title : nil + }, completion: { (_) in + applyChanges() + }) + } else { + applyChanges() + } + } + + private func updateTitleBarConstraints() { + let isUsingTitleBarInsteadOfNavigationBar = (displayType == .largeTitle || displayType == .centeredLargeTitle) + underBarViewTopTitleBarBottomConstraint.isActive = isUsingTitleBarInsteadOfNavigationBar + underBarViewTopBarBottomConstraint.isActive = !isUsingTitleBarInsteadOfNavigationBar && displayType != .hidden + underBarViewTopBottomConstraint.isActive = displayType == .hidden + bar.isHidden = isUsingTitleBarInsteadOfNavigationBar || displayType == .hidden + titleBar.isHidden = !isUsingTitleBarInsteadOfNavigationBar || displayType == .hidden + updateBarTopSpacing() + setNeedsUpdateConstraints() + } + + public override func safeAreaInsetsDidChange() { + super.safeAreaInsetsDidChange() + updateBarTopSpacing() + } + + // collapse bar top spacing if there's no status bar + private func updateBarTopSpacing() { + guard displayType == .largeTitle || displayType == .centeredLargeTitle else { + barTopSpacing = 0 + return + } + let isSafeAreaInsetsTopGreaterThanZero = safeAreaInsets.top > 0 + barTopSpacing = isSafeAreaInsetsTopGreaterThanZero ? 30 : 0 + titleBarHeightConstraint.constant = isSafeAreaInsetsTopGreaterThanZero ? 44 : 32 // it doesn't seem like there's a way to force update of bar metrics - as it stands the bar height gets stuck in whatever mode the app was launched in + } + + private func updateShadowConstraints() { + shadowTopUnderBarViewBottomConstraint.isActive = isShadowBelowUnderBarView + shadowTopExtendedViewBottomConstraint.isActive = !isShadowBelowUnderBarView + setNeedsUpdateConstraints() + } + + // Only used by this class and NavigationBarHider + var barHeight: CGFloat { + return (displayType == .largeTitle || displayType == .centeredLargeTitle ? titleBar.frame.height : bar.frame.height) + } + + // Only used by this class and NavigationBarHider + var underBarViewHeight: CGFloat { + return underBarView.frame.size.height + } + + // Only used by this class and NavigationBarHider + var extendedViewHeight: CGFloat { + return extendedView.frame.size.height + } + + // Only used by this class and NavigationBarHider + var topSpacingHideableHeight: CGFloat { + return isTopSpacingHidingEnabled ? barTopSpacing : 0 + } + + // Only used by this class and NavigationBarHider + var barHideableHeight: CGFloat { + return isBarHidingEnabled ? barHeight : 0 + } + + // Only used by this class and NavigationBarHider + var underBarViewHideableHeight: CGFloat { + return isUnderBarViewHidingEnabled ? underBarViewHeight : 0 + } + + // Only used by this class and NavigationBarHider + var extendedViewHideableHeight: CGFloat { + return isExtendedViewHidingEnabled ? extendedViewHeight : 0 + } + + // Only used by this class and NavigationBarHider + var hideableHeight: CGFloat { + return topSpacingHideableHeight + barHideableHeight + underBarViewHideableHeight + extendedViewHideableHeight + } + + public override func layoutSubviews() { + super.layoutSubviews() + let navigationBarPercentHidden = _navigationBarPercentHidden + let extendedViewPercentHidden = _extendedViewPercentHidden + let underBarViewPercentHidden = _underBarViewPercentHidden + let topSpacingPercentHidden = safeAreaInsets.top > 0 ? _topSpacingPercentHidden : 1 // treat top spacing as hidden if there's no status bar so that the title is smaller + + let underBarViewHeight = underBarView.frame.height + let barHeight = self.barHeight + let extendedViewHeight = extendedView.frame.height + + visibleHeight = statusBarUnderlay.frame.size.height + barHeight * (1.0 - navigationBarPercentHidden) + extendedViewHeight * (1.0 - extendedViewPercentHidden) + underBarViewHeight * (1.0 - underBarViewPercentHidden) + (barTopSpacing * (1.0 - topSpacingPercentHidden)) + + let spacingTransformHeight = barTopSpacing * topSpacingPercentHidden + let barTransformHeight = barHeight * navigationBarPercentHidden + spacingTransformHeight + let extendedViewTransformHeight = extendedViewHeight * extendedViewPercentHidden + let underBarTransformHeight = underBarViewHeight * underBarViewPercentHidden + + hiddenHeight = barTransformHeight + extendedViewTransformHeight + underBarTransformHeight + + let barTransform = CGAffineTransform(translationX: 0, y: 0 - barTransformHeight) + let barScaleTransform = CGAffineTransform(scaleX: 1.0 - navigationBarPercentHidden * navigationBarPercentHidden, y: 1.0 - navigationBarPercentHidden * navigationBarPercentHidden) + + self.bar.transform = barTransform + self.titleBar.transform = barTransform + + if isTitleShrinkingEnabled { + let titleScale: CGFloat = 1.0 - 0.2 * topSpacingPercentHidden + self.titleView?.transform = CGAffineTransform(scaleX: titleScale, y: titleScale) + } + + for subview in self.bar.subviews { + for subview in subview.subviews { + subview.transform = barScaleTransform + } + } + self.bar.alpha = min(backgroundAlpha, (1.0 - 2.0 * navigationBarPercentHidden).wmf_normalizedPercentage) + self.titleBar.alpha = self.bar.alpha + + let totalTransform = CGAffineTransform(translationX: 0, y: 0 - hiddenHeight) + self.backgroundView.transform = totalTransform + + let underBarTransform = CGAffineTransform(translationX: 0, y: 0 - barTransformHeight - underBarTransformHeight) + self.underBarView.transform = underBarTransform + + if isUnderBarFadingEnabled { + self.underBarView.alpha = 1.0 - underBarViewPercentHidden + } + + self.extendedView.transform = totalTransform + + if isExtendedViewFadingEnabled { + self.extendedView.alpha = min(backgroundAlpha, 1.0 - extendedViewPercentHidden) + } else { + self.extendedView.alpha = CGFloat(1).isLessThanOrEqualTo(extendedViewPercentHidden) ? 0 : backgroundAlpha + } + + self.progressView.transform = isShadowBelowUnderBarView ? underBarTransform : totalTransform + self.shadow.transform = isShadowBelowUnderBarView ? underBarTransform : totalTransform + } + + + @objc public func setProgressHidden(_ hidden: Bool, animated: Bool) { + let changes = { + self.progressView.alpha = min(hidden ? 0 : 1, self.backgroundAlpha) + } + if animated { + UIView.animate(withDuration: 0.2, animations: changes) + } else { + changes() + } + } + + @objc public func setProgress(_ progress: Float, animated: Bool) { + progressView.setProgress(progress, animated: animated) + } + + @objc public var progress: Float { + get { + return progressView.progress + } + set { + progressView.progress = newValue + } + } + + @objc public func addExtendedNavigationBarView(_ view: UIView) { + guard extendedView.subviews.first == nil else { + return + } + extendedViewHeightConstraint.isActive = false + extendedView.wmf_addSubviewWithConstraintsToEdges(view) + } + + @objc public func removeExtendedNavigationBarView() { + guard let subview = extendedView.subviews.first else { + return + } + subview.removeFromSuperview() + extendedViewHeightConstraint.isActive = true + } + + @objc public func addUnderNavigationBarView(_ view: UIView, shouldIgnoreSafeArea: Bool = false) { + guard underBarView.subviews.first == nil else { + return + } + underBarViewHeightConstraint.isActive = false + shouldUnderBarIgnoreSafeArea = shouldIgnoreSafeArea + underBarView.wmf_addSubviewWithConstraintsToEdges(view) + } + + @objc public func removeUnderNavigationBarView() { + guard let subview = underBarView.subviews.first else { + return + } + subview.removeFromSuperview() + underBarViewHeightConstraint.isActive = true + } + + @objc public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + + if allowsUnderbarHitsFallThrough + && underBarView.frame.contains(point) + && !bar.frame.contains(point) { + + for subview in underBarView.subviews { + let convertedPoint = self.convert(point, to: subview) + if subview.point(inside: convertedPoint, with: event) { + return true + } + } + + return false + } + + if allowsExtendedHitsFallThrough + && extendedView.frame.contains(point) + && !bar.frame.contains(point) { + + for subview in extendedView.subviews { + let convertedPoint = self.convert(point, to: subview) + if subview.point(inside: convertedPoint, with: event) { + return true + } + } + + return false + } + + return point.y <= visibleHeight + } + + public var backgroundAlpha: CGFloat = 1 { + didSet { + statusBarUnderlay.alpha = backgroundAlpha + backgroundView.alpha = backgroundAlpha + bar.alpha = backgroundAlpha + titleBar.alpha = backgroundAlpha + extendedView.alpha = backgroundAlpha + if backgroundAlpha < progressView.alpha { + progressView.alpha = backgroundAlpha + } + } + } +} + +extension NavigationBar: Themeable { + public func apply(theme: Theme) { + self.theme = theme + + backgroundColor = .clear + + statusBarUnderlay.backgroundColor = theme.colors.paperBackground + backgroundView.backgroundColor = theme.colors.paperBackground + + titleBar.setBackgroundImage(theme.navigationBarBackgroundImage, forToolbarPosition: .any, barMetrics: .default) + titleBar.isTranslucent = false + titleBar.tintColor = theme.colors.chromeText + titleBar.setShadowImage(theme.navigationBarShadowImage, forToolbarPosition: .any) + titleBar.barTintColor = theme.colors.chromeBackground + if let items = titleBar.items { + for item in items { + if let label = item.customView as? UILabel { + label.textColor = theme.colors.chromeText + } else if item.image == nil { + item.tintColor = theme.colors.link + } + } + } + + bar.setBackgroundImage(theme.navigationBarBackgroundImage, for: .default) + bar.titleTextAttributes = theme.navigationBarTitleTextAttributes + bar.isTranslucent = false + bar.barTintColor = theme.colors.chromeBackground + bar.shadowImage = theme.navigationBarShadowImage + bar.tintColor = theme.colors.chromeText + + extendedView.backgroundColor = .clear + underBarView.backgroundColor = .clear + + shadow.backgroundColor = theme[keyPath: shadowColorKeyPath] + + progressView.progressViewStyle = .bar + progressView.trackTintColor = .clear + progressView.progressTintColor = theme.colors.link + } +} + +extension NavigationBar: UINavigationBarDelegate { + public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool { + delegate?.navigationController?.popViewController(animated: true) + return false + } + + public func navigationBar(_ navigationBar: UINavigationBar, didPop item: UINavigationItem) { + /// During iOS 14's long press to access back history, this function is called *after* the unneeded navigationItems have been popped off. + /// However, with our custom navBar the actual articleVC isn't changed. So we need to find the articleVC for the top navItem, and pop to it. + /// This should be in `shouldPop`, but as of iOS 14.0, `shouldPop` isn't called when long pressing a back button. Once this is fixed by Apple, + /// we should move this to `shouldPop` to improve animations. (Update: A bug tracker was filed w/ Apple, and this won't be fixed anytime soon. + /// Apple: "This is expected behavior. Due to side effects that many clients have in the shouldPop handler, we do not consult it when using the back + /// button menu. We instead recommend that you hide the back button when you wish to disallow popping past a particular point in the navigation stack.") + if let topNavigationItem = navigationBar.items?.last, + let navController = delegate?.navigationController, + let tappedViewController = navController.viewControllers.first(where: {$0.navigationItem == topNavigationItem}) { + delegate?.navigationController?.popToViewController(tappedViewController, animated: true) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/NavigationState.swift b/Apps/Wikipedia/WMF Framework/NavigationState.swift new file mode 100644 index 0000000..01512dc --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/NavigationState.swift @@ -0,0 +1,106 @@ +public struct NavigationState: Codable { + static let libraryKey = "nav_state" + + public var viewControllers: [ViewController] + public var shouldAttemptLogin: Bool + + public init(viewControllers: [ViewController], shouldAttemptLogin: Bool) { + self.viewControllers = viewControllers + self.shouldAttemptLogin = shouldAttemptLogin + } + + public struct ViewController: Codable { + public var kind: Kind + public var presentation: Presentation + public var info: Info? + public var children: [ViewController] + + public mutating func updateChildren(_ children: [ViewController]) { + self.children = children + } + + public init?(kind: Kind?, presentation: Presentation, info: Info? = nil, children: [ViewController] = []) { + guard let kind = kind else { + return nil + } + self.kind = kind + self.presentation = presentation + self.info = info + self.children = children + } + + public enum Kind: Int, Codable { + case tab + + case article + case random + case themeableNavigationController + case settings + + case account + case talkPage + case talkPageReplyList + + case singleWebPage + + case readingListDetail + + case detail + + init?(from rawValue: Int?) { + guard let rawValue = rawValue else { + return nil + } + self.init(rawValue: rawValue) + } + } + + public enum Presentation: Int, Codable { + case push + case modal + } + + public struct Info: Codable { + public var selectedIndex: Int? + + public var articleKey: String? + public var articleSectionAnchor: String? + + public var talkPageSiteURLString: String? + public var talkPageTitle: String? + public var talkPageTypeRawValue: Int? + + public var currentSavedViewRawValue: Int? + + public var readingListURIString: String? + + public var searchTerm: String? + + public var shouldShowNavigationBar: Bool? + + public var contentGroupIDURIString: String? + + public var presentedContentGroupKey: String? + + public var url: URL? + + // TODO: Remove after moving to Swift 5.1 - + // https://github.com/apple/swift-evolution/blob/master/proposals/0242-default-values-memberwise.md + public init(url: URL? = nil, selectedIndex: Int? = nil, articleKey: String? = nil, articleSectionAnchor: String? = nil, talkPageSiteURLString: String? = nil, talkPageTitle: String? = nil, talkPageTypeRawValue: Int? = nil, currentSavedViewRawValue: Int? = nil, readingListURIString: String? = nil, searchTerm: String? = nil, shouldShowNavigationBar: Bool? = nil, contentGroupIDURIString: String? = nil, presentedContentGroupKey: String? = nil) { + self.url = url + self.selectedIndex = selectedIndex + self.articleKey = articleKey + self.articleSectionAnchor = articleSectionAnchor + self.talkPageSiteURLString = talkPageSiteURLString + self.talkPageTitle = talkPageTitle + self.talkPageTypeRawValue = talkPageTypeRawValue + self.currentSavedViewRawValue = currentSavedViewRawValue + self.readingListURIString = readingListURIString + self.searchTerm = searchTerm + self.shouldShowNavigationBar = shouldShowNavigationBar + self.contentGroupIDURIString = contentGroupIDURIString + self.presentedContentGroupKey = presentedContentGroupKey + } + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/PageIDToURLFetcher.swift b/Apps/Wikipedia/WMF Framework/PageIDToURLFetcher.swift new file mode 100644 index 0000000..d7668e0 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/PageIDToURLFetcher.swift @@ -0,0 +1,96 @@ +import Foundation + +public final class PageIDToURLFetcher: Fetcher { + + private let maxNumPageIDs = 50 + + /// Fetches equivalent page URLs for every pageID passed in. Automatically makes additional calls if number of pageIDs is greater than API maximum (50). + public func fetchPageURLs(_ siteURL: URL, pageIDs: [Int], failure: @escaping WMFErrorHandler, success: @escaping ([URL]) -> Void) { + + guard !pageIDs.isEmpty else { + failure(RequestError.invalidParameters) + return + } + + let pageIDChunks = pageIDs.chunked(into: maxNumPageIDs) + + var finalURLs: [URL] = [] + var errors: [Error] = [] + + let group = DispatchGroup() + + for pageIDChunk in pageIDChunks { + + group.enter() + fetchMaximumPageURLs(siteURL, pageIDs: pageIDChunk) { error in + DispatchQueue.main.async { + errors.append(error) + group.leave() + } + } success: { urls in + DispatchQueue.main.async { + finalURLs.append(contentsOf: urls) + group.leave() + } + } + } + + group.notify(queue: .main) { + if let error = errors.first { + failure(error) + } else if finalURLs.isEmpty { + failure(RequestError.unexpectedResponse) + } else { + success(finalURLs) + } + } + + } + + /// Fetches equivalent page URLs for every pageID passed in. Maximum of 50 page IDs allowed. Use fetchPageURLs(siteURL:pageID:failure:success) method if requesting > 50 pageIDs. + private func fetchMaximumPageURLs(_ siteURL: URL, pageIDs: [Int], failure: @escaping WMFErrorHandler, success: @escaping ([URL]) -> Void) { + + guard pageIDs.count <= maxNumPageIDs else { + failure(RequestError.invalidParameters) + return + } + + var params: [String: AnyObject] = [ + "action": "query" as AnyObject, + "prop": "info" as AnyObject, + "inprop": "url" as AnyObject, + "format": "json" as AnyObject + ] + + let stringPageIDs = pageIDs.map { String($0) } + params["pageids"] = stringPageIDs.joined(separator: "|") as AnyObject + + performMediaWikiAPIGET(for: siteURL, with: params, cancellationKey: nil) { (result, response, error) in + if let error = error { + failure(error) + return + } + guard let result = result else { + failure(RequestError.unexpectedResponse) + return + } + + guard let query = result["query"] as? [String: Any], + let pages = query["pages"] as? [String: AnyObject] else { + failure(RequestError.unexpectedResponse) + return + } + + var finalURLs: [URL] = [] + for (_, value) in pages { + guard let fullURLString = value["fullurl"] as? String, + let url = URL(string: fullURLString) else { + continue + } + finalURLs.append(url) + } + + success(finalURLs) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/PageNamespace.swift b/Apps/Wikipedia/WMF Framework/PageNamespace.swift new file mode 100644 index 0000000..b56bfb7 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/PageNamespace.swift @@ -0,0 +1,110 @@ +import Foundation + +// Emum for namespaces common amongst most Wikipedia languages. +@objc public enum PageNamespace: Int, Codable { + case media = -2 + case special = -1 + case main = 0 + case talk = 1 + case user = 2 + case userTalk = 3 + case wikipedia = 4 + case wikipediaTalk = 5 + case file = 6 + case fileTalk = 7 + case mediawiki = 8 + case mediawikiTalk = 9 + case template = 10 + case templateTalk = 11 + case help = 12 + case helpTalk = 13 + case category = 14 + case cateogryTalk = 15 + case thread = 90 + case threadTalk = 91 + case summary = 92 + case summaryTalk = 93 + case portal = 100 + case portalTalk = 101 + case project = 102 + case projectTalk = 103 + // case ambiguous1 = 104 + // case ambiguous2 = 105 + // case ambiguous3 = 106 + // case ambiguous4 = 107 + case book = 108 + case bookTalk = 109 + // case ambiguous5 = 110 + // case ambiguous6 = 111 + case draft = 118 + case draftTalk = 119 + case educationProgram = 446 + case educationProgramTalk = 447 + case campaign = 460 + case campaignTalk = 461 + case timedText = 710 + case timedTextTalk = 711 + case module = 828 + case moduleTalk = 829 + case gadget = 2300 + case gadgetTalk = 2301 + case gadgetDefinition = 2302 + case gadgetDefinitionTalk = 2303 + case topic = 2600 + + public var canonicalName: String { + switch self { + case .media: return "Media" + case .special: return "Special" + case .talk: return "Talk" + case .user: return "User" + case .userTalk: return "User talk" + case .wikipedia: return "Wikipedia" + case .wikipediaTalk: return "Wikipedia talk" + case .file: return "File" + case .fileTalk: return "File talk" + case .mediawiki: return "MediaWiki" + case .mediawikiTalk: return "MediaWiki talk" + case .template: return "Template" + case .templateTalk: return "Template talk" + case .help: return "Help" + case .helpTalk: return "Help talk" + case .category: return "Category" + case .cateogryTalk: return "Category talk" + case .portal: return "Portal" + case .portalTalk: return "Portal talk" + case .draft: return "Draft" + case .draftTalk: return "Draft talk" + case .timedText: return "TimedText" + case .timedTextTalk: return "TimedText talk" + case .module: return "Module" + case .moduleTalk: return "Module talk" + case .gadget: return "Gadget" + case .gadgetTalk: return "Gadget talk" + case .gadgetDefinition: return "Gadget definition" + case .gadgetDefinitionTalk: return "Gadget definition talk" + default: + return "" + } + } + + public var convertedToOrFromTalk: PageNamespace? { + switch self { + case .main: + return .talk + case .talk: + return .main + default: + return nil + } + } +} + +extension PageNamespace { + public init?(namespaceValue: Int?) { + guard let rawValue = namespaceValue else { + return nil + } + self.init(rawValue: rawValue) + } +} diff --git a/Apps/Wikipedia/WMF Framework/PeriodicWorker.swift b/Apps/Wikipedia/WMF Framework/PeriodicWorker.swift new file mode 100644 index 0000000..44cdefc --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/PeriodicWorker.swift @@ -0,0 +1,52 @@ +import Foundation + +@objc(WMFPeriodicWorker) public protocol PeriodicWorker: NSObjectProtocol { + func doPeriodicWork(_ completion: @escaping () -> Void) +} + +@objc(WMFPeriodicWorkerController) public class PeriodicWorkerController: WorkerController { + let interval: TimeInterval + let initialDelay: TimeInterval + let leeway: TimeInterval + + lazy var workTimer: RepeatingTimer = { + assert(Thread.isMainThread) + return RepeatingTimer(interval, afterDelay: initialDelay, leeway: leeway) { [weak self] in + self?.doPeriodicWork() + } + }() + + @objc(initWithInterval:initialDelay:leeway:) public required init(_ interval: TimeInterval, initialDelay: TimeInterval, leeway: TimeInterval) { + self.interval = interval + self.initialDelay = initialDelay + self.leeway = leeway + } + + var workers = [PeriodicWorker]() + + @objc public func add(_ worker: PeriodicWorker) { + workers.append(worker) + } + + @objc public func start() { + workTimer.resume() + } + + @objc public func stop() { + workTimer.pause() + } + + @objc public func doPeriodicWork(_ completion: (() -> Void)? = nil) { + let identifier = UUID().uuidString + delegate?.workerControllerWillStart(self, workWithIdentifier: identifier) + workers.asyncForEach({ (worker, completion) in + worker.doPeriodicWork(completion) + }) { [weak self] () in + completion?() + guard let strongSelf = self else { + return + } + strongSelf.delegate?.workerControllerDidEnd(strongSelf, workWithIdentifier: identifier) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/PermanentCacheController.swift b/Apps/Wikipedia/WMF Framework/PermanentCacheController.swift new file mode 100644 index 0000000..fc2d0f5 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/PermanentCacheController.swift @@ -0,0 +1,73 @@ +import Foundation + +@objc(WMFPermanentCacheController) +public final class PermanentCacheController: NSObject { + public let imageCache: ImageCacheController + public let articleCache: ArticleCacheController + let urlCache: PermanentlyPersistableURLCache + let managedObjectContext: NSManagedObjectContext + + /// - Parameter moc: the managed object context for the cache + /// - Parameter session: the session to utilize for image and article requests + /// - Parameter configuration: the configuration to utilize for configuring requests + /// - Parameter preferredLanguageDelegate: the preferredLanguageDelegate to utilize for determining the user's preferred languages + @objc public init(moc: NSManagedObjectContext, session: Session, configuration: Configuration, preferredLanguageDelegate: WMFPreferredLanguageInfoProvider) { + imageCache = ImageCacheController(moc: moc, session: session, configuration: configuration) + articleCache = ArticleCacheController(moc: moc, imageCacheController: imageCache, session: session, configuration: configuration, preferredLanguageDelegate: preferredLanguageDelegate) + urlCache = PermanentlyPersistableURLCache(moc: moc) + managedObjectContext = moc + super.init() + session.permanentCache = self + } + + /// Performs any necessary migrations on the CacheController's internal storage + /// Exists on this @objc PermanentCacheController so it can be accessed from WMFDataStore + @objc public static func setupCoreDataStack(_ completion: @escaping (NSManagedObjectContext?, Error?) -> Void) { + CacheController.setupCoreDataStack(completion) + } + + /// Performs any necessary teardown on CacheController's internal storage + /// Exists on this @objc PermanentCacheController so it can be accessed from WMFDataStore + @objc public func teardown(_ completion: @escaping () -> Void) { + imageCache.cancelAllTasks() + articleCache.cancelAllTasks() + managedObjectContext.perform { + // Give a bit of a buffer for other async activity to cease + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100), execute: completion) + } + } + + @objc public func fetchImage(withURL url: URL?, failure: @escaping (Error) -> Void, success: @escaping (ImageDownload) -> Void) { + imageCache.fetchImage(withURL: url, failure: failure, success: success) + } + + @objc public func memoryCachedImage(withURL url: URL) -> Image? { + return imageCache.memoryCachedImage(withURL: url) + } + + @objc public func fetchImage(withURL url: URL?, priority: Float, failure: @escaping (Error) -> Void, success: @escaping (ImageDownload) -> Void) -> String? { + return imageCache.fetchImage(withURL: url, priority: priority, failure: failure, success: success) + } + + @objc public func cancelImageFetch(withURL url: URL?, token: String?) { + imageCache.cancelFetch(withURL: url, token: token) + } + + @objc public func cachedImage(withURL url: URL?) -> Image? { + return imageCache.cachedImage(withURL: url) + } + + @objc public func fetchData(withURL url: URL, failure: @escaping (Error) -> Void, success: @escaping (Data, URLResponse) -> Void) { + imageCache.fetchData(withURL: url, failure: failure, success: success) + } + + @objc public func imageData(withURL url: URL) -> TypedImageData? { + return imageCache.data(withURL: url) + } + #if TEST + @objc public static func testController(with directory: URL, dataStore: MWKDataStore) -> PermanentCacheController { + let moc = CacheController.createCacheContext(cacheURL: directory)! + return PermanentCacheController(moc: moc, session: dataStore.session, configuration: dataStore.configuration, preferredLanguageDelegate: dataStore.languageLinkController) + } + #endif +} diff --git a/Apps/Wikipedia/WMF Framework/PermanentlyPersistableURLCache.swift b/Apps/Wikipedia/WMF Framework/PermanentlyPersistableURLCache.swift new file mode 100644 index 0000000..f1472c6 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/PermanentlyPersistableURLCache.swift @@ -0,0 +1,780 @@ +import Foundation +import CocoaLumberjackSwift + +public struct Header { + public static let persistentCacheItemType = "Persistent-Cache-Item-Type" + + // existence of a PersistItemType in a URLRequest header indicates to the system that we want to reference the persistent cache for the use of passing through Etags (If-None-Match) and falling back on a cached response (or other variant of) in the case of a urlSession error. + // pass PersistItemType header value urlRequest headers to gain different behaviors on how a request interacts with the cache, such as: + // for reading: + // article & imageInfo both set If-None-Match request header value based on previous cached E-tags in response headers + // there might be different fallback ordering logic if that particular variant is not cached but others are (image prioritizes by variant size, article by device language preferences) + // for writing: + // .image Cache database keys are saved as [host + "__" + imageName] pattern, variants = size prefix in url + // article Cache database keys are saved as .wmf_databaseURL, variants = detected preferred language variant. + // imageInfo Cache database keys are malformed with wmf_databaseURL, so it is saved as absoluteString.precomposedStringWithCanonicalMapping, variant = nil. + public enum PersistItemType: String { + case image = "Image" + case article = "Article" + case imageInfo = "ImageInfo" + } +} + +class PermanentlyPersistableURLCache: URLCache { + let cacheManagedObjectContext: NSManagedObjectContext + + init(moc: NSManagedObjectContext) { + cacheManagedObjectContext = moc + super.init(memoryCapacity: URLCache.shared.memoryCapacity, diskCapacity: URLCache.shared.diskCapacity, diskPath: nil) + } + +// MARK: Public - Overrides + + override func getCachedResponse(for dataTask: URLSessionDataTask, completionHandler: @escaping (CachedURLResponse?) -> Void) { + super.getCachedResponse(for: dataTask) { (response) in + if let response = response { + completionHandler(response) + return + } + guard let request = dataTask.originalRequest else { + completionHandler(nil) + return + } + completionHandler(self.permanentlyCachedResponse(for: request)) + } + + } + override func cachedResponse(for request: URLRequest) -> CachedURLResponse? { + if let response = super.cachedResponse(for: request) { + return response + } + let cachedResponse = permanentlyCachedResponse(for: request) + return cachedResponse + } + + + override func storeCachedResponse(_ cachedResponse: CachedURLResponse, for request: URLRequest) { + super.storeCachedResponse(cachedResponse, for: request) + + updateCacheWithCachedResponse(cachedResponse, request: request) + } + + override func storeCachedResponse(_ cachedResponse: CachedURLResponse, for dataTask: URLSessionDataTask) { + super.storeCachedResponse(cachedResponse, for: dataTask) + + if let request = dataTask.originalRequest { + updateCacheWithCachedResponse(cachedResponse, request: request) + } + } +} + +// MARK: Public - URLRequest Creation + +extension PermanentlyPersistableURLCache { + func urlRequestFromURL(_ url: URL, type: Header.PersistItemType, cachePolicy: WMFCachePolicy? = nil) -> URLRequest { + + var request = URLRequest(url: url) + + let typeHeaders = typeHeadersForType(type) + + for (key, value) in typeHeaders { + request.setValue(value, forHTTPHeaderField: key) + } + + let additionalHeaders = additionalHeadersForType(type, urlRequest: request) + + for (key, value) in additionalHeaders { + request.setValue(value, forHTTPHeaderField: key) + } + + if let cachePolicy = cachePolicy { + switch cachePolicy { + case .foundation(let cachePolicy): + request.cachePolicy = cachePolicy + request.prefersPersistentCacheOverError = true + case .noPersistentCacheOnError: + request.cachePolicy = .reloadIgnoringLocalCacheData + request.prefersPersistentCacheOverError = false + } + } + + return request + } + + func typeHeadersForType(_ type: Header.PersistItemType) -> [String: String] { + return [Header.persistentCacheItemType: type.rawValue] + } + + func additionalHeadersForType(_ type: Header.PersistItemType, urlRequest: URLRequest) -> [String: String] { + + var headers: [String: String] = [:] + + // add If-None-Match, otherwise it will not be populated if URLCache.shared is cleared but persistent cache exists. + switch type { + case .article, .imageInfo: + guard let cachedHeaders = permanentlyCachedHeaders(for: urlRequest) else { + break + } + headers[URLRequest.ifNoneMatchHeaderKey] = cachedHeaders[HTTPURLResponse.etagHeaderKey] + case .image: + break + } + + return headers + } + + func isCachedWithURLRequest(_ urlRequest: URLRequest, completion: @escaping (Bool) -> Void) { + guard let itemKey = itemKeyForURLRequest(urlRequest) else { + completion(false) + return + } + let moc = cacheManagedObjectContext + let variant = variantForURLRequest(urlRequest) + + return CacheDBWriterHelper.isCached(itemKey: itemKey, variant: variant, in: moc, completion: completion) + } +} + +// MARK: Private - URLRequest header creation + +private extension PermanentlyPersistableURLCache { + + func addEtagHeaderToURLRequest(_ urlRequest: inout URLRequest, type: Header.PersistItemType) { + + if let cachedUrlResponse = self.cachedResponse(for: urlRequest)?.response as? HTTPURLResponse { + for (key, value) in cachedUrlResponse.allHeaderFields { + if let keyString = key as? String, + let valueString = value as? String, + keyString == HTTPURLResponse.etagHeaderKey { + urlRequest.setValue(valueString, forHTTPHeaderField: URLRequest.ifNoneMatchHeaderKey) + } + } + } + } +} + +// MARK: Database key and variant creation + +extension PermanentlyPersistableURLCache { + + func itemKeyForURLRequest(_ urlRequest: URLRequest) -> String? { + guard let url = urlRequest.url, + let type = typeFromURLRequest(urlRequest: urlRequest) else { + return nil + } + + return itemKeyForURL(url, type: type) + } + + func variantForURLRequest(_ urlRequest: URLRequest) -> String? { + guard let url = urlRequest.url, + let type = typeFromURLRequest(urlRequest: urlRequest) else { + return nil + } + + return variantForURL(url, type: type) + } + + func itemKeyForURL(_ url: URL, type: Header.PersistItemType) -> String? { + switch type { + case .image: + return imageItemKeyForURL(url) + case .article: + return articleItemKeyForURL(url) + case .imageInfo: + return imageInfoItemKeyForURL(url) + } + } + + func variantForURL(_ url: URL, type: Header.PersistItemType) -> String? { + switch type { + case .image: + return imageVariantForURL(url) + case .article: + return articleVariantForURL(url) + case .imageInfo: + return imageInfoVariantForURL(url) + } + } +} + +private extension PermanentlyPersistableURLCache { + + func imageItemKeyForURL(_ url: URL) -> String? { + guard let host = url.host, let imageName = WMFParseImageNameFromSourceURL(url) else { + return url.absoluteString.precomposedStringWithCanonicalMapping + } + return (host + "__" + imageName).precomposedStringWithCanonicalMapping + } + + func articleItemKeyForURL(_ url: URL) -> String? { + return url.wmf_databaseKey + } + + func imageInfoItemKeyForURL(_ url: URL) -> String? { + return url.absoluteString.precomposedStringWithCanonicalMapping + } + + func imageVariantForURL(_ url: URL) -> String? { + let sizePrefix = WMFParseSizePrefixFromSourceURL(url) + return sizePrefix == NSNotFound ? "0" : String(sizePrefix) + } + + func articleVariantForURL(_ url: URL) -> String? { + return url.wmf_languageVariantCode + } + + func imageInfoVariantForURL(_ url: URL) -> String? { + return nil + } +} + +extension PermanentlyPersistableURLCache { + + func uniqueHeaderFileNameForItemKey(_ itemKey: CacheController.ItemKey, variant: String?) -> String { + let fileName = uniqueFileNameForItemKey(itemKey, variant: variant) + + return fileName + "__Header" + } + + func uniqueFileNameForURLRequest(_ urlRequest: URLRequest) -> String? { + + guard let url = urlRequest.url, + let type = typeFromURLRequest(urlRequest: urlRequest) else { + return nil + } + + return uniqueFileNameForURL(url, type: type) + } + + func uniqueFileNameForItemKey(_ itemKey: CacheController.ItemKey, variant: String?) -> String { + + guard let variant = variant else { + let fileName = itemKey.precomposedStringWithCanonicalMapping + return fileName.sha256 ?? fileName + } + + let fileName = "\(itemKey)__\(variant)".precomposedStringWithCanonicalMapping + return fileName.sha256 ?? fileName + } + + func uniqueFileNameForURL(_ url: URL, type: Header.PersistItemType) -> String? { + + guard let itemKey = itemKeyForURL(url, type: type) else { + return nil + } + + let variant = variantForURL(url, type: type) + + return uniqueFileNameForItemKey(itemKey, variant: variant) + } + + func uniqueHeaderFileNameForURL(_ url: URL, type: Header.PersistItemType) -> String? { + + guard let itemKey = itemKeyForURL(url, type: type) else { + return nil + } + + let variant = variantForURL(url, type: type) + + return uniqueHeaderFileNameForItemKey(itemKey, variant: variant) + } +} + +// MARK: Private - Helpers + +private extension PermanentlyPersistableURLCache { + func typeFromURLRequest(urlRequest: URLRequest) -> Header.PersistItemType? { + guard let typeRaw = urlRequest.allHTTPHeaderFields?[Header.persistentCacheItemType], + let type = Header.PersistItemType(rawValue: typeRaw) else { + return nil + } + + return type + } +} + +// MARK: Public - Permanent Cache Writing + +enum PermanentlyPersistableURLCacheError: Error { + case unableToDetermineURLFromRequest + case unableToDetermineTypeFromRequest + case unableToDetermineHeaderOrContentFileName +} + +public enum CacheResponseContentType { + case data(Data) + case string(String) +} + +extension PermanentlyPersistableURLCache { + + func cacheResponse(httpUrlResponse: HTTPURLResponse, content: CacheResponseContentType, urlRequest: URLRequest, success: @escaping () -> Void, failure: @escaping (Error) -> Void) { + + guard let url = urlRequest.url else { + failure(PermanentlyPersistableURLCacheError.unableToDetermineURLFromRequest) + return + } + + guard let type = typeFromURLRequest(urlRequest: urlRequest) else { + failure(PermanentlyPersistableURLCacheError.unableToDetermineTypeFromRequest) + return + } + + guard let headerFileName = uniqueHeaderFileNameForURL(url, type: type), + let contentFileName = uniqueFileNameForURL(url, type: type) else { + failure(PermanentlyPersistableURLCacheError.unableToDetermineHeaderOrContentFileName) + return + } + + let dispatchGroup = DispatchGroup() + + dispatchGroup.enter() + var headerSaveError: Error? = nil + var contentSaveError: Error? = nil + + CacheFileWriterHelper.saveResponseHeader(httpUrlResponse: httpUrlResponse, toNewFileName: headerFileName) { (result) in + + defer { + dispatchGroup.leave() + } + + switch result { + case .success, .exists: + break + case .failure(let error): + headerSaveError = error + } + } + + switch content { + case .data((let data)): + dispatchGroup.enter() + CacheFileWriterHelper.saveData(data: data, toNewFileWithKey: contentFileName) { (result) in + + defer { + dispatchGroup.leave() + } + + switch result { + case .success, .exists: + break + case .failure(let error): + contentSaveError = error + } + } + case .string(let string): + dispatchGroup.enter() + CacheFileWriterHelper.saveContent(string, toNewFileName: contentFileName) { (result) in + defer { + dispatchGroup.leave() + } + + switch result { + case .success, .exists: + break + case .failure(let error): + contentSaveError = error + } + } + } + + dispatchGroup.notify(queue: DispatchQueue.global(qos: .default)) { [headerSaveError, contentSaveError] in + + if let contentSaveError = contentSaveError { + self.remove(fileName: headerFileName) { + failure(contentSaveError) + } + return + } + + if let headerSaveError = headerSaveError { + self.remove(fileName: contentFileName) { + failure(headerSaveError) + } + return + } + + success() + } + } + + // Bundled migration only - copies files into cache + func writeBundledFiles(mimeType: String, bundledFileURL: URL, urlRequest: URLRequest, completion: @escaping (Result) -> Void) { + + guard let url = urlRequest.url else { + completion(.failure(PermanentlyPersistableURLCacheError.unableToDetermineURLFromRequest)) + return + } + + guard let type = typeFromURLRequest(urlRequest: urlRequest) else { + completion(.failure(PermanentlyPersistableURLCacheError.unableToDetermineTypeFromRequest)) + return + } + + guard let headerFileName = uniqueHeaderFileNameForURL(url, type: type), + let contentFileName = uniqueFileNameForURL(url, type: type) else { + completion(.failure(PermanentlyPersistableURLCacheError.unableToDetermineHeaderOrContentFileName)) + return + } + + CacheFileWriterHelper.copyFile(from: bundledFileURL, toNewFileWithKey: contentFileName) { (result) in + switch result { + case .success, .exists: + CacheFileWriterHelper.saveResponseHeader(headerFields: ["Content-Type": mimeType], toNewFileName: headerFileName) { (result) in + switch result { + case .success, .exists: + completion(.success(())) + case .failure(let error): + completion(.failure(error)) + } + } + + case .failure(let error): + completion(.failure(error)) + } + } + } + + private func remove(fileName: String, completion: () -> Void) { + + // remove from file system + let fileURL = CacheFileWriterHelper.fileURL(for: fileName) + do { + try FileManager.default.removeItem(at: fileURL) + } catch let error as NSError { + DDLogError("Error removing file: \(error)") + } + + completion() + } + + private func updateCacheWithCachedResponse(_ cachedResponse: CachedURLResponse, request: URLRequest) { + + func customCacheUpdatingItemKeyForURLRequest(_ urlRequest: URLRequest) -> String? { + + // this inner method is a workaround to allow the mobile-html URLRequest with a revisionID in the url to update the cached response under the revisionless url. + // we intentionally don't want to modify the itemKeyForURLRequest(_ urlRequest: URLRequest) method to keep this a lighter touch + + guard let url = urlRequest.customCacheUpdatingURL ?? urlRequest.url, + let type = typeFromURLRequest(urlRequest: urlRequest) else { + return nil + } + + return itemKeyForURL(url, type: type) + } + + func clearCustomCacheUpdatingResponseFromFoundation(with urlRequest: URLRequest) { + + // If we have a custom cache url to update, we need to remove that from foundation's URLCache, otherwise that + // will still take over even if we have updated the saved article cache. + + if let customCacheUpdatingURL = urlRequest.customCacheUpdatingURL { + let updatingRequest = URLRequest(url: customCacheUpdatingURL) + removeCachedResponse(for: updatingRequest) + } + } + + let isArticleOrImageInfoRequest: Bool + if let typeRaw = request.allHTTPHeaderFields?[Header.persistentCacheItemType], + let type = Header.PersistItemType(rawValue: typeRaw), + (type == .article || type == .imageInfo) { + isArticleOrImageInfoRequest = true + } else { + isArticleOrImageInfoRequest = false + } + + // we only want to update specific variant for image types + // for articles and imageInfo's it's okay to update the alternative language variants in the cache. + let variant: String? = isArticleOrImageInfoRequest ? nil : variantForURLRequest(request) + + clearCustomCacheUpdatingResponseFromFoundation(with: request) + + guard let itemKey = customCacheUpdatingItemKeyForURLRequest(request), + let httpResponse = cachedResponse.response as? HTTPURLResponse, + httpResponse.statusCode == 200 else { + return + } + + let moc = cacheManagedObjectContext + + CacheDBWriterHelper.isCached(itemKey: itemKey, variant: variant, in: moc, completion: { (isCached) in + guard isCached else { + return + } + + let cachedHeaders = self.permanentlyCachedHeaders(for: request) + let cachedETag = cachedHeaders?[HTTPURLResponse.etagHeaderKey] + let responseETag = httpResponse.allHeaderFields[HTTPURLResponse.etagHeaderKey] as? String + guard cachedETag == nil || cachedETag != responseETag else { + return + } + + let headerFileName: String + let contentFileName: String + + if isArticleOrImageInfoRequest, + let topVariant = CacheDBWriterHelper.allDownloadedVariantItems(itemKey: itemKey, in: moc).first { + + headerFileName = self.uniqueHeaderFileNameForItemKey(itemKey, variant: topVariant.variant) + contentFileName = self.uniqueFileNameForItemKey(itemKey, variant: topVariant.variant) + + } else { + headerFileName = self.uniqueHeaderFileNameForItemKey(itemKey, variant: variant) + contentFileName = self.uniqueFileNameForItemKey(itemKey, variant: variant) + } + + CacheFileWriterHelper.replaceResponseHeaderWithURLResponse(httpResponse, atFileName: headerFileName) { (result) in + switch result { + case .success: + break + case .failure(let error): + DDLogError("Failed updating cached header file: \(error)") + case .exists: + assertionFailure("This shouldn't happen.") + break + } + } + + CacheFileWriterHelper.replaceFileWithData(cachedResponse.data, fileName: contentFileName) { (result) in + switch result { + case .success: + break + case .failure(let error): + DDLogError("Failed updating cached content file: \(error)") + case .exists: + assertionFailure("This shouldn't happen.") + break + } + } + }) + + } +} + +// MARK: Private - Permanent Cache Fetching + +private extension PermanentlyPersistableURLCache { + + func permanentlyCachedHeaders(for request: URLRequest) -> [String: String]? { + guard let url = request.url, + let typeRaw = request.allHTTPHeaderFields?[Header.persistentCacheItemType], + let type = Header.PersistItemType(rawValue: typeRaw) else { + return nil + } + guard let responseHeaderFileName = uniqueHeaderFileNameForURL(url, type: type) else { + return nil + } + guard let responseHeaderData = FileManager.default.contents(atPath: CacheFileWriterHelper.fileURL(for: responseHeaderFileName).path) else { + return nil + } + return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(responseHeaderData) as? [String: String] + } + + func permanentlyCachedResponse(for request: URLRequest) -> CachedURLResponse? { + + // 1. try pulling from Persistent Cache + if let persistedCachedResponse = persistedResponseWithURLRequest(request) { + return persistedCachedResponse + // 2. else try pulling a fallback from Persistent Cache + } else if let fallbackCachedResponse = fallbackPersistedResponse(urlRequest: request, moc: cacheManagedObjectContext) { + return fallbackCachedResponse + } + + return nil + } + + enum PersistedResponseRequest { + case urlAndType(url: URL, type: Header.PersistItemType) + case fallbackItemKeyAndVariant(url: URL, itemKey: String, variant: String?) + } + + func persistedResponseWithURLRequest(_ urlRequest: URLRequest) -> CachedURLResponse? { + + guard let url = urlRequest.url, + let typeRaw = urlRequest.allHTTPHeaderFields?[Header.persistentCacheItemType], + let type = Header.PersistItemType(rawValue: typeRaw) else { + return nil + } + + let request = PersistedResponseRequest.urlAndType(url: url, type: type) + return persistedResponseWithRequest(request) + } + + func persistedResponseWithRequest(_ request: PersistedResponseRequest) -> CachedURLResponse? { + + let maybeResponseFileName: String? + let maybeResponseHeaderFileName: String? + let url: URL + + switch request { + case .urlAndType(let inURL, let type): + url = inURL + maybeResponseFileName = uniqueFileNameForURL(url, type: type) + maybeResponseHeaderFileName = uniqueHeaderFileNameForURL(url, type: type) + case .fallbackItemKeyAndVariant(let inURL, let itemKey, let variant): + url = inURL + maybeResponseFileName = uniqueFileNameForItemKey(itemKey, variant: variant) + maybeResponseHeaderFileName = uniqueHeaderFileNameForItemKey(itemKey, variant: variant) + } + + guard let responseFileName = maybeResponseFileName, + let responseHeaderFileName = maybeResponseHeaderFileName else { + return nil + } + + // assert(!Thread.isMainThread) + + guard let responseData = FileManager.default.contents(atPath: CacheFileWriterHelper.fileURL(for: responseFileName).path) else { + return nil + } + + guard let responseHeaderData = FileManager.default.contents(atPath: CacheFileWriterHelper.fileURL(for: responseHeaderFileName).path) else { + + return nil + } + + var responseHeaders: [String: String]? + do { + if let unarchivedHeaders = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(responseHeaderData) as? [String: String] { + responseHeaders = unarchivedHeaders + } + } catch { + + } + + if let httpResponse = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: responseHeaders) { + return CachedURLResponse(response: httpResponse, data: responseData) + } + + return nil + } + + func fallbackPersistedResponse(urlRequest: URLRequest, moc: NSManagedObjectContext) -> CachedURLResponse? { + + guard let url = urlRequest.url, + let typeRaw = urlRequest.allHTTPHeaderFields?[Header.persistentCacheItemType], + let type = Header.PersistItemType(rawValue: typeRaw), + let itemKey = itemKeyForURL(url, type: type) else { + return nil + } + + // lookup fallback itemKey/variant in DB (language fallback logic for article item type, size fallback logic for image item type) + + var response: CachedURLResponse? = nil + moc.performAndWait { + var allVariantItems = CacheDBWriterHelper.allDownloadedVariantItems(itemKey: itemKey, in: moc) + + switch type { + case .image: + allVariantItems.sortAsImageCacheItems() + case .article, .imageInfo: + break + } + + if let fallbackItemKey = allVariantItems.first?.key { + + let fallbackVariant = allVariantItems.first?.variant + + // migrated images do not have urls. defaulting to url passed in here. + let fallbackURL = allVariantItems.first?.url ?? url + + // first see if URLCache has the fallback + let quickCheckRequest = URLRequest(url: fallbackURL) + if let systemCachedResponse = URLCache.shared.cachedResponse(for: quickCheckRequest) { + response = systemCachedResponse + } + + // then see if persistent cache has the fallback + let request = PersistedResponseRequest.fallbackItemKeyAndVariant(url: fallbackURL, itemKey: fallbackItemKey, variant: fallbackVariant) + response = persistedResponseWithRequest(request) + } + } + + return response + } +} + +public extension HTTPURLResponse { + static let etagHeaderKey = "Etag" + static let varyHeaderKey = "Vary" + static let acceptLanguageHeaderValue = "Accept-Language" +} + +public extension URLRequest { + static let ifNoneMatchHeaderKey = "If-None-Match" + static let customCachePolicyHeaderKey = "Custom-Cache-Policy" + static let customCacheUpdatingURL = "Custom-Cache-Updating-URL" + + var prefersPersistentCacheOverError: Bool { + get { + if let customCachePolicyValue = allHTTPHeaderFields?[URLRequest.customCachePolicyHeaderKey], + let intCustomCachePolicyValue = UInt(customCachePolicyValue), + intCustomCachePolicyValue == WMFCachePolicy.noPersistentCacheOnError.rawValue { + return false + } + + return true + } + set { + let value = newValue ? nil : String(WMFCachePolicy.noPersistentCacheOnError.rawValue) + setValue(value, forHTTPHeaderField: URLRequest.customCachePolicyHeaderKey) + } + + } + + // if you need the response to this request written to the cache stored at a different url, set this value + var customCacheUpdatingURL: URL? { + get { + guard let urlString = allHTTPHeaderFields?[URLRequest.customCacheUpdatingURL] else { + return nil + } + return URL(string: urlString) + } + set { + setValue(newValue?.absoluteString, forHTTPHeaderField: URLRequest.customCacheUpdatingURL) + } + } +} + +public extension Array where Element == CacheController.ItemKeyAndVariant { + mutating func sortAsImageItemKeyAndVariants() { + sort { (lhs, rhs) -> Bool in + + guard let lhsVariant = lhs.variant, + let lhsSize = Int64(lhsVariant), + let rhsVariant = rhs.variant, + let rhsSize = Int64(rhsVariant) else { + return true + } + // 0 is original so treat it as larger than others + if rhsSize == 0 { + return true + } else if lhsSize == 0 { + return false + } + return lhsSize < rhsSize + } + } +} + +public extension Array where Element: CacheItem { + mutating func sortAsImageCacheItems() { + sort { (lhs, rhs) -> Bool in + + guard let lhsVariant = lhs.variant, + let lhsSize = Int64(lhsVariant), + let rhsVariant = rhs.variant, + let rhsSize = Int64(rhsVariant) else { + return true + } + // 0 is original so treat it as larger than others + if rhsSize == 0 { + return true + } else if lhsSize == 0 { + return false + } + return lhsSize < rhsSize + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/PersistentCache.xcdatamodeld/NewCache.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/PersistentCache.xcdatamodeld/NewCache.xcdatamodel/contents new file mode 100644 index 0000000..9b707fe --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/PersistentCache.xcdatamodeld/NewCache.xcdatamodel/contents @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/ReadingList+CoreDataClass.swift b/Apps/Wikipedia/WMF Framework/ReadingList+CoreDataClass.swift new file mode 100644 index 0000000..5774c54 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ReadingList+CoreDataClass.swift @@ -0,0 +1,86 @@ +import Foundation +import CoreData + +public class ReadingList: NSManagedObject { + + @objc public static let entriesLimitReachedNotification = NSNotification.Name(rawValue:"WMFEntriesLimitReachedNotification") + @objc public static let entriesLimitReachedReadingListKey = "readingList" + + // Note that this returns articleKey strings that do not take language variant into account. + // This allows clients to check if an article of any variant is an entry in the list. + // This is intentional. + public var articleKeys: [String] { + let entries = self.entries ?? [] + let existingKeys = entries.compactMap { (entry) -> String? in + guard entry.isDeletedLocally == false else { + return nil + } + return entry.articleKey + } + return existingKeys + } + + private var previousCountOfEntries: Int64 = 0 + private var isEntriesLimitReached: Bool = false { + didSet { + guard isEntriesLimitReached, countOfEntries > previousCountOfEntries else { + return + } + let userInfo: [String: Any] = [ReadingList.entriesLimitReachedReadingListKey: self] + NotificationCenter.default.post(name: ReadingList.entriesLimitReachedNotification, object: nil, userInfo: userInfo) + } + } + + public func updateArticlesAndEntries() throws { + previousCountOfEntries = countOfEntries + + let previousArticles = articles ?? [] + let previousKeys = Set(previousArticles.compactMap { $0.inMemoryKey }) + let validEntries = (entries ?? []).filter { !$0.isDeletedLocally } + let validArticleKeys = Set(validEntries.compactMap { $0.inMemoryKey }) + for article in previousArticles { + guard let key = article.inMemoryKey, validArticleKeys.contains(key) else { + removeFromArticles(article) + article.readingListsDidChange() + continue + } + } + if !validArticleKeys.isEmpty { + let articleKeysToAdd = validArticleKeys.subtracting(previousKeys) + let articlesToAdd = try managedObjectContext?.fetchArticlesWithInMemoryURLKeys(Array(articleKeysToAdd)) ?? [] + countOfEntries = Int64(validEntries.count) + for article in articlesToAdd { + addToArticles(article) + article.readingListsDidChange() + } + let sortedArticles = articles?.sorted(by: { (a, b) -> Bool in + guard let aDate = a.savedDate else { + return false + } + guard let bDate = b.savedDate else { + return true + } + return aDate.compare(bDate) == .orderedDescending + }) ?? [] + let updatedPreviewArticles = NSMutableOrderedSet() + for article in sortedArticles { + guard updatedPreviewArticles.count < 4 else { + break + } + guard article.imageURLString != nil || article.thumbnailURLString != nil else { + continue + } + updatedPreviewArticles.add(article) + } + previewArticles = updatedPreviewArticles + } else { + countOfEntries = 0 + articles = [] + previewArticles = [] + } + + if let moc = managedObjectContext { + isEntriesLimitReached = countOfEntries >= moc.wmf_readingListsConfigMaxEntriesPerList.int64Value + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/ReadingList+CoreDataProperties.swift b/Apps/Wikipedia/WMF Framework/ReadingList+CoreDataProperties.swift new file mode 100644 index 0000000..bd5bd1c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ReadingList+CoreDataProperties.swift @@ -0,0 +1,115 @@ +import Foundation +import CoreData + +extension ReadingList { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "ReadingList") + } + + @NSManaged public var canonicalName: String? + @NSManaged public var color: String? + @NSManaged public var countOfEntries: Int64 + @NSManaged public var createdDate: NSDate? + @NSManaged public var errorCode: String? + @NSManaged public var iconName: String? + @NSManaged public var imageName: String? + @NSManaged public var isDefault: Bool + @NSManaged public var isDeletedLocally: Bool + @NSManaged public var isUpdatedLocally: Bool + @NSManaged public var readingListDescription: String? + @NSManaged public var readingListID: NSNumber? + @NSManaged public var updatedDate: NSDate? + @NSManaged public var articles: Set? + @NSManaged public var entries: Set? + @NSManaged public var previewArticles: NSOrderedSet? + @NSManaged public var sortOrder: NSNumber? + + public var name: String? { + get { + return isDefault ? CommonStrings.readingListsDefaultListTitle : canonicalName + } + set { + canonicalName = newValue?.precomposedStringWithCanonicalMapping + } + } + + public var APIError: APIReadingListError? { + guard let errorCode = errorCode ?? entries?.first(where: { !$0.isDeletedLocally && $0.errorCode != nil })?.errorCode else { + return nil + } + return APIReadingListError(rawValue: errorCode) + } + + @objc static let defaultListCanonicalName = "default" + +} + +// MARK: Generated accessors for articles +extension ReadingList { + + @objc(addArticlesObject:) + @NSManaged public func addToArticles(_ value: WMFArticle) + + @objc(removeArticlesObject:) + @NSManaged public func removeFromArticles(_ value: WMFArticle) + + @objc(addArticles:) + @NSManaged public func addToArticles(_ values: NSSet) + + @objc(removeArticles:) + @NSManaged public func removeFromArticles(_ values: NSSet) + +} + +// MARK: Generated accessors for entries +extension ReadingList { + + @objc(addEntriesObject:) + @NSManaged public func addToEntries(_ value: ReadingListEntry) + + @objc(removeEntriesObject:) + @NSManaged public func removeFromEntries(_ value: ReadingListEntry) + + @objc(addEntries:) + @NSManaged public func addToEntries(_ values: NSSet) + + @objc(removeEntries:) + @NSManaged public func removeFromEntries(_ values: NSSet) + +} + +// MARK: Generated accessors for previewArticles +extension ReadingList { + + @objc(insertObject:inPreviewArticlesAtIndex:) + @NSManaged public func insertIntoPreviewArticles(_ value: WMFArticle, at idx: Int) + + @objc(removeObjectFromPreviewArticlesAtIndex:) + @NSManaged public func removeFromPreviewArticles(at idx: Int) + + @objc(insertPreviewArticles:atIndexes:) + @NSManaged public func insertIntoPreviewArticles(_ values: [WMFArticle], at indexes: NSIndexSet) + + @objc(removePreviewArticlesAtIndexes:) + @NSManaged public func removeFromPreviewArticles(at indexes: NSIndexSet) + + @objc(replaceObjectInPreviewArticlesAtIndex:withObject:) + @NSManaged public func replacePreviewArticles(at idx: Int, with value: WMFArticle) + + @objc(replacePreviewArticlesAtIndexes:withPreviewArticles:) + @NSManaged public func replacePreviewArticles(at indexes: NSIndexSet, with values: [WMFArticle]) + + @objc(addPreviewArticlesObject:) + @NSManaged public func addToPreviewArticles(_ value: WMFArticle) + + @objc(removePreviewArticlesObject:) + @NSManaged public func removeFromPreviewArticles(_ value: WMFArticle) + + @objc(addPreviewArticles:) + @NSManaged public func addToPreviewArticles(_ values: NSOrderedSet) + + @objc(removePreviewArticles:) + @NSManaged public func removeFromPreviewArticles(_ values: NSOrderedSet) + +} diff --git a/Apps/Wikipedia/WMF Framework/ReadingList+JSON.swift b/Apps/Wikipedia/WMF Framework/ReadingList+JSON.swift new file mode 100644 index 0000000..18dcccf --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ReadingList+JSON.swift @@ -0,0 +1,34 @@ +import Foundation + +extension ReadingList { + @objc public static let conflictingReadingListNameUpdatedNotification = NSNotification.Name(rawValue: "WMFConflictingReadingListNameUpdatedNotification") + @objc public static let conflictingReadingListNameUpdatedOldNameKey = NSNotification.Name(rawValue: "oldName") + @objc public static let conflictingReadingListNameUpdatedNewNameKey = NSNotification.Name(rawValue: "newName") + + func update(with remoteList: APIReadingList) { + self.readingListID = NSNumber(value: remoteList.id) + if remoteList.name == CommonStrings.readingListsDefaultListTitle { + let userCreatedAnnotation = WMFLocalizedString("reading-list-name-user-created-annotation", value: "_[user created]", comment: "Annotation added to a conflicting reading list name") + let newName = String.localizedStringWithFormat("%1$@%2$@", remoteList.name, userCreatedAnnotation) + if newName != self.name { + let userInfo = [ReadingList.conflictingReadingListNameUpdatedOldNameKey: remoteList.name, ReadingList.conflictingReadingListNameUpdatedNewNameKey: newName] + NotificationCenter.default.post(name: ReadingList.conflictingReadingListNameUpdatedNotification, object: nil, userInfo: userInfo) + } + self.name = newName + } else { + self.name = remoteList.name + } + self.readingListDescription = remoteList.description + if let createdDate = DateFormatter.wmf_iso8601().date(from: remoteList.created) { + self.createdDate = createdDate as NSDate + } + if let updatedDate = DateFormatter.wmf_iso8601().date(from: remoteList.updated) { + self.updatedDate = updatedDate as NSDate + } + if remoteList.isDefault && !isDefault { + isDefault = true + } + errorCode = nil + isUpdatedLocally = false + } +} diff --git a/Apps/Wikipedia/WMF Framework/ReadingListAlertType.swift b/Apps/Wikipedia/WMF Framework/ReadingListAlertType.swift new file mode 100644 index 0000000..b533954 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ReadingListAlertType.swift @@ -0,0 +1,22 @@ +public enum ReadingListAlertType { + case listLimitExceeded(limit: Int) + case entryLimitExceeded(limit: Int) + case genericNotSynced + case downloading + case articleError(_: ArticleError) +} + +extension ReadingListAlertType: Equatable { + public static func ==(lhs: ReadingListAlertType, rhs: ReadingListAlertType) -> Bool { + switch (lhs, rhs) { + case (.listLimitExceeded, .listLimitExceeded), + (.entryLimitExceeded, .entryLimitExceeded), + (.genericNotSynced, .genericNotSynced), + (.downloading, .downloading), + (.articleError, .articleError): + return true + default: + return false + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/ReadingListEntry+CoreDataClass.swift b/Apps/Wikipedia/WMF Framework/ReadingListEntry+CoreDataClass.swift new file mode 100644 index 0000000..5b476c1 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ReadingListEntry+CoreDataClass.swift @@ -0,0 +1,18 @@ +import Foundation +import CoreData + +public class ReadingListEntry: NSManagedObject { + public var inMemoryKey: WMFInMemoryURLKey? { + guard let key = articleKey else { + return nil + } + return WMFInMemoryURLKey(databaseKey: key, languageVariantCode: variant) + } + + public var APIError: APIReadingListError? { + guard let errorCode = errorCode else { + return nil + } + return APIReadingListError(rawValue: errorCode) + } +} diff --git a/Apps/Wikipedia/WMF Framework/ReadingListEntry+CoreDataProperties.swift b/Apps/Wikipedia/WMF Framework/ReadingListEntry+CoreDataProperties.swift new file mode 100644 index 0000000..38e9457 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ReadingListEntry+CoreDataProperties.swift @@ -0,0 +1,21 @@ +import Foundation +import CoreData + +extension ReadingListEntry { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "ReadingListEntry") + } + + @NSManaged public var readingListEntryID: NSNumber? + @NSManaged public var createdDate: NSDate? + @NSManaged public var updatedDate: NSDate? + @NSManaged public var displayTitle: String? + @NSManaged public var list: ReadingList? + @NSManaged public var articleKey: String? + @NSManaged public var variant: String? + @NSManaged public var isDeletedLocally: Bool + @NSManaged public var isUpdatedLocally: Bool + @NSManaged public var errorCode: String? + +} diff --git a/Apps/Wikipedia/WMF Framework/ReadingListEntry+JSON.swift b/Apps/Wikipedia/WMF Framework/ReadingListEntry+JSON.swift new file mode 100644 index 0000000..19e93fa --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ReadingListEntry+JSON.swift @@ -0,0 +1,15 @@ +import Foundation + +extension ReadingListEntry { + func update(with remoteEntry: APIReadingListEntry) { + readingListEntryID = NSNumber(value: remoteEntry.id) + let remoteCreatedDate = DateFormatter.wmf_iso8601().date(from: remoteEntry.created) + createdDate = remoteCreatedDate as NSDate? + let remoteUpdatedDate = DateFormatter.wmf_iso8601().date(from: remoteEntry.updated) + updatedDate = remoteUpdatedDate as NSDate? + errorCode = nil + isUpdatedLocally = false + } +} + + diff --git a/Apps/Wikipedia/WMF Framework/ReadingListsAPIController.swift b/Apps/Wikipedia/WMF Framework/ReadingListsAPIController.swift new file mode 100644 index 0000000..ed0ff91 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ReadingListsAPIController.swift @@ -0,0 +1,554 @@ +import Foundation +import CocoaLumberjackSwift + +internal let APIReadingListUpdateLimitForFullSyncFallback = 1000 // if we receive over this # of updated items, fall back to full sync + +public enum APIReadingListError: String, Error, Equatable { + case generic = "readinglists-client-error-generic" + case notLoggedIn = "notloggedin" + case badtoken = "badtoken" + case notSetup = "readinglists-db-error-not-set-up" + case alreadySetUp = "readinglists-db-error-already-set-up" + case listLimit = "readinglists-db-error-list-limit" + case entryLimit = "readinglists-db-error-entry-limit" + case duplicateEntry = "readinglists-db-error-duplicate-page" + case needsFullSync = "readinglists-client-error-needs-full-sync" + case listDeleted = "readinglists-db-error-list-deleted" + case listEntryDeleted = "readinglists-db-error-list-entry-deleted" + case defaultListCannotBeUpdated = "readinglists-db-error-cannot-update-default-list" + case defaultListCannotBeDeleted = "readinglists-db-error-cannot-delete-default-list" + case noSuchProject = "readinglists-db-error-no-such-project" + case noSuchListEntry = "readinglists-db-error-no-such-list-entry" + case noSuchList = "readinglists-db-error-no-such-list" + case duplicateList = "readinglists-db-error-duplicate-list" + + public var localizedDescription: String { + switch self { + case .listLimit: + return WMFLocalizedString("reading-list-api-error-list-limit", value: "This list is not synced because you have reached the limit for the number of synced lists.", comment: "You have too many lists.") + case .entryLimit: + return WMFLocalizedString("reading-list-api-error-entry-limit", value: "This entry is not synced because you have reached the limit for the number of entries in this list.", comment: "You have too many entries in this list.") + default: + return WMFLocalizedString("reading-list-api-error-generic", value: "An unexpected error occurred while syncing your reading lists.", comment: "An unexpected error occurred while syncing your reading lists.") + } + } +} + +struct APIReadingLists: Codable { + let lists: [APIReadingList] + let next: String? + let since: String? + enum CodingKeys: String, CodingKey { + case lists + case next + case since = "continue-from" + } +} + +public struct APIReadingList: Codable { + enum CodingKeys: String, CodingKey { + case id + case name + case description + case created + case updated + case deleted + case isDefault = "default" + } + + public let id: Int64 + let name: String + let description: String + let created: String + let updated: String + let deleted: Bool? + let isDefault: Bool +} + +struct APIReadingListEntries: Codable { + let entries: [APIReadingListEntry] + let next: String? +} + +public struct APIReadingListEntry: Codable { + let id: Int64 + let project: String + let title: String + let created: String + let updated: String + let listId: Int64? + let deleted: Bool? +} + +struct APIReadingListChanges: Codable { + let lists: [APIReadingList]? + let entries: [APIReadingListEntry]? + let next: String? + let since: String? + enum CodingKeys: String, CodingKey { + case lists + case entries + case next + case since = "continue-from" + } +} + +struct APIReadingListErrorResponse: Codable { + let type: String? + let title: String + let method: String? + let detail: String? +} + +enum APIReadingListRequestType: String { + case setup, teardown +} + +/* Note that because the reading list API does not support language variants, + * the articleURL will always have a nil language variant. + * + * The RemoteReadingListArticleKey type is a type alias for String. + * Since ReadingListsSyncOperation handles remote entries that don't have a variant, + * and local entries that do have a variant, this type makes it more clear when + * a non-variant aware key is being used. + * + * Also, if the remote API adds variant support, it should be straightforward to + * update the type alias from String to WMFInMemoryURLKey. +*/ +typealias RemoteReadingListArticleKey = String +extension APIReadingListEntry { + var articleURL: URL? { + guard let site = URL(string: project) else { + return nil + } + return site.wmf_URL(withTitle: title) + } + + var articleKey: RemoteReadingListArticleKey? { + return articleURL?.wmf_databaseKey + } +} + +public class ReadingListsAPIController: Fetcher { + private let builder = Configuration.current.pageContentServiceBuilder(withWikiHost: "en.wikipedia.org") + private let basePathComponents = ["data", "lists"] + var lastRequestType: APIReadingListRequestType? + + fileprivate func get(path: [String], queryParameters: [String: Any]? = nil, completionHandler: @escaping (T?, URLResponse?, Error?) -> Swift.Void) { + let key = UUID().uuidString + let components = builder.components(byAppending: basePathComponents + path, queryParameters: queryParameters) + guard + let task = session.jsonDecodableTaskWithDecodableError(with: components.url, method: .get, completionHandler: { (result: T?, errorResult: APIReadingListErrorResponse?, response, error) in + if let errorResult = errorResult, let error = APIReadingListError(rawValue: errorResult.title) { + completionHandler(nil, nil, error) + } else { + completionHandler(result, response, error) + } + self.untrack(taskFor: key) + }) else { + return + } + track(task: task, for: key) + task.resume() + } + + fileprivate func requestWithCSRF(path: [String], method: Session.Request.Method, bodyParameters: [String: Any]? = nil, completion: @escaping ([String: Any]?, URLResponse?, Error?) -> Void) { + let components = builder.components(byAppending: basePathComponents + path) + requestMediaWikiAPIAuthToken(for: components.url, type: .csrf) { (result) in + switch result { + case .failure(let error): + completion(nil, nil, error) + case .success(let token): + let tokenQueryParameters = ["csrf_token": token.value] + var componentsWithToken = components + componentsWithToken.appendQueryParametersToPercentEncodedQuery(tokenQueryParameters) + let identifier = UUID().uuidString + let task = self.session.jsonDictionaryTask(with: componentsWithToken.url, method: method, bodyParameters: bodyParameters, completionHandler: { (result, response, error) in + defer { + self.untrack(taskFor: identifier) + } + if let apiErrorType = result?["title"] as? String, let apiError = APIReadingListError(rawValue: apiErrorType), apiError != .alreadySetUp { + DDLogDebug("RLAPI FAILED: \(method.stringValue) \(path) \(apiError)") + } else { + #if DEBUG + if let error = error { + DDLogDebug("RLAPI FAILED: \(method.stringValue) \(path) \(error)") + } else { + DDLogDebug("RLAPI: \(method.stringValue) \(path)") + } + #endif + } + completion(result, response, error) + }) + self.track(task: task, for: identifier) + task?.resume() + } + } + } + + fileprivate func post(path: [String], bodyParameters: [String: Any]? = nil, completion: @escaping ([String: Any]?, URLResponse?, Error?) -> Void) { + requestWithCSRF(path: path, method: .post, bodyParameters: bodyParameters, completion: completion) + } + + fileprivate func delete(path: [String], completion: @escaping ([String: Any]?, URLResponse?, Error?) -> Void) { + requestWithCSRF(path: path, method: .delete, completion: completion) + } + + fileprivate func put(path: [String], bodyParameters: [String: Any]? = nil, completion: @escaping ([String: Any]?, URLResponse?, Error?) -> Void) { + requestWithCSRF(path: path, method: .put, bodyParameters: bodyParameters, completion: completion) + } + + @objc func setupReadingLists(completion: @escaping (Error?) -> Void) { + let requestType = APIReadingListRequestType.setup + post(path: [requestType.rawValue]) { (result, response, error) in + self.lastRequestType = requestType + completion(error) + } + } + + @objc func teardownReadingLists(completion: @escaping (Error?) -> Void) { + let requestType = APIReadingListRequestType.teardown + post(path: [requestType.rawValue]) { (result, response, error) in + self.lastRequestType = requestType + completion(error) + } + } + + + /** + Creates a new reading list using the reading list API + - parameters: + - name: The name for the new list + - description: The description for the new list + - completion: Called after the request completes + - listID: The list ID if it was created + - error: Any error preventing list creation + */ + func createList(name: String, description: String?, completion: @escaping (_ listID: Int64?,_ error: Error?) -> Swift.Void ) { + let bodyParams = ["name": name.precomposedStringWithCanonicalMapping, "description": description ?? ""] + // empty string path is required to add the trailing slash, server 404s otherwise + post(path: [""], bodyParameters: bodyParams) { (result, response, error) in + guard let id = result?["id"] as? Int64 else { + completion(nil, error ?? ReadingListError.unableToCreateList) + return + } + completion(id, nil) + } + } + + /** + Creates a new reading list using the reading list API + - parameters: + - lists: The names and descriptions for the new lists + - completion: Called after the request completes + - listIDs: The list IDs if they were created + - error: Any error preventing list creation + */ + func createLists(_ lists: [(name: String, description: String?)], completion: @escaping (_ listIDs: [(Int64?, Error?)]?,_ error: Error?) -> Swift.Void ) { + guard !lists.isEmpty else { + completion([], nil) + return + } + let bodyParams = ["batch": lists.map { ["name": $0.name.precomposedStringWithCanonicalMapping, "description": $0.description ?? ""] } ] + post(path: ["batch"], bodyParameters: bodyParams) { (result, response, error) in + guard let batch = result?["batch"] as? [[String: Any]] else { + guard lists.count > 1 else { + completion([(nil, error ?? APIReadingListError.generic)], nil) + return + } + DispatchQueue.global().async { + let taskGroup = WMFTaskGroup() + var listsByName: [String: (Int64?, Error?)] = [:] + for list in lists { + taskGroup.enter() + self.createList(name: list.name, description: list.description, completion: { (listID, error) in + taskGroup.leave() + listsByName[list.name] = (listID, error) + }) + } + taskGroup.wait() + var listsOrErrors: [(Int64?, Error?)] = [] + for list in lists { + guard let list = listsByName[list.name] else { + completion(nil, ReadingListError.unableToCreateList) + return + } + listsOrErrors.append(list) + } + completion(listsOrErrors, nil) + } + return + } + completion(batch.compactMap { + let id = $0["id"] as? Int64 + var error: Error? = nil + if let errorString = $0["error"] as? String { + error = APIReadingListError(rawValue: errorString) ?? APIReadingListError.generic + } + return (id, error) + }, nil) + } + } + + /** + Adds a new entry to a reading list using the reading list API + - parameters: + - listID: The list ID of the list that is getting an entry + - project: The project name of the new entry + - title: The title of the new entry + - completion: Called after the request completes + - entryID: The entry ID if it was created + - error: Any error preventing entry creation + */ + func addEntryToList(withListID listID: Int64, project: String, title: String, completion: @escaping (_ entryID: Int64?,_ error: Error?) -> Swift.Void ) { + let title = title.precomposedStringWithCanonicalMapping + let project = project.precomposedStringWithCanonicalMapping + let bodyParams = ["project": project, "title": title] + // "" for trailing slash is required, server 404s otherwise + post(path: ["\(listID)", "entries", ""], bodyParameters: bodyParams) { (result, response, error) in + if let apiError = error as? APIReadingListError { + switch apiError { + case .duplicateEntry: + // TODO: Remove when error response returns ID + self.getAllEntriesForReadingListWithID(readingListID: listID, completion: { (entries, error) in + guard let entry = entries.first(where: { (entry) -> Bool in entry.title == title && entry.project == project }) else { + completion(nil, error ?? ReadingListError.unableToAddEntry) + return + } + completion(entry.id, nil) + }) + default: + completion(nil, apiError) + } + return + } else if let error = error { + completion(nil, error) + return + } + + guard let id = result?["id"] as? Int64 else { + completion(nil, ReadingListError.unableToAddEntry) + return + } + + completion(id, nil) + } + } + + /** + Adds a new entry to a reading list using the reading list API + - parameters: + - listID: The list ID of the list that is getting an entry + - entries: The project and titles for each new entry + - completion: Called after the request completes + - entryIDs: The entry IDs if they were created + - error: Any error preventing entry creation + */ + func addEntriesToList(withListID listID: Int64, entries: [(project: String, title: String)], completion: @escaping (_ entryIDs: [(Int64?, Error?)]?,_ error: Error?) -> Swift.Void ) { + guard !entries.isEmpty else { + completion([], nil) + return + } + let bodyParams = ["batch": entries.map { ["project": $0.project.precomposedStringWithCanonicalMapping, "title": $0.title.precomposedStringWithCanonicalMapping] } ] + post(path: ["\(listID)", "entries", "batch"], bodyParameters: bodyParams) { (result, response, error) in + if let apiError = error as? APIReadingListError, apiError != .listDeleted { + guard entries.count > 1 else { + completion([(nil, apiError)], nil) + return + } + self.getAllEntriesForReadingListWithID(readingListID: listID, completion: { (remoteEntries, getAllEntriesError) in + var remoteEntriesByProjectAndTitle: [String: [String: APIReadingListEntry]] = [:] + for remoteEntry in remoteEntries { + remoteEntriesByProjectAndTitle[remoteEntry.project.precomposedStringWithCanonicalMapping, default: [:]][remoteEntry.title.precomposedStringWithCanonicalMapping] = remoteEntry + } + let results: [(Int64?, Error?)] = entries.map { + let project = $0.project.precomposedStringWithCanonicalMapping + let title = $0.title.precomposedStringWithCanonicalMapping + guard let remoteEntry = remoteEntriesByProjectAndTitle[project]?[title] else { + return (nil, apiError == .entryLimit ? apiError : APIReadingListError.generic) + } + return (remoteEntry.id, nil) + } + completion(results, nil) + }) + return + } else if let error = error { + completion(nil, error) + return + } + + guard let result = result else { + completion(nil, ReadingListError.unableToAddEntry) + return + } + + guard let batch = result["batch"] as? [[String: Any]] else { + DDLogError("Unexpected result: \(result)") + completion(nil, ReadingListError.unableToAddEntry) + return + } + + completion(batch.compactMap { + let id = $0["id"] as? Int64 + var error: Error? = nil + if let errorString = $0["error"] as? String { + error = APIReadingListError(rawValue: errorString) ?? APIReadingListError.generic + } + return (id, error) + }, nil) + } + } + + + /** + Remove entry from reading list using the reading list API + - parameters: + - listID: The list ID of the list that will have an entry removed + - entryID: The entry ID to remove from the list + - completion: Called after the request completes + - error: Any error preventing entry deletion + */ + func removeEntry(withEntryID entryID: Int64, fromListWithListID listID: Int64, completion: @escaping (_ error: Error?) -> Swift.Void ) { + + delete(path: ["\(listID)", "entries", "\(entryID)"]) { (result, response, error) in + guard error == nil else { + completion(error ?? ReadingListError.unableToRemoveEntry) + return + } + completion(nil) + } + } + + /** + Deletes a reading list using the reading list API + - parameters: + - listID: The list ID of the list to delete + - completion: Called after the request completes + - error: Any error preventing list deletion + */ + func deleteList(withListID listID: Int64, completion: @escaping (_ error: Error?) -> Swift.Void ) { + delete(path: ["\(listID)"]) { (result, response, error) in + guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { + completion(error ?? ReadingListError.unableToDeleteList) + return + } + completion(nil) + } + } + + /** + Updates a reading list using the reading list API + - parameters: + - listID: The list ID of the list to update + - name: The name of the list + - description: The description of the list + - completion: Called after the request completes + - error: Any error preventing list update + */ + func updateList(withListID listID: Int64, name: String, description: String?, completion: @escaping (_ error: Error?) -> Swift.Void ) { + put(path: ["\(listID)"], bodyParameters: ["name": name, "description": description ?? ""]) { (result, response, error) in + guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { + completion(error ?? ReadingListError.unableToDeleteList) + return + } + completion(nil) + } + } + + + /** + Gets updated lists and entries list API + - parameters: + - since: The continuation token for this whole list of updates. Lets the server know the current state of the device. Currently an ISO 8601 date string + - next: The continuation within this whole list of updates (since is the start of the whole list, next is the next page) + - nextSince: The paramater to use for "since" the next time you call this method to get the updates that have happened since this update. + - lists: Lists to append to the results + - entries: Entries to append to the results + - lists: All updated lists + - entries: All updated entries + - since: The date to use for the next update call + - error: Any error + */ + func updatedListsAndEntries(since: String, next: String? = nil, nextSince: String? = nil, lists: [APIReadingList] = [], entries: [APIReadingListEntry] = [], completion: @escaping (_ lists: [APIReadingList], _ entries: [APIReadingListEntry], _ since: String?, _ error: Error?) -> Swift.Void ) { + var queryParameters: [String: Any]? = nil + if let next = next { + queryParameters = ["next": next] + } + get(path: ["changes", "since", "\(since)"], queryParameters: queryParameters) { (result: APIReadingListChanges?, response, error) in + guard let result = result, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { + completion([], [], nil, error ?? ReadingListError.generic) + return + } + var combinedLists = lists + if let lists = result.lists { + combinedLists.append(contentsOf: lists) + } + var combinedEntries = entries + if let entries = result.entries { + combinedEntries.append(contentsOf: entries) + } + let nextSince = nextSince ?? result.since + if let next = result.next { + if combinedLists.count + combinedEntries.count > APIReadingListUpdateLimitForFullSyncFallback { + completion([], [], nil, APIReadingListError.needsFullSync) + } else { + self.updatedListsAndEntries(since: since, next: next, nextSince: nextSince, lists: combinedLists, entries: combinedEntries, completion: completion) + } + } else { + completion(combinedLists, combinedEntries, nextSince, nil) + } + } + } + + /** + Gets all reading lists from the API + - parameters: + - next: Optional continuation token for this list of results + - lists: Lists to append to the results + - lists: All lists + - since: The string to use for the next /changes/since call + - error: Any error + */ + public func getAllReadingLists(next: String? = nil, nextSince: String? = nil, lists: [APIReadingList] = [], completion: @escaping ([APIReadingList], String?, Error?) -> Swift.Void ) { + var queryParameters: [String: Any]? = nil + if let next = next { + queryParameters = ["next": next] + } + // empty string path is required to add the trailing slash, server 404s otherwise + get(path: [""], queryParameters: queryParameters) { (apiListsResponse: APIReadingLists?, response, error) in + guard let apiListsResponse = apiListsResponse else { + completion([], nil, error) + return + } + var combinedList = lists + combinedList.append(contentsOf: apiListsResponse.lists) + let nextSince = nextSince ?? apiListsResponse.since + if let next = apiListsResponse.next { + self.getAllReadingLists(next: next, nextSince: nextSince, lists: combinedList, completion: completion) + } else { + completion(combinedList, nextSince, nil) + } + } + } + + public func getAllEntriesForReadingListWithID(next: String? = nil, entries: [APIReadingListEntry] = [], readingListID: Int64, completion: @escaping ([APIReadingListEntry], Error?) -> Swift.Void ) { + var queryParameters: [String: Any]? = nil + if let next = next { + queryParameters = ["next": next] + } + // "" for trailing slash is required, server 404s otherwise + get(path: ["\(readingListID)", "entries", ""], queryParameters: queryParameters) { (apiEntriesResponse: APIReadingListEntries?, response, error) in + guard let apiEntriesResponse = apiEntriesResponse else { + completion([], error) + return + } + var combinedList = entries + combinedList.append(contentsOf: apiEntriesResponse.entries) + if let next = apiEntriesResponse.next { + self.getAllEntriesForReadingListWithID(next: next, entries: combinedList, readingListID: readingListID, completion: completion) + } else { + completion(combinedList, nil) + } + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/ReadingListsOperation.swift b/Apps/Wikipedia/WMF Framework/ReadingListsOperation.swift new file mode 100644 index 0000000..98d1844 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ReadingListsOperation.swift @@ -0,0 +1,16 @@ +internal class ReadingListsOperation: AsyncOperation { + internal weak var readingListsController: ReadingListsController! + + internal var apiController: ReadingListsAPIController { + return readingListsController.apiController + } + + internal var dataStore: MWKDataStore { + return readingListsController.dataStore + } + + init(readingListsController: ReadingListsController) { + self.readingListsController = readingListsController + super.init() + } +} diff --git a/Apps/Wikipedia/WMF Framework/ReadingListsSyncOperation.swift b/Apps/Wikipedia/WMF Framework/ReadingListsSyncOperation.swift new file mode 100644 index 0000000..c2f1f89 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ReadingListsSyncOperation.swift @@ -0,0 +1,1076 @@ +import CocoaLumberjackSwift + +internal enum ReadingListsOperationError: Error { + case cancelled +} + +internal class ReadingListsSyncOperation: ReadingListsOperation { + var syncedReadingListsCount = 0 + var syncedReadingListEntriesCount = 0 + + override func execute() { + syncedReadingListsCount = 0 + syncedReadingListEntriesCount = 0 + + DispatchQueue.main.async { + self.dataStore.performBackgroundCoreDataOperation { (moc) in + do { + try self.executeSync(on: moc) + } catch let error { + DDLogError("error during sync operation: \(error)") + do { + if moc.hasChanges { + try moc.save() + } + } catch let error { + DDLogError("error during sync save: \(error)") + } + if let readingListError = error as? APIReadingListError, readingListError == .notSetup { + DispatchQueue.main.async { + self.readingListsController.setSyncEnabled(false, shouldDeleteLocalLists: false, shouldDeleteRemoteLists: false) + self.finish() + } + } else if let readingListError = error as? APIReadingListError, readingListError == .needsFullSync { + let readingListsController = self.readingListsController + DispatchQueue.main.async { + guard let oldState = readingListsController?.syncState else { + return + } + var newState = oldState + newState.insert(.needsSync) + if newState != oldState { + readingListsController?.syncState = newState + } + self.finish() + DispatchQueue.main.async { + readingListsController?.sync() + } + } + } else if let readingListError = error as? ReadingListsOperationError, readingListError == .cancelled { + self.apiController.cancelAllTasks() + self.finish() + } else { + self.finish(with: error) + } + } + } + } + } + + func executeSync(on moc: NSManagedObjectContext) throws { + + let syncEndpointsAreAvailable = moc.wmf_isSyncRemotelyEnabled + + let rawSyncState = moc.wmf_numberValue(forKey: WMFReadingListSyncStateKey)?.int64Value ?? 0 + var syncState = ReadingListSyncState(rawValue: rawSyncState) + + let taskGroup = WMFTaskGroup() + + let hasValidLocalCredentials = apiController.session.hasValidCentralAuthCookies(for: Configuration.current.wikipediaCookieDomain) + + if syncEndpointsAreAvailable && syncState.contains(.needsRemoteDisable) && hasValidLocalCredentials { + var disableReadingListsError: Error? = nil + taskGroup.enter() + apiController.teardownReadingLists(completion: { (error) in + disableReadingListsError = error + taskGroup.leave() + }) + taskGroup.wait() + if let error = disableReadingListsError { + try moc.save() + self.finish(with: error) + return + } else { + syncState.remove(.needsRemoteDisable) + moc.wmf_setValue(NSNumber(value: syncState.rawValue), forKey: WMFReadingListSyncStateKey) + try moc.save() + } + } + + if syncState.contains(.needsRandomLists) { + try executeRandomListPopulation(in: moc) + syncState.remove(.needsRandomLists) + moc.wmf_setValue(NSNumber(value: syncState.rawValue), forKey: WMFReadingListSyncStateKey) + try moc.save() + } + + if syncState.contains(.needsRandomEntries) { + try executeRandomArticlePopulation(in: moc, englishOnly: false) + syncState.remove(.needsRandomEntries) + moc.wmf_setValue(NSNumber(value: syncState.rawValue), forKey: WMFReadingListSyncStateKey) + try moc.save() + } else if syncState.contains(.needsRandomEnEntries) { + try executeRandomArticlePopulation(in: moc, englishOnly: true) + syncState.remove(.needsRandomEnEntries) + moc.wmf_setValue(NSNumber(value: syncState.rawValue), forKey: WMFReadingListSyncStateKey) + try moc.save() + } + + if syncState.contains(.needsLocalReset) { + try moc.wmf_batchProcessObjects(handler: { (readingListEntry: ReadingListEntry) in + readingListEntry.readingListEntryID = nil + readingListEntry.isUpdatedLocally = true + readingListEntry.errorCode = nil + guard !self.isCancelled else { + throw ReadingListsOperationError.cancelled + } + }) + try moc.wmf_batchProcessObjects(handler: { (readingList: ReadingList) in + readingList.readingListID = nil + readingList.isUpdatedLocally = true + readingList.errorCode = nil + guard !self.isCancelled else { + throw ReadingListsOperationError.cancelled + } + }) + syncState.remove(.needsLocalReset) + moc.wmf_setValue(NSNumber(value: syncState.rawValue), forKey: WMFReadingListSyncStateKey) + try moc.save() + moc.reset() + } + + if syncState.contains(.needsLocalListClear) { + try moc.wmf_batchProcess(matchingPredicate: NSPredicate(format: "isDefault != YES"), handler: { (lists: [ReadingList]) in + try self.readingListsController.markLocalDeletion(for: lists) + guard !self.isCancelled else { + throw ReadingListsOperationError.cancelled + } + }) + + syncState.remove(.needsLocalListClear) + moc.wmf_setValue(NSNumber(value: syncState.rawValue), forKey: WMFReadingListSyncStateKey) + try moc.save() + } + + if syncState.contains(.needsLocalArticleClear) { + try moc.wmf_batchProcess(matchingPredicate: NSPredicate(format: "savedDate != NULL"), handler: { (articles: [WMFArticle]) in + self.readingListsController.unsave(articles, in: moc) + guard !self.isCancelled else { + throw ReadingListsOperationError.cancelled + } + }) + + syncState.remove(.needsLocalArticleClear) + moc.wmf_setValue(NSNumber(value: syncState.rawValue), forKey: WMFReadingListSyncStateKey) + try moc.save() + } + + let localSyncOnly = { + try self.executeLocalOnlySync(on: moc) + try moc.save() + self.finish() + } + + guard hasValidLocalCredentials else { + try localSyncOnly() + return + } + + // local only sync + guard syncState != [] else { + if syncEndpointsAreAvailable { + // make an update call to see if the user has enabled sync on another device + var updateError: Error? = nil + taskGroup.enter() + let iso8601String = DateFormatter.wmf_iso8601().string(from: Date()) + apiController.updatedListsAndEntries(since: iso8601String, completion: { (lists, entries, since, error) in + updateError = error + taskGroup.leave() + }) + taskGroup.wait() + + if updateError == nil { + DispatchQueue.main.async { + self.readingListsController.setSyncEnabled(true, shouldDeleteLocalLists: false, shouldDeleteRemoteLists: false) + self.readingListsController.postReadingListsServerDidConfirmSyncWasEnabledForAccountNotification(true) + self.finish() + } + } else { + if let apiError = updateError as? APIReadingListError, apiError == .notSetup { + readingListsController.postReadingListsServerDidConfirmSyncWasEnabledForAccountNotification(false) + } + try localSyncOnly() + } + } else { + readingListsController.postReadingListsServerDidConfirmSyncWasEnabledForAccountNotification(false) + try localSyncOnly() + } + return + } + + guard syncEndpointsAreAvailable else { + self.finish() + return + } + + if syncState.contains(.needsRemoteEnable) { + var enableReadingListsError: Error? = nil + taskGroup.enter() + apiController.setupReadingLists(completion: { (error) in + enableReadingListsError = error + taskGroup.leave() + }) + taskGroup.wait() + if let error = enableReadingListsError { + try moc.save() + finish(with: error) + return + } else { + syncState.remove(.needsRemoteEnable) + moc.wmf_setValue(NSNumber(value: syncState.rawValue), forKey: WMFReadingListSyncStateKey) + self.readingListsController.postReadingListsServerDidConfirmSyncWasEnabledForAccountNotification(true) + try moc.save() + } + } + + if syncState.contains(.needsSync) { + try executeFullSync(on: moc) + syncState.remove(.needsSync) + syncState.insert(.needsUpdate) + } else if syncState.contains(.needsUpdate) { + try executeUpdate(on: moc) + } + + moc.wmf_setValue(NSNumber(value: syncState.rawValue), forKey: WMFReadingListSyncStateKey) + if moc.hasChanges { + try moc.save() + } + finish() + } + + func executeLocalOnlySync(on moc: NSManagedObjectContext) throws { + try moc.wmf_batchProcessObjects(matchingPredicate: NSPredicate(format: "isDeletedLocally == YES"), handler: { (list: ReadingList) in + moc.delete(list) + }) + try moc.wmf_batchProcessObjects(matchingPredicate: NSPredicate(format: "isDeletedLocally == YES"), handler: { (entry: ReadingListEntry) in + moc.delete(entry) + }) + } + + func executeFullSync(on moc: NSManagedObjectContext) throws { + let taskGroup = WMFTaskGroup() + var allAPIReadingLists: [APIReadingList] = [] + var getAllAPIReadingListsError: Error? + var nextSince: String? = nil + taskGroup.enter() + readingListsController.apiController.getAllReadingLists { (readingLists, since, error) in + getAllAPIReadingListsError = error + allAPIReadingLists = readingLists + nextSince = since + taskGroup.leave() + } + + taskGroup.wait() + guard !isCancelled else { + throw ReadingListsOperationError.cancelled + } + + if let getAllAPIReadingListsError = getAllAPIReadingListsError { + throw getAllAPIReadingListsError + } + + let group = WMFTaskGroup() + + syncedReadingListsCount += try createOrUpdate(remoteReadingLists: allAPIReadingLists, deleteMissingLocalLists: true, inManagedObjectContext: moc) + + // Get all entries + var remoteEntriesByReadingListID: [Int64: [APIReadingListEntry]] = [:] + for remoteReadingList in allAPIReadingLists { + group.enter() + apiController.getAllEntriesForReadingListWithID(readingListID: remoteReadingList.id, completion: { (entries, error) in + if let error = error { + DDLogError("Error fetching entries for reading list with ID \(remoteReadingList.id): \(error)") + } else { + remoteEntriesByReadingListID[remoteReadingList.id] = entries + } + group.leave() + }) + } + + group.wait() + guard !isCancelled else { + throw ReadingListsOperationError.cancelled + } + + for (readingListID, remoteReadingListEntries) in remoteEntriesByReadingListID { + syncedReadingListEntriesCount += try createOrUpdate(remoteReadingListEntries: remoteReadingListEntries, for: readingListID, deleteMissingLocalEntries: true, inManagedObjectContext: moc) + } + + if let since = nextSince { + moc.wmf_setValue(since as NSString, forKey: WMFReadingListUpdateKey) + } + + if moc.hasChanges { + try moc.save() + } + + try processLocalUpdates(in: moc) + + guard moc.hasChanges else { + return + } + try moc.save() + } + + func executeUpdate(on moc: NSManagedObjectContext) throws { + try processLocalUpdates(in: moc) + + if moc.hasChanges { + try moc.save() + } + + guard let since = moc.wmf_stringValue(forKey: WMFReadingListUpdateKey) else { + return + } + + var updatedLists: [APIReadingList] = [] + var updatedEntries: [APIReadingListEntry] = [] + var updateError: Error? + var nextSince: String? + + let taskGroup = WMFTaskGroup() + taskGroup.enter() + apiController.updatedListsAndEntries(since: since, completion: { (lists, entries, since, error) in + updateError = error + updatedLists = lists + updatedEntries = entries + nextSince = since + taskGroup.leave() + }) + + taskGroup.wait() + guard !isCancelled else { + throw ReadingListsOperationError.cancelled + } + + if let error = updateError { + throw error + } + + syncedReadingListsCount += try createOrUpdate(remoteReadingLists: updatedLists, inManagedObjectContext: moc) + syncedReadingListEntriesCount += try createOrUpdate(remoteReadingListEntries: updatedEntries, inManagedObjectContext: moc) + + if let since = nextSince { + moc.wmf_setValue(since as NSString, forKey: WMFReadingListUpdateKey) + } + + guard moc.hasChanges else { + return + } + try moc.save() + } + + func executeRandomListPopulation(in moc: NSManagedObjectContext) throws { + let countOfListsToCreate = moc.wmf_numberValue(forKey: "WMFCountOfListsToCreate")?.int64Value ?? 10 + for i in 1...countOfListsToCreate { + do { + _ = try readingListsController.createReadingList(named: "\(i)", in: moc) + } catch { + _ = try readingListsController.createReadingList(named: "\(arc4random())", in: moc) + } + } + try moc.save() + } + + func executeRandomArticlePopulation(in moc: NSManagedObjectContext, englishOnly: Bool) throws { + + let countOfEntriesToCreate = moc.wmf_numberValue(forKey: "WMFCountOfEntriesToCreate")?.int64Value ?? 10 + + var maybeSiteURL = URL(string: "https://en.wikipedia.org") + + let randomArticleFetcher = RandomArticleFetcher() + let taskGroup = WMFTaskGroup() + try moc.wmf_batchProcessObjects { (list: ReadingList) in + guard !list.isDefault else { + return + } + do { + var summaryResponses: [WMFInMemoryURLKey: ArticleSummary] = [:] + for i in 1...countOfEntriesToCreate { + + if !englishOnly { + if let randomLanguage = dataStore.languageLinkController.allLanguages.randomElement() { + maybeSiteURL = randomLanguage.siteURL + } + } + + guard let siteURL = maybeSiteURL else { + continue + } + + taskGroup.enter() + randomArticleFetcher.fetchRandomArticle(withSiteURL: siteURL, completion: { (error, result, summary) in + if let key = result?.wmf_inMemoryKey, let summary = summary { + summaryResponses[key] = summary + } + taskGroup.leave() + }) + if i % 16 == 0 || i == countOfEntriesToCreate { + taskGroup.wait() + guard !isCancelled else { + throw ReadingListsOperationError.cancelled + } + let articlesByKey = try moc.wmf_createOrUpdateArticleSummmaries(withSummaryResponses: summaryResponses) + let articles = Array(articlesByKey.values) + try readingListsController.add(articles: articles, to: list, in: moc) + if let defaultReadingList = moc.defaultReadingList { + try readingListsController.add(articles: articles, to: defaultReadingList, in: moc) + } + try moc.save() + summaryResponses.removeAll(keepingCapacity: true) + } + } + } catch let error { + DDLogError("error populating lists: \(error)") + } + } + } + + private func split(readingList: ReadingList, intoReadingListsOfSize size: Int, in moc: NSManagedObjectContext) throws -> [ReadingList] { + var newReadingLists: [ReadingList] = [] + + guard let readingListName = readingList.name else { + assertionFailure("Missing reading list name") + return newReadingLists + } + + let entries = Array(readingList.entries ?? []) + let entriesCount = Int(readingList.countOfEntries) + + let splitEntriesArrays = stride(from: size, to: entriesCount, by: size).map { + Array(entries[$0.. = ReadingList.fetchRequest() + listsToCreateOrUpdateFetch.sortDescriptors = [NSSortDescriptor(keyPath: \ReadingList.createdDate, ascending: false)] + listsToCreateOrUpdateFetch.predicate = NSPredicate(format: "isUpdatedLocally == YES") + let listsToUpdate = try moc.fetch(listsToCreateOrUpdateFetch) + var createdReadingLists: [Int64: ReadingList] = [:] + var failedReadingLists: [(ReadingList, APIReadingListError)] = [] + var updatedReadingLists: [Int64: ReadingList] = [:] + var deletedReadingLists: [Int64: ReadingList] = [:] + var listsToCreate: [ReadingList] = [] + var requestCount = 0 + for localReadingList in listsToUpdate { + autoreleasepool { + guard let readingListName = localReadingList.name else { + moc.delete(localReadingList) + return + } + guard let readingListID = localReadingList.readingListID?.int64Value else { + if localReadingList.isDeletedLocally || localReadingList.canonicalName == nil { + // if it has no id and is deleted locally, it can just be deleted + moc.delete(localReadingList) + } else if !localReadingList.isDefault { + let entryLimit = moc.wmf_readingListsConfigMaxListsPerUser + if localReadingList.countOfEntries > entryLimit && !UserDefaults.standard.wmf_didSplitExistingReadingLists() { + if let splitReadingLists = try? split(readingList: localReadingList, intoReadingListsOfSize: entryLimit, in: moc) { + listsToCreate.append(contentsOf: splitReadingLists) + } + } else { + listsToCreate.append(localReadingList) + } + } + return + } + if localReadingList.isDeletedLocally { + requestCount += 1 + taskGroup.enter() + self.apiController.deleteList(withListID: readingListID, completion: { (deleteError) in + defer { + taskGroup.leave() + } + guard deleteError == nil else { + DDLogError("Error deleting reading list: \(String(describing: deleteError))") + return + } + deletedReadingLists[readingListID] = localReadingList + }) + } else if localReadingList.isUpdatedLocally { + requestCount += 1 + taskGroup.enter() + self.apiController.updateList(withListID: readingListID, name: readingListName, description: localReadingList.readingListDescription, completion: { (updateError) in + defer { + taskGroup.leave() + } + guard updateError == nil else { + DDLogError("Error updating reading list: \(String(describing: updateError))") + return + } + updatedReadingLists[readingListID] = localReadingList + }) + } + } + } + + + let listNamesAndDescriptionsToCreate: [(name: String, description: String?)] = listsToCreate.map { + assert($0.canonicalName != nil) + return (name: $0.canonicalName ?? "", description: $0.readingListDescription) + } + var start = 0 + var end = 0 + while end < listNamesAndDescriptionsToCreate.count { + end = min(listNamesAndDescriptionsToCreate.count, start + WMFReadingListBatchSizePerRequestLimit) + autoreleasepool { + taskGroup.enter() + let listsToCreateSubarray = Array(listsToCreate[start.. = ReadingListEntry.fetchRequest() + entriesToCreateOrUpdateFetch.predicate = NSPredicate(format: "isUpdatedLocally == YES") + entriesToCreateOrUpdateFetch.sortDescriptors = [NSSortDescriptor(keyPath: \ReadingListEntry.createdDate, ascending: false)] + let localReadingListEntriesToUpdate = try moc.fetch(entriesToCreateOrUpdateFetch) + + + var deletedReadingListEntries: [Int64: ReadingListEntry] = [:] + var entriesToAddByListID: [Int64: [(project: String, title: String, entry: ReadingListEntry)]] = [:] + + for localReadingListEntry in localReadingListEntriesToUpdate { + try autoreleasepool { + guard let articleKey = localReadingListEntry.articleKey, let articleURL = URL(string: articleKey), let project = articleURL.wmf_site?.absoluteString, let title = articleURL.wmf_title else { + moc.delete(localReadingListEntry) + return + } + guard let readingListID = localReadingListEntry.list?.readingListID?.int64Value else { + return + } + guard let readingListEntryID = localReadingListEntry.readingListEntryID?.int64Value else { + if localReadingListEntry.isDeletedLocally { + // if it has no id and is deleted locally, it can just be deleted + moc.delete(localReadingListEntry) + } else { + entriesToAddByListID[readingListID, default: []].append((project: project, title: title, entry: localReadingListEntry)) + } + return + } + if localReadingListEntry.isDeletedLocally { + requestCount += 1 + taskGroup.enter() + self.apiController.removeEntry(withEntryID: readingListEntryID, fromListWithListID: readingListID, completion: { (deleteError) in + defer { + taskGroup.leave() + } + guard deleteError == nil else { + DDLogError("Error deleting reading list entry: \(String(describing: deleteError))") + return + } + deletedReadingListEntries[readingListEntryID] = localReadingListEntry + }) + if requestCount % WMFReadingListBatchSizePerRequestLimit == 0 { + taskGroup.wait() + for (_, localReadingListEntry) in deletedReadingListEntries { + moc.delete(localReadingListEntry) + } + deletedReadingListEntries.removeAll(keepingCapacity: true) + try moc.save() + guard !isCancelled else { + throw ReadingListsOperationError.cancelled + } + } + } else { + // there's no "updating" of an entry currently + localReadingListEntry.isUpdatedLocally = false + } + } + } + + taskGroup.wait() + + for (_, localReadingListEntry) in deletedReadingListEntries { + moc.delete(localReadingListEntry) + } + + try moc.save() + + guard !isCancelled else { + throw ReadingListsOperationError.cancelled + } + + for (readingListID, entries) in entriesToAddByListID { + var start = 0 + var end = 0 + while end < entries.count { + end = min(entries.count, start + WMFReadingListBatchSizePerRequestLimit) + try autoreleasepool { + var createdReadingListEntries: [Int64: ReadingListEntry] = [:] + var failedReadingListEntries: [(ReadingListEntry, APIReadingListError)] = [] + requestCount += 1 + taskGroup.enter() + let entrySubarray = Array(entries[start.. URL? { + guard let articleURL = remoteEntry.articleURL else { + return nil + } + let preferredLanguageVariantCode = dataStore.languageLinkController.preferredLanguageVariantCode(forLanguageCode: articleURL.wmf_languageCode) + var variantAwareArticleURL = articleURL + variantAwareArticleURL.wmf_languageVariantCode = preferredLanguageVariantCode + return variantAwareArticleURL + } + + // Code using RemoteReadingListArticleKey as a key coordinate between remote and local readling list entries. + // This key is the articleKey value which *does not* take language variants into account. + // Collections using the WMFInMemoryURLKey *do* take language variants into account. + internal func locallyCreate(_ readingListEntries: [APIReadingListEntry], with readingListsByEntryID: [Int64: ReadingList]? = nil, in moc: NSManagedObjectContext) throws { + guard !readingListEntries.isEmpty else { + return + } + let summaryFetcher = ArticleFetcher(session: apiController.session, configuration: Configuration.current) + let group = WMFTaskGroup() + let semaphore = DispatchSemaphore(value: 1) + var remoteEntriesToCreateLocallyByArticleKey: [WMFInMemoryURLKey: APIReadingListEntry] = [:] + var requestedArticleKeys: Set = [] + var articleSummariesByArticleKey: [WMFInMemoryURLKey: ArticleSummary] = [:] + var entryCount = 0 + var articlesByKey: [WMFInMemoryURLKey: WMFArticle] = [:] + for remoteEntry in readingListEntries { + autoreleasepool { + let isDeleted = remoteEntry.deleted ?? false + guard !isDeleted else { + return + } + + guard let variantAwareArticleURL = variantAwareURLForRemoteEntry(remoteEntry), + let variantAwareArticleKey = variantAwareArticleURL.wmf_inMemoryKey, + let remoteArticleKey = remoteEntry.articleKey else { + return + } + + remoteEntriesToCreateLocallyByArticleKey[variantAwareArticleKey] = remoteEntry + guard !requestedArticleKeys.contains(remoteArticleKey) else { + return + } + requestedArticleKeys.insert(remoteArticleKey) + if let article = dataStore.fetchArticle(with: variantAwareArticleURL, in: moc) { + articlesByKey[variantAwareArticleKey] = article + } else { + group.enter() + summaryFetcher.fetchSummaryForArticle(with: variantAwareArticleKey, completion: { (result, response, error) in + guard let result = result else { + group.leave() + return + } + semaphore.wait() + articleSummariesByArticleKey[variantAwareArticleKey] = result + semaphore.signal() + group.leave() + }) + entryCount += 1 + } + } + } + + group.wait() + guard !isCancelled else { + throw ReadingListsOperationError.cancelled + } + + let updatedArticlesByKey = try moc.wmf_createOrUpdateArticleSummmaries(withSummaryResponses: articleSummariesByArticleKey) + articlesByKey.merge(updatedArticlesByKey, uniquingKeysWith: { (a, b) in return a }) + var finalReadingListsByEntryID: [Int64: ReadingList] + if let readingListsByEntryID = readingListsByEntryID { + finalReadingListsByEntryID = readingListsByEntryID + } else { + finalReadingListsByEntryID = [:] + var readingListsByReadingListID: [Int64: ReadingList] = [:] + let localReadingListsFetch: NSFetchRequest = ReadingList.fetchRequest() + localReadingListsFetch.predicate = NSPredicate(format: "readingListID IN %@", readingListEntries.compactMap { $0.listId }) + let localReadingLists = try moc.fetch(localReadingListsFetch) + for localReadingList in localReadingLists { + guard let localReadingListID = localReadingList.readingListID?.int64Value else { + continue + } + readingListsByReadingListID[localReadingListID] = localReadingList + } + for readingListEntry in readingListEntries { + guard let listId = readingListEntry.listId, let readingList = readingListsByReadingListID[listId] else { + DDLogError("Missing list for reading list entry: \(readingListEntry)") + throw APIReadingListError.needsFullSync + } + finalReadingListsByEntryID[readingListEntry.id] = readingList + } + } + + var updatedLists: Set = [] + for (variantAwareArticleKey, remoteEntry) in remoteEntriesToCreateLocallyByArticleKey { + autoreleasepool { + guard let readingList = finalReadingListsByEntryID[remoteEntry.id] else { + return + } + + var fetchedArticle = articlesByKey[variantAwareArticleKey] + if fetchedArticle == nil { + if let newArticle = dataStore.fetchArticle(withKey: variantAwareArticleKey.databaseKey, variant: variantAwareArticleKey.languageVariantCode, in: moc) { + if newArticle.displayTitleHTML == "" { + newArticle.displayTitleHTML = remoteEntry.title + } + fetchedArticle = newArticle + } + } + + guard let article = fetchedArticle else { + return + } + + var entry = ReadingListEntry(context: moc) + entry.update(with: remoteEntry) + + // if there's a key mismatch, locally delete the bad entry and create a new one with the correct key + // Note the remote entry has no variant info, so its *articleKey* should match the article *key* property. + // Comparing inMemoryKey values leads to a mismatch since the article will potentially have variant info + if remoteEntry.articleKey != article.key { + entry.list = readingList + entry.articleKey = remoteEntry.articleKey + try? readingListsController.markLocalDeletion(for: [entry]) + entry = ReadingListEntry(context: moc) + entry.update(with: remoteEntry) + entry.readingListEntryID = nil + entry.isUpdatedLocally = true + } + + if entry.createdDate == nil { + entry.createdDate = NSDate() + } + if entry.updatedDate == nil { + entry.updatedDate = entry.createdDate + } + entry.list = readingList + entry.articleKey = article.key + entry.variant = article.variant + entry.displayTitle = article.displayTitle + if article.savedDate == nil { + article.savedDate = entry.createdDate as Date? + } + updatedLists.insert(readingList) + } + } + for readingList in updatedLists { + try autoreleasepool { + try readingList.updateArticlesAndEntries() + } + } + } + + internal func createOrUpdate(remoteReadingLists: [APIReadingList], deleteMissingLocalLists: Bool = false, inManagedObjectContext moc: NSManagedObjectContext) throws -> Int { + guard !remoteReadingLists.isEmpty || deleteMissingLocalLists else { + return 0 + } + var createdOrUpdatedReadingListsCount = 0 + // Arrange remote lists by ID and name for merging with local lists + var remoteReadingListsByID: [Int64: APIReadingList] = [:] + var remoteReadingListsByName: [String: [Int64: APIReadingList]] = [:] // server still allows multiple lists with the same name + var remoteDefaultReadingList: APIReadingList? = nil + for remoteReadingList in remoteReadingLists { + if remoteReadingList.isDefault { + remoteDefaultReadingList = remoteReadingList + } + remoteReadingListsByID[remoteReadingList.id] = remoteReadingList + remoteReadingListsByName[remoteReadingList.name.precomposedStringWithCanonicalMapping, default: [:]][remoteReadingList.id] = remoteReadingList + } + + if let remoteDefaultReadingList = remoteDefaultReadingList { + let defaultReadingList = moc.defaultReadingList + defaultReadingList?.readingListID = NSNumber(value: remoteDefaultReadingList.id) + } + + let localReadingListsFetch: NSFetchRequest = ReadingList.fetchRequest() + let localReadingLists = try moc.fetch(localReadingListsFetch) + var localListsMissingRemotely: [ReadingList] = [] + for localReadingList in localReadingLists { + var remoteReadingList: APIReadingList? + if let localReadingListID = localReadingList.readingListID?.int64Value { + // remove from the dictionary because we will create any lists left in the dictionary + if let remoteReadingListByID = remoteReadingListsByID.removeValue(forKey: localReadingListID) { + remoteReadingListsByName[remoteReadingListByID.name]?.removeValue(forKey: remoteReadingListByID.id) + remoteReadingList = remoteReadingListByID + } + } + + if remoteReadingList == nil { + if localReadingList.readingListID == nil, let localReadingListName = localReadingList.name?.precomposedStringWithCanonicalMapping { + if let remoteReadingListByName = remoteReadingListsByName[localReadingListName]?.values.first { + // remove from the dictionary because we will create any lists left in this dictionary + remoteReadingListsByID.removeValue(forKey: remoteReadingListByName.id) + remoteReadingList = remoteReadingListByName + } + } + } + + guard let remoteReadingListForUpdate = remoteReadingList else { + if localReadingList.readingListID != nil { + localListsMissingRemotely.append(localReadingList) + } + continue + } + + let isDeleted = remoteReadingListForUpdate.deleted ?? false + if isDeleted { + try readingListsController.markLocalDeletion(for: [localReadingList]) + moc.delete(localReadingList) // object can be removed since we have the server-side update + createdOrUpdatedReadingListsCount += 1 + } else { + localReadingList.update(with: remoteReadingListForUpdate) + createdOrUpdatedReadingListsCount += 1 + } + } + + if deleteMissingLocalLists { + // Delete missing reading lists + try readingListsController.markLocalDeletion(for: localListsMissingRemotely) + for readingList in localListsMissingRemotely { + moc.delete(readingList) + createdOrUpdatedReadingListsCount += 1 + } + } + + // create any list that wasn't matched by ID or name + for (_, remoteReadingList) in remoteReadingListsByID { + let isDeleted = remoteReadingList.deleted ?? false + guard !isDeleted else { + continue + } + guard let localList = NSEntityDescription.insertNewObject(forEntityName: "ReadingList", into: moc) as? ReadingList else { + continue + } + localList.update(with: remoteReadingList) + if localList.createdDate == nil { + localList.createdDate = NSDate() + } + if localList.updatedDate == nil { + localList.updatedDate = localList.createdDate + } + createdOrUpdatedReadingListsCount += 1 + } + return createdOrUpdatedReadingListsCount + } + + // This method uses RemoteReadingListArticleKey as a key to coordinate between remote and local readling list entries. + // This key is the articleKey value which *does not* take language variants into account. + // Since APIReadingListEntry does not support language variants, this is the desired behavior. + internal func createOrUpdate(remoteReadingListEntries: [APIReadingListEntry], for readingListID: Int64? = nil, deleteMissingLocalEntries: Bool = false, inManagedObjectContext moc: NSManagedObjectContext) throws -> Int { + guard !remoteReadingListEntries.isEmpty || deleteMissingLocalEntries else { + return 0 + } + var createdOrUpdatedReadingListEntriesCount = 0 + // Arrange remote list entries by ID and key for merging with local lists + var remoteReadingListEntriesByReadingListID: [Int64: [RemoteReadingListArticleKey: APIReadingListEntry]] = [:] + + if let readingListID = readingListID { + remoteReadingListEntriesByReadingListID[readingListID] = [:] + } + + for remoteReadingListEntry in remoteReadingListEntries { + guard let listID = remoteReadingListEntry.listId ?? readingListID, let articleKey = remoteReadingListEntry.articleKey else { + DDLogError("missing id or article key for remote entry: \(remoteReadingListEntry)") + assert(false) + continue + } + + remoteReadingListEntriesByReadingListID[listID, default: [:]][articleKey] = remoteReadingListEntry + } + + var entriesToDelete: [ReadingListEntry] = [] + for (readingListID, readingListEntriesByKey) in remoteReadingListEntriesByReadingListID { + try autoreleasepool { + + let localReadingListsFetch: NSFetchRequest = ReadingList.fetchRequest() + localReadingListsFetch.predicate = NSPredicate(format: "readingListID == %@", NSNumber(value: readingListID)) + let localReadingLists = try moc.fetch(localReadingListsFetch) + let localReadingListEntries = localReadingLists.first?.entries?.filter { !$0.isDeletedLocally } ?? [] + + var localEntriesMissingRemotely: [ReadingListEntry] = [] + var remoteEntriesMissingLocally: [RemoteReadingListArticleKey: APIReadingListEntry] = readingListEntriesByKey + for localReadingListEntry in localReadingListEntries { + guard let articleKey = localReadingListEntry.articleKey else { + moc.delete(localReadingListEntry) + createdOrUpdatedReadingListEntriesCount += 1 + continue + } + + guard let remoteReadingListEntryForUpdate: APIReadingListEntry = remoteEntriesMissingLocally.removeValue(forKey: articleKey) else { + if localReadingListEntry.readingListEntryID != nil { + localEntriesMissingRemotely.append(localReadingListEntry) + } + continue + } + + let isDeleted = remoteReadingListEntryForUpdate.deleted ?? false + if isDeleted { + entriesToDelete.append(localReadingListEntry) + createdOrUpdatedReadingListEntriesCount += 1 + } else { + localReadingListEntry.update(with: remoteReadingListEntryForUpdate) + createdOrUpdatedReadingListEntriesCount += 1 + } + } + + if deleteMissingLocalEntries { + entriesToDelete.append(contentsOf: localEntriesMissingRemotely) + } + + try readingListsController.markLocalDeletion(for: entriesToDelete) + for entry in entriesToDelete { + moc.delete(entry) + createdOrUpdatedReadingListEntriesCount += 1 + } + + try moc.save() + moc.reset() + + // create any entry that wasn't matched + var start = 0 + var end = 0 + let entries = Array(remoteEntriesMissingLocally.values) + while end < entries.count { + end = min(entries.count, start + WMFReadingListCoreDataBatchSize) + try locallyCreate(Array(entries[start.. Void) { + guard + let articleURL = articleURL, + let articleTitle = articleURL.percentEncodedPageTitleForPathComponents + else { + completion(Fetcher.invalidParametersError, nil) + return + } + + let pathComponents = ["page", "related", articleTitle] + guard let taskURL = configuration.pageContentServiceAPIURLForURL(articleURL, appending: pathComponents) else { + completion(Fetcher.invalidParametersError, nil) + return + } + session.jsonDecodableTask(with: taskURL) { (relatedPages: RelatedPages?, response, error) in + if let error = error { + completion(error, nil) + return + } + + guard let response = response as? HTTPURLResponse else { + completion(Fetcher.unexpectedResponseError, nil) + return + } + + guard response.statusCode == 200 else { + let error = response.statusCode == 302 ? Fetcher.noNewDataError : Fetcher.unexpectedResponseError + completion(error, nil) + return + } + + + guard let summaries = relatedPages?.pages, summaries.count > 0 else { + completion(Fetcher.unexpectedResponseError, nil) + return + } + + let summaryKeysWithValues: [(WMFInMemoryURLKey, ArticleSummary)] = summaries.compactMap { (summary) -> (WMFInMemoryURLKey, ArticleSummary)? in + summary.languageVariantCode = articleURL.wmf_languageVariantCode + guard let articleKey = summary.key else { + return nil + } + return (articleKey, summary) + } + + completion(nil, Dictionary(uniqueKeysWithValues: summaryKeysWithValues)) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/EchoModelVersion.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/EchoModelVersion.swift new file mode 100644 index 0000000..e04f6f4 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/EchoModelVersion.swift @@ -0,0 +1,6 @@ +import Foundation + +/// Represents the identifier received in a remote push notification sent by the Echo push notifier service. Note this is a class with a static property instead of an enum to simplify the interoperability with Objective-C. +@objc public class EchoModelVersion: NSObject { + @objc public static let current = "checkEchoV1" +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/PushNotificationContentIdentifier.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/PushNotificationContentIdentifier.swift new file mode 100644 index 0000000..6a92c73 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/PushNotificationContentIdentifier.swift @@ -0,0 +1,37 @@ +import Foundation + +/// A lightweight model to uniquely identify incoming push notifications +@objc public class PushNotificationContentIdentifier: NSObject, Codable { + + // MARK: - Properties + + fileprivate static let dictionaryKey = "WMFPushNotificationContentIdentifier" + + public let key: String + public let date: Date? + + // MARK: - Lifecycle + + public init(key: String, date: Date?) { + self.key = key + self.date = date + } + + // MARK: - Public Utilities + + public static func save(_ identifiers: [PushNotificationContentIdentifier], to userInfo: inout [AnyHashable: Any]) { + let encoded = try? JSONEncoder().encode(identifiers) + if let encoded = encoded { + userInfo[dictionaryKey] = encoded + } + } + + @objc public static func load(from userInfo: [AnyHashable: Any]) -> [PushNotificationContentIdentifier] { + guard let data = userInfo[dictionaryKey] as? Data, let decoded = try? JSONDecoder().decode([PushNotificationContentIdentifier].self, from: data) else { + return [] + } + + return decoded + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/PushNotificationsCache.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/PushNotificationsCache.swift new file mode 100644 index 0000000..e914677 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/PushNotificationsCache.swift @@ -0,0 +1,14 @@ +import Foundation + +/// Shared cache object used by the Notifications Service Extension for persisted values set in the main app, and vice versa. +public struct PushNotificationsCache: Codable { + public var settings: PushNotificationsSettings + public var notifications: Set + public var currentUnreadCount: Int = 0 + + public init(settings: PushNotificationsSettings, notifications: Set, currentUnreadCount: Int = 0) { + self.settings = settings + self.notifications = notifications + self.currentUnreadCount = currentUnreadCount + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/PushNotificationsSettings.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/PushNotificationsSettings.swift new file mode 100644 index 0000000..74b2c2c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/PushNotificationsSettings.swift @@ -0,0 +1,21 @@ +import Foundation + +public struct PushNotificationsSettings: Codable { + + // MARK: - Properties + + public static let `default` = PushNotificationsSettings(primaryLanguageCode: "en", primaryLocalizedName: "English", primaryLanguageVariantCode: nil) + + public let primaryLanguageCode: String + public let primaryLocalizedName: String + public let primaryLanguageVariantCode: String? + + // MARK: - Public + + public init(primaryLanguageCode: String, primaryLocalizedName: String, primaryLanguageVariantCode: String?) { + self.primaryLanguageCode = primaryLanguageCode + self.primaryLocalizedName = primaryLocalizedName + self.primaryLanguageVariantCode = primaryLanguageVariantCode + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotification+CoreDataClass.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotification+CoreDataClass.swift new file mode 100644 index 0000000..d89975c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotification+CoreDataClass.swift @@ -0,0 +1,136 @@ +import Foundation +import CoreData + +@objc(RemoteNotification) +public class RemoteNotification: NSManagedObject { + + public var type: RemoteNotificationType { + return RemoteNotification.typeFrom(category: self.categoryString, type: self.typeString, section: self.section) + } + + public static func typeFrom(notification: RemoteNotificationsAPIController.NotificationsResult.Notification) -> RemoteNotificationType { + return typeFrom(category: notification.category, type: notification.type, section: notification.section) + } + + public static func typeFrom(category: String?, type: String?, section: String?) -> RemoteNotificationType { + switch (category, type) { + case ("edit-user-talk", "edit-user-talk"): + return .userTalkPageMessage + case ("mention", "mention"): + return .mentionInTalkPage + case ("mention", "mention-summary"): + return .mentionInEditSummary + case ("mention-success", "mention-success"): + return .successfulMention + case ("mention-failure", "mention-failure"), + ("mention-failure", "mention-failure-too-many"): + return .failedMention + case ("reverted", "reverted"): + return .editReverted + case ("user-rights", "user-rights"): + return .userRightsChange + case ("page-review", "pagetriage-mark-as-reviewed"): + return .pageReviewed + case ("article-linked", "page-linked"): + return .pageLinked + case ("wikibase-action", "page-connection"): + return .connectionWithWikidata + case ("emailuser", "emailuser"): + return .emailFromOtherUser + case ("edit-thank", "edit-thank"): + return .thanks + case ("cx", "cx-first-translation"): + return .translationMilestone(1) + case ("cx", "cx-tenth-translation"): + return .translationMilestone(10) + case ("cx", "cx-hundredth-translation"): + return .translationMilestone(100) + case ("thank-you-edit", "thank-you-edit"): + return .editMilestone + case ("system-noemail", "welcome"): + return .welcome + case ("login-fail", "login-fail-new"): + return .loginFailUnknownDevice + case ("login-fail", "login-fail-known"): + return .loginFailKnownDevice + case ("login-success", "login-success"): + return .loginSuccessUnknownDevice + case ("system", _), + ("system-noemail", _), + ("system-emailonly", _): + + switch section { + case "alert": + return .unknownSystemAlert + case "message": + return .unknownSystemNotice + default: + return .unknown + } + + default: + switch section { + case "alert": + return .unknownAlert + case "message": + return .unknownNotice + default: + return .unknown + } + } + } + + public var linkHost: String? { + if let primaryLinkHost = messageLinks?.primary?.url?.host { + return primaryLinkHost + } + + if let secondaryLinks = messageLinks?.secondary { + for secondaryLink in secondaryLinks { + if let secondaryHost = secondaryLink.url?.host { + return secondaryHost + } + } + } + + return nil + } + + public var primaryLinkFragment: String? { + return primaryLinkURL?.fragment + } + + public var primaryLinkURL: URL? { + return messageLinks?.primary?.url + } + + public var legacyPrimaryLinkURL: URL? { + return messageLinks?.legacyPrimary?.url + } + + public var legacyPrimaryLinkFragment: String? { + return legacyPrimaryLinkURL?.fragment + } + + public var primaryLinkLabel: String? { + return messageLinks?.primary?.label + } + + public var primaryLink: RemoteNotificationLink? { + return messageLinks?.primary + } + + public var secondaryLinks: [RemoteNotificationLink]? { + return messageLinks?.secondary + } + + public struct IdentifierGroup: Hashable { + let key: String? + let id: String? + let wiki: String? + } + + public var identifierGroup: IdentifierGroup { + return IdentifierGroup(key: key, id: id, wiki: wiki) + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotification+CoreDataProperties.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotification+CoreDataProperties.swift new file mode 100644 index 0000000..f973c86 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotification+CoreDataProperties.swift @@ -0,0 +1,35 @@ +import Foundation +import CoreData + + +extension RemoteNotification { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "RemoteNotification") + } + + @NSManaged public var agentId: String? + @NSManaged public var agentName: String? + @NSManaged public var categoryString: String? + @NSManaged public var date: Date? + @NSManaged public var id: String? + @NSManaged public var isRead: Bool + @NSManaged public var key: String? + @NSManaged public var messageBody: String? + @NSManaged public var messageHeader: String? + @NSManaged public var messageLinks: RemoteNotificationLinks? + @NSManaged public var section: String? + @NSManaged public var titleFull: String? + @NSManaged public var titleNamespace: String? + @NSManaged public var titleNamespaceKey: Int16 + @NSManaged public var titleText: String? + @NSManaged public var typeString: String? + @NSManaged public var utcUnixString: String? + @NSManaged public var wiki: String? + @NSManaged public var revisionID: String? + +} + +extension RemoteNotification : Identifiable { + +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationFilterType.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationFilterType.swift new file mode 100644 index 0000000..a9bc45b --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationFilterType.swift @@ -0,0 +1,296 @@ +import Foundation + +public enum RemoteNotificationFilterType: String, CaseIterable { + case userTalkPageMessage + case pageReviewed + case pageLinked + case connectionWithWikidata + case emailFromOtherUser + case mentionInTalkPage + case mentionInEditSummary + case successfulMention + case failedMention + case userRightsChange + case editReverted + case loginAttempts + case loginSuccess + case editMilestone + case translationMilestone + case thanks + case welcome + case other + + init?(from filterIdentifier: String) { + switch filterIdentifier { + case "userTalkPageMessage": + self = .userTalkPageMessage + case "pageReviewed": + self = .pageReviewed + case "pageLinked": + self = .pageLinked + case "connectionWithWikidata": + self = .connectionWithWikidata + case "emailFromOtherUser": + self = .emailFromOtherUser + case "mentionInTalkPage": + self = .mentionInTalkPage + case "mentionInEditSummary": + self = .mentionInEditSummary + case "successfulMention": + self = .successfulMention + case "failedMention": + self = .failedMention + case "userRightsChange": + self = .userRightsChange + case "editReverted": + self = .editReverted + case "loginAttempts": + self = .loginAttempts + case "loginSuccess": + self = .loginSuccess + case "editMilestone": + self = .editMilestone + case "translationMilestone": + self = .translationMilestone + case "thanks": + self = .thanks + case "welcome": + self = .welcome + case "other": + self = .other + default: + return nil + } + } + + public var filterIdentifier: String? { + switch self { + case .userTalkPageMessage: + return "userTalkPageMessage" + case .pageReviewed: + return "pageReviewed" + case .pageLinked: + return "pageLinked" + case .connectionWithWikidata: + return "connectionWithWikidata" + case .emailFromOtherUser: + return "emailFromOtherUser" + case .mentionInTalkPage: + return "mentionInTalkPage" + case .mentionInEditSummary: + return "mentionInEditSummary" + case .successfulMention: + return "successfulMention" + case .failedMention: + return "failedMention" + case .userRightsChange: + return "userRightsChange" + case .editReverted: + return "editReverted" + case .loginAttempts: + return "loginAttempts" + case .loginSuccess: + return "loginSuccess" + case .editMilestone: + return "editMilestone" + case .translationMilestone: + return "translationMilestone" + case .thanks: + return "thanks" + case .welcome: + return "welcome" + case .other: + return "other" + } + } + + public static func categoryStringsForFilterType(type: RemoteNotificationFilterType) -> [String] { + switch type { + case .userTalkPageMessage: + return ["edit-user-talk"] + case .pageReviewed: + return ["page-review"] + case .pageLinked: + return ["article-linked"] + case .connectionWithWikidata: + return ["wikibase-action"] + case .emailFromOtherUser: + return ["emailuser"] + case .mentionInTalkPage, + .mentionInEditSummary: + return ["mention"] + case .successfulMention: + return ["mention-success"] + case .failedMention: + return ["mention-failure"] + case .userRightsChange: + return ["user-rights"] + case .editReverted: + return ["reverted"] + case .loginAttempts: + return ["login-fail"] + case .loginSuccess: + return ["login-success"] + case .editMilestone: + return ["thank-you-edit"] + case .translationMilestone: + return ["cx"] + case .thanks: + return ["edit-thank"] + case .welcome: + return ["system-noemail"] + case .other: + return [] + } + } + + public static func typeStringForFilterType(type: RemoteNotificationFilterType) -> [String] { + switch type { + case .userTalkPageMessage: + return ["edit-user-talk"] + case .pageReviewed: + return ["pagetriage-mark-as-reviewed"] + case .pageLinked: + return ["page-linked"] + case .connectionWithWikidata: + return ["page-connection"] + case .emailFromOtherUser: + return ["emailuser"] + case .mentionInTalkPage: + return ["mention"] + case .mentionInEditSummary: + return ["mention-summary"] + case .successfulMention: + return ["mention-success"] + case .failedMention: + return ["mention-failure", + "mention-failure-too-many"] + case .userRightsChange: + return ["user-rights"] + case .editReverted: + return ["reverted"] + case .loginAttempts: + return ["login-fail-new", + "login-fail-known"] + case .loginSuccess: + return ["login-success"] + case .editMilestone: + return ["thank-you-edit"] + case .translationMilestone: + return ["cx-first-translation", + "cx-tenth-translation", + "cx-hundredth-translation"] + case .thanks: + return ["edit-thank"] + case .welcome: + return ["welcome"] + case .other: + return [] + } + } + + public var imageName: String { + // Return image for the notification type + switch self { + case .userTalkPageMessage: + return "notifications-type-user-talk-message" + case .mentionInTalkPage, .mentionInEditSummary, .successfulMention, .failedMention: + return "notifications-type-mention" + case .editReverted: + return "notifications-type-edit-revert" + case .userRightsChange: + return "notifications-type-user-rights" + case .pageReviewed: + return "notifications-type-page-reviewed" + case .pageLinked, .connectionWithWikidata: + return "notifications-type-link" + case .thanks: + return "notifications-type-thanks" + case .welcome, .translationMilestone, .editMilestone: + return "notifications-type-milestone" + case .loginAttempts, .loginSuccess: + return "notifications-type-login-notify" + case .emailFromOtherUser: + return "notifications-type-email" + case .other: + return "notifications-type-default" + } + } + + public func imageBackgroundColorWithTheme(_ theme: Theme) -> UIColor { + switch self { + case .editMilestone, .translationMilestone, .welcome, .thanks: + return theme.colors.accent + case .loginAttempts, .loginSuccess: + return theme.colors.error + case .failedMention, .editReverted, .userRightsChange: + return theme.colors.warning + default: + return theme.colors.link + } + } + + public static var orderingForFilters: [RemoteNotificationFilterType] { + return [ + .userTalkPageMessage, + .pageReviewed, + .pageLinked, + .connectionWithWikidata, + .emailFromOtherUser, + .mentionInTalkPage, + .mentionInEditSummary, + .successfulMention, + .failedMention, + .userRightsChange, + .editReverted, + .loginAttempts, + .loginSuccess, + .editMilestone, + .translationMilestone, + .thanks, + .welcome, + .other + ] + } + + public var title: String { + switch self { + case .userTalkPageMessage: + return CommonStrings.notificationsCenterUserTalkPageMessage + case .pageReviewed: + return CommonStrings.notificationsCenterPageReviewed + case .pageLinked: + return CommonStrings.notificationsCenterPageLinked + case .connectionWithWikidata: + return CommonStrings.notificationsCenterConnectionWithWikidata + case .emailFromOtherUser: + return CommonStrings.notificationsCenterEmailFromOtherUser + case .mentionInTalkPage: + return CommonStrings.notificationsCenterMentionInTalkPage + case .mentionInEditSummary: + return CommonStrings.notificationsCenterMentionInEditSummary + case .successfulMention: + return CommonStrings.notificationsCenterSuccessfulMention + case .failedMention: + return CommonStrings.notificationsCenterFailedMention + case .userRightsChange: + return CommonStrings.notificationsCenterUserRightsChange + case .editReverted: + return CommonStrings.notificationsCenterEditReverted + case .loginAttempts: + return CommonStrings.notificationsCenterLoginAttempts + case .loginSuccess: + return CommonStrings.notificationsCenterLoginSuccess + case .editMilestone: + return CommonStrings.notificationsCenterEditMilestone + case .translationMilestone: + return CommonStrings.notificationsCenterTranslationMilestone + case .thanks: + return CommonStrings.notificationsCenterThanks + case .welcome: + return CommonStrings.notificationsCenterWelcome + case .other: + return CommonStrings.notificationsCenterOtherFilter + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationLinks.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationLinks.swift new file mode 100644 index 0000000..a63f757 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationLinks.swift @@ -0,0 +1,90 @@ +import Foundation + +@objc(RemoteNotificationLinks) +public class RemoteNotificationLinks: NSObject, NSSecureCoding, Codable { + public static var supportsSecureCoding: Bool = true + + let primary: RemoteNotificationLink? + let secondary: [RemoteNotificationLink]? + let legacyPrimary: RemoteNotificationLink? + + init(primary: RemoteNotificationLink?, secondary: [RemoteNotificationLink]?, legacyPrimary: RemoteNotificationLink?) { + self.primary = primary + self.secondary = secondary + self.legacyPrimary = legacyPrimary + } + + public required convenience init(coder decoder: NSCoder) { + + let primary = decoder.decodeObject(of: RemoteNotificationLink.self, forKey: "primary") + let classes = [NSArray.classForCoder(), RemoteNotificationLink.classForCoder()] + let secondary = decoder.decodeObject(of: classes, forKey: "secondary") as? [RemoteNotificationLink] + let legacyPrimary = decoder.decodeObject(of: RemoteNotificationLink.self, forKey: "legacyPrimary") + self.init(primary: primary, secondary: secondary, legacyPrimary: legacyPrimary) + } + + public func encode(with coder: NSCoder) { + coder.encode(primary, forKey: "primary") + coder.encode(secondary, forKey: "secondary") + coder.encode(legacyPrimary, forKey: "legacyPrimary") + } +} + +@objc(RemoteNotificationLink) +public final class RemoteNotificationLink: NSObject, NSSecureCoding, Codable { + public static var supportsSecureCoding: Bool = true + + let type: String? + public let url: URL? + public let label: String? + + init(type: NSString?, url: URL?, label: NSString?) { + self.type = type as String? + self.url = url + self.label = label as String? + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + type = try? values.decode(String.self, forKey: .type) + + let urlString = try? values.decode(String.self, forKey: .url) + if var urlString = urlString { + + // If there's a fragment, it may not be encoded from the API, thus URLs and URLComponents won't instantiate. + // Manually encoding it here. + // https://phabricator.wikimedia.org/T307604 + if let firstHashIndex = urlString.firstIndex(of: "#") { + let preFragment = urlString.prefix(upTo: firstHashIndex) + let nextIndex = urlString.index(firstHashIndex, offsetBy: 1) + let fragment = urlString.suffix(from: nextIndex) + + let unallowedFragmentCharacters = CharacterSet.urlFragmentAllowed.inverted + if fragment.rangeOfCharacter(from: unallowedFragmentCharacters) != nil { + urlString = preFragment + "#" + (fragment.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? "") + } + } + + url = URL(string: urlString) + } else { + url = nil + } + + label = try? values.decode(String.self, forKey: .label) + } + + public required convenience init(coder decoder: NSCoder) { + let type = decoder.decodeObject(of: NSString.self, forKey: "type") + let urlString = decoder.decodeObject(of: NSString.self, forKey: "url") + let label = decoder.decodeObject(of: NSString.self, forKey: "label") + let url = URL(string: urlString as String? ?? "") + self.init(type: type, url: url, label: label) + } + + public func encode(with coder: NSCoder) { + coder.encode(type, forKey: "type") + let urlString = url?.absoluteString + coder.encode(urlString, forKey: "url") + coder.encode(label, forKey: "label") + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationType.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationType.swift new file mode 100644 index 0000000..5631bb7 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationType.swift @@ -0,0 +1,181 @@ +import Foundation + +public enum RemoteNotificationType: Hashable { + case userTalkPageMessage // Message on your talk page + case mentionInTalkPage // Mention in article talk + case mentionInEditSummary // Mention in edit summary + case successfulMention // Successful mention + case failedMention // Failed mention + case editReverted // Edit reverted + case userRightsChange // Usage rights change + case pageReviewed // Page review + case pageLinked // Page link + case connectionWithWikidata // Wikidata link + case emailFromOtherUser // Email from other user + case thanks // Thanks + case translationMilestone(Int) // Translation milestone + case editMilestone // Editing milestone + case welcome // Welcome + case loginFailUnknownDevice // Failed log in from an unfamiliar device + case loginFailKnownDevice // Log in Notify + case loginSuccessUnknownDevice // Successful log in unfamiliar device + case unknownSystemAlert // No specific type ID, system alert type + case unknownSystemNotice // No specific type ID, system notice type + case unknownNotice // No specific type ID, notice type + case unknownAlert // No specific type ID, alert type + case unknown + +// Possible flow-related notifications to target. Leaving it to default handling for now but we may need to bring these in for special handling. +// case flowUserTalkPageNewTopic //Message on your talk page +// case flowUserTalkPageReply //Reply on your talk page +// case flowDiscussionNewTopic //?? +// case flowDiscussionReply //Reply on an article talk page +// case flowMention //Mention in article talk +// case flowThanks //Thanks +} + +public extension RemoteNotificationType { + var imageName: String { + // Return image for the notification type + switch self { + case .userTalkPageMessage: + return "notifications-type-user-talk-message" + case .mentionInTalkPage, .mentionInEditSummary, .successfulMention, .failedMention: + return "notifications-type-mention" + case .editReverted: + return "notifications-type-edit-revert" + case .userRightsChange: + return "notifications-type-user-rights" + case .pageReviewed: + return "notifications-type-page-reviewed" + case .pageLinked, .connectionWithWikidata: + return "notifications-type-link" + case .thanks: + return "notifications-type-thanks" + case .welcome, .translationMilestone, .editMilestone: + return "notifications-type-milestone" + case .loginFailKnownDevice, .loginFailUnknownDevice, .loginSuccessUnknownDevice, + .unknownSystemAlert, .unknownAlert: + return "notifications-type-login-notify" + case .emailFromOtherUser: + return "notifications-type-email" + default: + return "notifications-type-default" + } + } + + func imageBackgroundColorWithTheme(_ theme: Theme) -> UIColor { + switch self { + case .editMilestone, .translationMilestone, .welcome, .thanks: + return theme.colors.accent + case .loginFailKnownDevice, .loginFailUnknownDevice, .loginSuccessUnknownDevice: + return theme.colors.error + case .failedMention, .editReverted, .userRightsChange: + return theme.colors.warning + default: + return theme.colors.link + } + } + +} + +public extension RemoteNotificationType { + + var title: String { + switch self { + case .userTalkPageMessage: + return CommonStrings.notificationsCenterUserTalkPageMessage + case .pageReviewed: + return CommonStrings.notificationsCenterPageReviewed + case .pageLinked: + return CommonStrings.notificationsCenterPageLinked + case .connectionWithWikidata: + return CommonStrings.notificationsCenterConnectionWithWikidata + case .emailFromOtherUser: + return CommonStrings.notificationsCenterEmailFromOtherUser + case .mentionInTalkPage: + return CommonStrings.notificationsCenterMentionInTalkPage + case .mentionInEditSummary: + return CommonStrings.notificationsCenterMentionInEditSummary + case .successfulMention: + return CommonStrings.notificationsCenterSuccessfulMention + case .failedMention: + return CommonStrings.notificationsCenterFailedMention + case .userRightsChange: + return CommonStrings.notificationsCenterUserRightsChange + case .editReverted: + return CommonStrings.notificationsCenterEditReverted + case .loginFailKnownDevice, + .loginFailUnknownDevice: + return CommonStrings.notificationsCenterLoginAttempts + case .loginSuccessUnknownDevice: + return CommonStrings.notificationsCenterLoginSuccess + case .editMilestone: + return CommonStrings.notificationsCenterEditMilestone + case .translationMilestone: + return CommonStrings.notificationsCenterTranslationMilestone + case .thanks: + return CommonStrings.notificationsCenterThanks + case .welcome: + return CommonStrings.notificationsCenterWelcome + case .unknownSystemAlert, + .unknownAlert, + .unknown: + return CommonStrings.notificationsCenterAlert + case .unknownSystemNotice, + .unknownNotice: + return CommonStrings.notificationsCenterNotice + } + } +} + +/// Interruption levels and and relevance scores +@available(iOS 15.0, *) public extension RemoteNotificationType { + + typealias Priority = (interruptionLevel: UNNotificationInterruptionLevel, relevanceScore: Double) + + /// Multiple notifications coalesced into a single alert + static var bulkPriority: Priority = (.passive, 0.2) + + var priority: Priority { + switch self { + case .userTalkPageMessage: + return (.timeSensitive, 1) + case .mentionInTalkPage: + return (.timeSensitive, 0.9) + case .mentionInEditSummary: + return (.timeSensitive, 0.85) + case .editReverted: + return (.timeSensitive, 0.9) + case .userRightsChange: + return (.active, 0.6) + case .pageReviewed: + return (.active, 0.7) + case .pageLinked: + return (.passive, 0.2) + case .connectionWithWikidata: + return (.passive, 0.3) + case .emailFromOtherUser: + return (.passive, 0.5) + case .thanks: + return (.active, 0.7) + case .translationMilestone: + return (.passive, 0.5) + case .editMilestone: + return (.passive, 0.4) + case .failedMention: + return (.active, 0.5) + case .successfulMention: + return (.passive, 0.65) + case .welcome: + return (.active, 0.8) + case .loginSuccessUnknownDevice: + return (.passive, 0.7) + case .loginFailUnknownDevice, .loginFailKnownDevice: + return (.active, 0.85) + default: + return (.passive, 0.1) + } + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/.xccurrentversion b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/.xccurrentversion new file mode 100644 index 0000000..0f1042b --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ + + + + + _XCCurrentVersionName + RemoteNotifications 3.xcdatamodel + + diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/RemoteNotifications 2.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/RemoteNotifications 2.xcdatamodel/contents new file mode 100644 index 0000000..3332477 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/RemoteNotifications 2.xcdatamodel/contents @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/RemoteNotifications 3.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/RemoteNotifications 3.xcdatamodel/contents new file mode 100644 index 0000000..fd8a368 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/RemoteNotifications 3.xcdatamodel/contents @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/RemoteNotifications.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/RemoteNotifications.xcdatamodel/contents new file mode 100644 index 0000000..e53fc54 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotifications.xcdatamodeld/RemoteNotifications.xcdatamodel/contents @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationsModelController.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationsModelController.swift new file mode 100644 index 0000000..a8fe2e4 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/RemoteNotificationsModelController.swift @@ -0,0 +1,415 @@ +import CocoaLumberjackSwift +import CoreData + +public extension Notification.Name { + static let NotificationsCenterContextDidSave = Notification.Name("NotificationsCenterContextDidSave") + static let NotificationsCenterBadgeNeedsUpdate = Notification.Name("NotificationsCenterBadgeNeedsUpdate") +} + +@objc public extension NSNotification { + static let notificationsCenterContextDidSave = Notification.Name.NotificationsCenterContextDidSave + static let notificationsCenterBadgeNeedsUpdate = Notification.Name.NotificationsCenterBadgeNeedsUpdate +} + +final class RemoteNotificationsModelController { + + enum LibraryKey: String { + case completedImportFlags = "RemoteNotificationsCompletedImportFlags" + case continueIdentifer = "RemoteNotificationsContinueIdentifier" + case filterSettings = "RemoteNotificationsFilterSettings" + + func fullKeyForProject(_ project: WikimediaProject) -> String { + if self == .filterSettings { + assertionFailure("Shouldn't be using this key for filterSettings") + } + return "\(self.rawValue)-\(project.notificationsApiWikiIdentifier)" + } + } + + public static let didLoadPersistentStoresNotification = NSNotification.Name(rawValue: "ModelControllerDidLoadPersistentStores") + + let viewContext: NSManagedObjectContext + let persistentContainer: NSPersistentContainer + private let containerURL: URL + + enum InitializationError: Error { + case unableToCreateModelURL(String, String, Bundle) + case unableToCreateModel(URL, String) + + var localizedDescription: String { + switch self { + case .unableToCreateModelURL(let modelName, let modelExtension, let modelBundle): + return "Couldn't find url for resource named \(modelName) with extension \(modelExtension) in bundle \(modelBundle); make sure you're providing the right name, extension and bundle" + case .unableToCreateModel(let modelURL, let modelName): + return "Couldn't create model with contents of \(modelURL); make sure \(modelURL) is the correct url for \(modelName)" + } + } + } + + enum ReadWriteError: LocalizedError { + case unexpectedResultsForDistinctWikis + case missingNotifications + case missingDateInNotification + + var errorDescription: String? { + return CommonStrings.genericErrorDescription + } + } + + static let modelName = "RemoteNotifications" + + required init(containerURL: URL) throws { + self.containerURL = containerURL + let modelName = RemoteNotificationsModelController.modelName + let modelExtension = "momd" + let modelBundle = Bundle.wmf + + guard let modelURL = modelBundle.url(forResource: modelName, withExtension: modelExtension) else { + let error = InitializationError.unableToCreateModelURL(modelName, modelExtension, modelBundle) + assertionFailure(error.localizedDescription) + throw error + } + guard let model = NSManagedObjectModel(contentsOf: modelURL) else { + let error = InitializationError.unableToCreateModel(modelURL, modelName) + assertionFailure(error.localizedDescription) + throw error + } + let container = NSPersistentContainer(name: modelName, managedObjectModel: model) + let remoteNotificationsStorageURL = containerURL.appendingPathComponent("\(modelName).sqlite") + + let description = NSPersistentStoreDescription(url: remoteNotificationsStorageURL) + container.persistentStoreDescriptions = [description] + container.loadPersistentStores { (storeDescription, error) in + DispatchQueue.main.async { + NotificationCenter.default.post(name: RemoteNotificationsModelController.didLoadPersistentStoresNotification, object: error) + } + } + + viewContext = container.viewContext + viewContext.name = "RemoteNotificationsViewContext" + viewContext.automaticallyMergesChangesFromParent = true + viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy + + self.persistentContainer = container + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + // MARK: Public + + func deleteLegacyDatabaseFiles() throws { + let modelName = Self.modelName + let legacyStorageURL = containerURL.appendingPathComponent(modelName) + + try persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: legacyStorageURL, ofType: NSSQLiteStoreType, options: nil) + + let legecyJournalShmUrl = containerURL.appendingPathComponent("\(modelName)-shm") + let legecyJournalWalUrl = containerURL.appendingPathComponent("\(modelName)-wal") + + try FileManager.default.removeItem(at: legacyStorageURL) + try FileManager.default.removeItem(at: legecyJournalShmUrl) + try FileManager.default.removeItem(at: legecyJournalWalUrl) + } + + func resetDatabaseAndSharedCache() throws { + + let batchDeleteBlock: (NSFetchRequest, NSManagedObjectContext) throws -> Void = { [weak self] (fetchRequest, backgroundContext) in + + guard let self = self else { + return + } + + let batchRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) + batchRequest.resultType = .resultTypeObjectIDs + + let result = try backgroundContext.execute(batchRequest) as? NSBatchDeleteResult + let objectIDArray = result?.result as? [NSManagedObjectID] + let changes: [AnyHashable : Any] = [NSDeletedObjectsKey : objectIDArray as Any] + NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [self.viewContext]) + } + + let backgroundContext = newBackgroundContext() + let request: NSFetchRequest = RemoteNotification.fetchRequest() + let libraryRequest: NSFetchRequest = NSFetchRequest(entityName: "WMFKeyValue") + + // batch delete all notification managed objects from Core Data + try batchDeleteBlock(request, backgroundContext) + + // batch delete all library values from Core Data + try batchDeleteBlock(libraryRequest, backgroundContext) + + // remove notifications from shared cache (referenced by the NotificationsService extension) + let sharedCache = SharedContainerCache.init(fileName: SharedContainerCacheCommonNames.pushNotificationsCache, defaultCache: { PushNotificationsCache(settings: .default, notifications: []) }) + var cache = sharedCache.loadCache() + cache.notifications = [] + cache.currentUnreadCount = 0 + sharedCache.saveCache(cache) + } + + func newBackgroundContext() -> NSManagedObjectContext { + let backgroundContext = persistentContainer.newBackgroundContext() + backgroundContext.name = "RemoteNotificationsBackgroundContext" + backgroundContext.automaticallyMergesChangesFromParent = true + backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy + return backgroundContext + } + + // MARK: Count convenience helpers + + func numberOfUnreadNotifications() throws -> Int { + assert(Thread.isMainThread) + let fetchRequest = RemoteNotification.fetchRequest() + fetchRequest.predicate = unreadNotificationsPredicate + return try viewContext.count(for: fetchRequest) + } + + func numberOfAllNotifications() throws -> Int { + assert(Thread.isMainThread) + let fetchRequest = RemoteNotification.fetchRequest() + return try viewContext.count(for: fetchRequest) + } + + // MARK: Fetch and create + + func fetchNotifications(fetchLimit: Int = 50, fetchOffset: Int = 0, predicate: NSPredicate?) throws -> [RemoteNotification] { + assert(Thread.isMainThread) + + let fetchRequest = RemoteNotification.fetchRequest() + fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)] + fetchRequest.fetchLimit = fetchLimit + fetchRequest.fetchOffset = fetchOffset + fetchRequest.predicate = predicate + + return try viewContext.fetch(fetchRequest) + } + + func createNewNotifications(moc: NSManagedObjectContext, notificationsFetchedFromTheServer: Set, completion: @escaping ((Result) -> Void)) { + moc.perform { [weak self] in + + guard let self = self else { + return + } + + for notification in notificationsFetchedFromTheServer { + try? self.createNewNotification(moc: moc, notification: notification) + } + + do { + try self.save(moc: moc) + NotificationCenter.default.post(name: Notification.Name.NotificationsCenterBadgeNeedsUpdate, object: nil) + completion(.success(())) + } catch let error { + completion(.failure(error)) + } + + } + } + + // MARK: Mark as read + + func markAllAsRead(moc: NSManagedObjectContext, project: WikimediaProject, completion: @escaping (Result) -> Void) { + + moc.perform { [weak self] in + + guard let self = self else { + return + } + + let unreadPredicate = self.unreadNotificationsPredicate + let wikiPredicate = NSPredicate(format: "wiki == %@", project.notificationsApiWikiIdentifier) + let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [unreadPredicate, wikiPredicate]) + + do { + + let notifications = try self.notifications(moc: moc, predicate: compoundPredicate) + + guard !notifications.isEmpty else { + completion(.failure(ReadWriteError.missingNotifications)) + return + } + + notifications.forEach { notification in + notification.isRead = true + } + + try self.save(moc: moc) + + NotificationCenter.default.post(name: Notification.Name.NotificationsCenterBadgeNeedsUpdate, object: nil) + completion(.success(())) + } catch let error { + completion(.failure(error)) + } + } + + } + + func markAsReadOrUnread(moc: NSManagedObjectContext, identifierGroups: Set, shouldMarkRead: Bool, completion: @escaping (Result) -> Void) { + + let keys = identifierGroups.compactMap { $0.key } + moc.perform { [weak self] in + + guard let self = self else { + return + } + + let predicate = NSPredicate(format: "key IN %@", keys) + do { + let notifications = try self.notifications(moc: moc, predicate: predicate) + + notifications.forEach { notification in + notification.isRead = shouldMarkRead + } + + try self.save(moc: moc) + + NotificationCenter.default.post(name: Notification.Name.NotificationsCenterBadgeNeedsUpdate, object: nil) + completion(.success(())) + + } catch let error { + completion(.failure(error)) + } + } + } + + // MARK: Fetch Distinct Wikis + + func distinctWikisWithUnreadNotifications() throws -> Set { + return try distinctWikis(predicate: unreadNotificationsPredicate) + } + + func distinctWikis(predicate: NSPredicate?) throws -> Set { + assert(Thread.isMainThread) + return try distinctWikis(moc: viewContext, predicate: predicate) + } + + func distinctWikis(backgroundContext: NSManagedObjectContext, predicate: NSPredicate?, completion: @escaping (Result, Error>) -> Void) { + backgroundContext.perform { [weak self] in + + guard let self = self else { + return + } + + do { + let results = try self.distinctWikis(moc: backgroundContext, predicate: predicate) + completion(.success(results)) + } catch let error { + completion(.failure(error)) + } + + } + } + + // MARK: Filter Settings + + func getFilterSettingsFromLibrary() -> NSDictionary? { + return libraryValue(forKey: LibraryKey.filterSettings.rawValue) as? NSDictionary + } + + func setFilterSettingsToLibrary(dictionary: NSDictionary?) { + setLibraryValue(dictionary, forKey: LibraryKey.filterSettings.rawValue) + } + + // MARK: WMFLibraryValue Helpers + func libraryValue(forKey key: String) -> NSCoding? { + var result: NSCoding? = nil + let backgroundContext = newBackgroundContext() + backgroundContext.performAndWait { + result = backgroundContext.wmf_keyValue(forKey: key)?.value + } + + return result + } + + func setLibraryValue(_ value: NSCoding?, forKey key: String) { + let backgroundContext = newBackgroundContext() + backgroundContext.perform { + backgroundContext.wmf_setValue(value, forKey: key) + do { + try backgroundContext.save() + } catch let error { + DDLogError("Error saving RemoteNotifications backgroundContext for library keys: \(error)") + } + } + } + + func isProjectAlreadyImported(project: WikimediaProject) -> Bool { + + let key = LibraryKey.completedImportFlags.fullKeyForProject(project) + guard let nsNumber = libraryValue(forKey: key) as? NSNumber else { + return false + } + + return nsNumber.boolValue + } + + // MARK: Private + + private var unreadNotificationsPredicate: NSPredicate { + return NSPredicate(format: "isRead == %@", NSNumber(value: false)) + } + + private func createNewNotification(moc: NSManagedObjectContext, notification: RemoteNotificationsAPIController.NotificationsResult.Notification) throws { + guard let date = notification.date else { + assertionFailure("Notification should have a date") + throw ReadWriteError.missingDateInNotification + } + + let isRead = notification.readString == nil ? NSNumber(booleanLiteral: false) : NSNumber(booleanLiteral: true) + moc.wmf_create(entityNamed: "RemoteNotification", + withKeysAndValues: [ + "wiki": notification.wiki, + "id": notification.id, + "key": notification.key, + "typeString": notification.type, + "categoryString" : notification.category, + "section" : notification.section, + "date": date, + "utcUnixString": notification.timestamp.utcunix, + "titleFull": notification.title?.full, + "titleNamespace": notification.title?.namespace, + "titleNamespaceKey": notification.title?.namespaceKey, + "titleText": notification.title?.text, + "agentId": notification.agent?.id, + "agentName": notification.agent?.name, + "isRead" : isRead, + "revisionID": notification.revisionID, + "messageHeader": notification.message?.header, + "messageBody": notification.message?.body, + "messageLinks": notification.message?.links]) + } + + private func notifications(moc: NSManagedObjectContext, predicate: NSPredicate? = nil) throws -> [RemoteNotification] { + let fetchRequest = RemoteNotification.fetchRequest() + fetchRequest.predicate = predicate + return try moc.fetch(fetchRequest) + } + + private func distinctWikis(moc: NSManagedObjectContext, predicate: NSPredicate?) throws -> Set { + guard let entityName = RemoteNotification.entity().name else { + throw ReadWriteError.unexpectedResultsForDistinctWikis + } + + let fetchRequest = NSFetchRequest(entityName: entityName) + fetchRequest.predicate = predicate + fetchRequest.resultType = .dictionaryResultType + fetchRequest.propertiesToFetch = ["wiki"] + fetchRequest.returnsDistinctResults = true + + let result = try moc.fetch(fetchRequest) + guard let dictionaries = result as? [[String: String]] else { + throw ReadWriteError.unexpectedResultsForDistinctWikis + } + + let results = dictionaries.flatMap { $0.values } + return Set(results) + } + + private func save(moc: NSManagedObjectContext) throws { + if moc.hasChanges { + try moc.save() + NotificationCenter.default.post(name: Notification.Name.NotificationsCenterContextDidSave, object: nil) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/WikimediaProject+RemoteNotifications.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/WikimediaProject+RemoteNotifications.swift new file mode 100644 index 0000000..d59f0cb --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Model/WikimediaProject+RemoteNotifications.swift @@ -0,0 +1,275 @@ +import Foundation + +extension WikimediaProject { + + private static var commonsIdentifier: String { + return "commonswiki" + } + + private static var wikidataIdentifier: String { + return "wikidatawiki" + } + + private static var mediawikiIdentifier: String { + return "mediawikiwiki" + } + + private static var wikispeciesIdentifier: String { + return "specieswiki" + } + + private static var wikipediaLanguageSuffix: String { + return "wiki" + } + + private static var wikibooksLanguageSuffix: String { + return "wikibooks" + } + + private static var wiktionaryLanguageSuffix: String { + return "wiktionary" + } + + private static var wikiquoteLanguageSuffix: String { + return "wikiquote" + } + + private static var wikimediaLanguageSuffix: String { + return "wikimedia" + } + + private static var wikisourceLanguageSuffix: String { + return "wikisource" + } + + private static var wikinewsLanguageSuffix: String { + return "wikinews" + } + + private static var wikiversityLanguageSuffix: String { + return "wikiversity" + } + + private static var wikivoyageLanguageSuffix: String { + return "wikivoyage" + } + + public var notificationsApiWikiIdentifier: String { + switch self { + case .wikipedia(let languageCode, _, _): + return languageCode + Self.wikipediaLanguageSuffix + case .commons: + return Self.commonsIdentifier + case .wikidata: + return Self.wikidataIdentifier + case .wikibooks(let languageCode, _): + return languageCode + Self.wikibooksLanguageSuffix + case .wiktionary(let languageCode, _): + return languageCode + Self.wiktionaryLanguageSuffix + case .wikiquote(let languageCode, _): + return languageCode + Self.wikiquoteLanguageSuffix + case .wikisource(let languageCode, _): + return languageCode + Self.wikisourceLanguageSuffix + case .wikinews(let languageCode, _): + return languageCode + Self.wikinewsLanguageSuffix + case .wikiversity(let languageCode, _): + return languageCode + Self.wikiversityLanguageSuffix + case .wikivoyage(let languageCode, _): + return languageCode + Self.wikivoyageLanguageSuffix + case .mediawiki: + return Self.mediawikiIdentifier + case .wikispecies: + return Self.wikispeciesIdentifier + } + } + + private var projectName: String { + + // TODO: This would be better as a generated mapping file from translatewiki's project translations (for example: https://translatewiki.net/wiki/Special:ExportTranslations?group=ext-wikimediaprojectnames&language=he&format=export-to-file) + // See https://phabricator.wikimedia.org/T297620 + + switch self { + case .wikipedia: + return CommonStrings.plainWikipediaName + case .wikibooks: + return WMFLocalizedString("project-name-wikibooks", value:"Wikibooks", comment: "Project name for Wikibooks.") + case .wiktionary: + return WMFLocalizedString("project-name-wiktionary", value:"Wiktionary", comment: "Project name for Wiktionary.") + case .wikiquote: + return WMFLocalizedString("project-name-wikiquote", value:"Wikiquote", comment: "Project name for Wikiquote.") + case .wikisource: + return WMFLocalizedString("project-name-wikisource", value:"Wikisource", comment: "Project name for Wikisource.") + case .wikinews: + return WMFLocalizedString("project-name-wikinews", value:"Wikinews", comment: "Project name for Wikinews.") + case .wikiversity: + return WMFLocalizedString("project-name-wikiversity", value:"Wikiversity", comment: "Project name for Wikiversity.") + case .wikivoyage: + return WMFLocalizedString("project-name-wikivoyage", value:"Wikivoyage", comment: "Project name for Wikivoyage.") + case .commons: + return WMFLocalizedString("project-name-wikimedia-commons", value:"Wikimedia Commons", comment: "Project name for Wikimedia Commons.") + case .wikidata: + return WMFLocalizedString("project-name-wikidata", value:"Wikidata", comment: "Project name for Wikidata.") + case .mediawiki: + return WMFLocalizedString("project-name-mediawiki", value:"MediaWiki", comment: "Project name for MediaWiki.") + case .wikispecies: + return WMFLocalizedString("project-name-wikispecies", value:"Wikispecies", comment: "Project name for Wikispecies.") + } + } + + /// Returns formatted descriptive project name + /// - Parameters: + /// - shouldReturnCodedFormat: Boolean for if you want description in coded format for langauge projects ("EN-Wikipedia" vs "English Wikipedia"). This is ignored for commons and wikidata projects. + /// - Returns: Formatted descriptive project name + public func projectName(shouldReturnCodedFormat: Bool) -> String { + + let createLanguageProjectNameBlock: (String, String, String, Bool) -> String = { (languageCode, localizedLanguageName, projectName, shouldReturnCodedFormat) in + + let format = WMFLocalizedString("notifications-center-language-project-name-format", value: "%1$@ %2$@", comment: "Format used for the ordering of language project name descriptions. This description is inserted into the header text of notifications in Notifications Center. For example, \"English Wikipedia\". Use this format to reorder these words if necessary or insert additional connecting words. Parameters: %1$@ = localized language name (\"English\"), %2$@ = localized name for Wikipedia (\"Wikipedia\")") + + if !shouldReturnCodedFormat { + return String.localizedStringWithFormat(format, localizedLanguageName, projectName) + } else { + let codedProjectName = "\(languageCode.localizedUppercase)-\(projectName)" + return codedProjectName + } + + } + + switch self { + case .wikipedia(let languageCode, let localizedLanguageName, _): + return createLanguageProjectNameBlock(languageCode, localizedLanguageName, projectName, shouldReturnCodedFormat) + case .commons: + return projectName + case .wikidata: + return projectName + case .wikibooks(let languageCode, let localizedLanguageName): + return createLanguageProjectNameBlock(languageCode, localizedLanguageName, projectName, shouldReturnCodedFormat) + case .wiktionary(let languageCode, let localizedLanguageName): + return createLanguageProjectNameBlock(languageCode, localizedLanguageName, projectName, shouldReturnCodedFormat) + case .wikiquote(let languageCode, let localizedLanguageName): + return createLanguageProjectNameBlock(languageCode, localizedLanguageName, projectName, shouldReturnCodedFormat) + case .wikisource(let languageCode, let localizedLanguageName): + return createLanguageProjectNameBlock(languageCode, localizedLanguageName, projectName, shouldReturnCodedFormat) + case .wikinews(let languageCode, let localizedLanguageName): + return createLanguageProjectNameBlock(languageCode, localizedLanguageName, projectName, shouldReturnCodedFormat) + case .wikiversity(let languageCode, let localizedLanguageName): + return createLanguageProjectNameBlock(languageCode, localizedLanguageName, projectName, shouldReturnCodedFormat) + case .wikivoyage(let languageCode, let localizedLanguageName): + return createLanguageProjectNameBlock(languageCode, localizedLanguageName, projectName, shouldReturnCodedFormat) + case .mediawiki: + return projectName + case .wikispecies: + return projectName + } + } + + public var languageVariantCode: String? { + switch self { + case .wikipedia(_, _, let languageVariantCode): + return languageVariantCode + default: + return nil + } + } + + func mediaWikiAPIURL(configuration: Configuration, queryParameters: RemoteNotificationsAPIController.Query.Parameters?) -> URL? { + + switch self { + case .commons: + return configuration.commonsAPIURLComponents(with: queryParameters).url + case .wikidata: + return configuration.wikidataAPIURLComponents(with: queryParameters).url + case .wikipedia(let languageCode, _, _): + return configuration.mediaWikiAPIURLForLanguageCode(languageCode, queryParameters: queryParameters).url + case .wikibooks(let languageCode, _): + return configuration.mediaWikiAPIURLForLanguageCode(languageCode, siteDomain: Configuration.Domain.wikibooks, queryParameters: queryParameters).url + case .wiktionary(let languageCode, _): + return configuration.mediaWikiAPIURLForLanguageCode(languageCode, siteDomain: Configuration.Domain.wiktionary, queryParameters: queryParameters).url + case .wikiquote(let languageCode, _): + return configuration.mediaWikiAPIURLForLanguageCode(languageCode, siteDomain: Configuration.Domain.wikiquote, queryParameters: queryParameters).url + case .wikisource(let languageCode, _): + return configuration.mediaWikiAPIURLForLanguageCode(languageCode, siteDomain: Configuration.Domain.wikisource, queryParameters: queryParameters).url + case .wikinews(let languageCode, _): + return configuration.mediaWikiAPIURLForLanguageCode(languageCode, siteDomain: Configuration.Domain.wikinews, queryParameters: queryParameters).url + case .wikiversity(let languageCode, _): + return configuration.mediaWikiAPIURLForLanguageCode(languageCode, siteDomain: Configuration.Domain.wikiversity, queryParameters: queryParameters).url + case .wikivoyage(let languageCode, _): + return configuration.mediaWikiAPIURLForLanguageCode(languageCode, siteDomain: Configuration.Domain.wikivoyage, queryParameters: queryParameters).url + case .mediawiki: + return configuration.mediaWikiAPIURLForHost(Configuration.Domain.mediaWiki, with: queryParameters).url + case .wikispecies: + return configuration.mediaWikiAPIURLForHost(Configuration.Domain.wikispecies, with: queryParameters).url + } + } + + private static func projectForLanguageSuffix(suffix: String, language: MWKLanguageLink?) -> WikimediaProject? { + + guard let language = language else { + return nil + } + + switch suffix { + case Self.wikipediaLanguageSuffix: + return .wikipedia(language.languageCode, language.localizedName, language.languageVariantCode) + case Self.wikibooksLanguageSuffix: + return .wikibooks(language.languageCode, language.localizedName) + case Self.wiktionaryLanguageSuffix: + return .wiktionary(language.languageCode, language.localizedName) + case Self.wikiquoteLanguageSuffix: + return .wikiquote(language.languageCode, language.localizedName) + case Self.wikisourceLanguageSuffix: + return .wikisource(language.languageCode, language.localizedName) + case Self.wikinewsLanguageSuffix: + return .wikinews(language.languageCode, language.localizedName) + case Self.wikiversityLanguageSuffix: + return .wikiversity(language.languageCode, language.localizedName) + case Self.wikivoyageLanguageSuffix: + return .wikivoyage(language.languageCode, language.localizedName) + default: + return nil + } + } + + /// Initializes WikimediaProject with wiki identifier recognize by the MediaWiki Notifications API + /// - Parameters: + /// - notificationsApiIdentifier: The API identifier used by the MediaWiki Notifications API. (e.g. "enwiki", "commonswiki", "wikidatawiki", etc.) + /// - languageLinkController: Included to validate project against a list of languages that the app recognizes. This also associates extra metadata to language enum associated value, like localizedName and languageVariantCode. + public init?(notificationsApiIdentifier: String, languageLinkController: MWKLanguageLinkController) { + + switch notificationsApiIdentifier { + case Self.commonsIdentifier: + self = .commons + case Self.wikidataIdentifier: + self = .wikidata + case Self.wikispeciesIdentifier: + self = .wikispecies + case Self.mediawikiIdentifier: + self = .mediawiki + default: + + let suffixes = [Self.wikipediaLanguageSuffix, Self.wikibooksLanguageSuffix, Self.wiktionaryLanguageSuffix, Self.wikiquoteLanguageSuffix, Self.wikimediaLanguageSuffix, Self.wikisourceLanguageSuffix, Self.wikinewsLanguageSuffix, Self.wikiversityLanguageSuffix, Self.wikivoyageLanguageSuffix] + + var project: WikimediaProject? + for suffix in suffixes { + let strippedIdentifier = notificationsApiIdentifier.hasSuffix(suffix) ? String(notificationsApiIdentifier.dropLast(suffix.count)) : notificationsApiIdentifier + + // confirm it is a recognized language + let recognizedLanguage = languageLinkController.allLanguages.first { languageLink in + languageLink.languageCode == strippedIdentifier + } + + project = Self.projectForLanguageSuffix(suffix: suffix, language: recognizedLanguage) + + if project != nil { + break + } + } + + guard let project = project else { + return nil + } + + self = project + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/NotificationServiceHelper.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/NotificationServiceHelper.swift new file mode 100644 index 0000000..597857c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/NotificationServiceHelper.swift @@ -0,0 +1,61 @@ +import Foundation + +// Testable helper methods for service extension logic +public class NotificationServiceHelper { + public static func allNotificationsAreForSameTalkPage(notifications: Set) -> Bool { + + guard let firstTitle = notifications.first?.titleFull, + let firstNamespace = notifications.first?.namespace, + (firstNamespace == .talk || firstNamespace == .userTalk) else { + return false + } + + for notification in notifications { + if (notification.titleFull != firstTitle) || + (notification.namespace != firstNamespace) { + return false + } + } + + return true + } + + public static func determineNotificationsToDisplayAndCache(fetchedNotifications: Set, cachedNotifications: Set) -> (notificationsToDisplay: Set, notificationsToCache: Set) { + + // Prune persisted keys of any > 1 day ago so the cache doesn't grow too big + let recentCachedNotifications = cachedNotifications.filter { $0.isNewerThan(timeAgo: TimeInterval.oneDay) } + + // Prune fetched notifications > 10mins ago since a server delay should be no longer than that. + let recentFetchedNotifications = fetchedNotifications.filter { $0.isNewerThan(timeAgo: TimeInterval.tenMinutes) } + + // Only consider new notifications that don't exist in cache for display + let notificationsToDisplay = recentFetchedNotifications.subtracting(recentCachedNotifications) + + // New cache should keep track of recently cached notifications + new notifications to display + let notificationsToCache = notificationsToDisplay.union(recentCachedNotifications) + + return (notificationsToDisplay, notificationsToCache) + } + + public static func talkPageContent(for notifications: Set) -> (subtitle: String, body: String)? { + + guard NotificationServiceHelper.allNotificationsAreForSameTalkPage(notifications: notifications), + let talkPageTitle = notifications.first?.titleFull else { + return nil + } + + let subtitle: String + let body: String + + subtitle = String.localizedStringWithFormat(WMFLocalizedString("notifications-push-talk-title-format", value: "{{PLURAL:%1$d|New message|New messages}}", comment: "Title text for a push notification that represents talk page messages. %1$d is used to determine the text to display."), notifications.count) + + if notifications.count == 1, + let pushContentText = notifications.first?.pushContentText { + body = pushContentText + } else { + body = String.localizedStringWithFormat(WMFLocalizedString("notifications-push-talk-body-format", value: "{{PLURAL:%1$d|%1$d new message|%1$d new messages}} on %2$@", comment: "Body text for a push notification that represents talk page messages. %1$d is replaced with the number of talk page messages, %2$@ is replaced with the talk page title. For example, \"3 new messages on User talk: Username\""), notifications.count, talkPageTitle) + } + + return (subtitle, body) + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsImportOperation.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsImportOperation.swift new file mode 100644 index 0000000..339ceee --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsImportOperation.swift @@ -0,0 +1,59 @@ +import CocoaLumberjackSwift + +class RemoteNotificationsImportOperation: RemoteNotificationsPagingOperation { + + // MARK: Overrides + + override var shouldExecute: Bool { + // isAlreadyImported computed property fetches a persisted flag + return !isAlreadyImported + } + + override var initialContinueId: String? { + // continueId computed property fetches a persisted continue id, so we can pick up importing where we left off + return continueId + } + + override func didFetchAndSaveAllPages() { + saveLanguageAsImportCompleted() + } + + override func willFetchAndSaveNewPage(newContinueId: String) { + saveContinueId(newContinueId) + } + + // MARK: Private + + private func saveLanguageAsImportCompleted() { + let key = RemoteNotificationsModelController.LibraryKey.completedImportFlags.fullKeyForProject(project) + setAlreadyImported(true, forKey: key) + } + + private func saveContinueId(_ continueId: String) { + let key = RemoteNotificationsModelController.LibraryKey.continueIdentifer.fullKeyForProject(project) + setContinueId(continueId, forKey: key) + } +} + +// MARK: Library Key Value helpers + +private extension RemoteNotificationsImportOperation { + var continueId: String? { + + let key = RemoteNotificationsModelController.LibraryKey.continueIdentifer.fullKeyForProject(project) + return modelController.libraryValue(forKey: key) as? String + } + + var isAlreadyImported: Bool { + return modelController.isProjectAlreadyImported(project: project) + } + + func setContinueId(_ continueId: String, forKey key: String) { + modelController.setLibraryValue(continueId as NSString, forKey: key) + } + + func setAlreadyImported(_ value: Bool, forKey key: String) { + let nsNumber = NSNumber(value: value) + modelController.setLibraryValue(nsNumber, forKey: key) + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsMarkAllAsReadOperation.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsMarkAllAsReadOperation.swift new file mode 100644 index 0000000..6fcd7ad --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsMarkAllAsReadOperation.swift @@ -0,0 +1,38 @@ +import Foundation + +class RemoteNotificationsMarkAllAsReadOperation: RemoteNotificationsProjectOperation { + + override func execute() { + + // optimistically mark in database first for UI to reflect, then in API. + + let backgroundContext = modelController.newBackgroundContext() + self.modelController.markAllAsRead(moc: backgroundContext, project: project) { [weak self] result in + guard let self = self else { + return + } + + switch result { + case .success: + + self.apiController.markAllAsRead(project: self.project) { [weak self] error in + guard let self = self else { + return + } + + if let error = error { + self.finish(with: error) + return + } + + self.finish() + } + + case .failure(let error): + self.finish(with: error) + } + + + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsMarkReadOrUnreadOperation.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsMarkReadOrUnreadOperation.swift new file mode 100644 index 0000000..52fe15d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsMarkReadOrUnreadOperation.swift @@ -0,0 +1,49 @@ +class RemoteNotificationsMarkReadOrUnreadOperation: RemoteNotificationsProjectOperation { + + private let shouldMarkRead: Bool + private let identifierGroups: Set + + required init(project: WikimediaProject, apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController, identifierGroups: Set, shouldMarkRead: Bool) { + self.shouldMarkRead = shouldMarkRead + self.identifierGroups = identifierGroups + super.init(project: project, apiController: apiController, modelController: modelController) + } + + required init(project: WikimediaProject, apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController) { + fatalError("init(project:apiController:modelController:) has not been implemented") + } + + required init(apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController) { + fatalError("init(apiController:modelController:) has not been implemented") + } + + override func execute() { + + // optimistically mark in database first for UI to reflect, then in API. + + let backgroundContext = modelController.newBackgroundContext() + modelController.markAsReadOrUnread(moc: backgroundContext, identifierGroups: identifierGroups, shouldMarkRead: shouldMarkRead) { [weak self] result in + + guard let self = self else { + return + } + + switch result { + case .success: + + self.apiController.markAsReadOrUnread(project: self.project, identifierGroups: self.identifierGroups, shouldMarkRead: self.shouldMarkRead) { error in + if let error = error { + self.finish(with: error) + return + } + + self.finish() + } + + case .failure(let error): + self.finish(with: error) + } + + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsOperation.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsOperation.swift new file mode 100644 index 0000000..784c837 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsOperation.swift @@ -0,0 +1,10 @@ +class RemoteNotificationsOperation: AsyncOperation { + let apiController: RemoteNotificationsAPIController + let modelController: RemoteNotificationsModelController + + required init(apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController) { + self.apiController = apiController + self.modelController = modelController + super.init() + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsPagingOperation.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsPagingOperation.swift new file mode 100644 index 0000000..089c6fc --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsPagingOperation.swift @@ -0,0 +1,136 @@ +import Foundation + +/// Base class for operations that deal with fetching and persisting user notifications. Operation will recursively call the next page, with overrideable hooks to adjust this behavior. +class RemoteNotificationsPagingOperation: RemoteNotificationsProjectOperation { + + private let needsCrossWikiSummary: Bool + private(set) var crossWikiSummaryNotification: RemoteNotificationsAPIController.NotificationsResult.Notification? + + required init(project: WikimediaProject, apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController, needsCrossWikiSummary: Bool) { + self.needsCrossWikiSummary = needsCrossWikiSummary + super.init(project: project, apiController: apiController, modelController: modelController) + } + + required init(project: WikimediaProject, apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController) { + fatalError("init(project:apiController:modelController:) has not been implemented") + } + + required init(apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController) { + fatalError("init(apiController:modelController:) has not been implemented") + } + + // MARK: Overridable hooks + + /// Boolean logic for allowing operation execution. Override to add any validation before executing this operation. + var shouldExecute: Bool { + return true + } + + /// Hook to exit early from recursively paging and persisting the API response. This is called right before the next page is fetched from the API. This value will take priority even if last response indicates that there are additional pages to fetch. + /// - Parameter lastNotification: The last notification returned from the previous response + /// - Returns: Boolean flag indicating recursive paging should continue or not in this operation. + func shouldContinueToPage(lastNotification: RemoteNotificationsAPIController.NotificationsResult.Notification) -> Bool { + return true + } + + /// Hook that is called when the last page of notifications has been fetched and saved locally. Do any additional cleanup here. + func didFetchAndSaveAllPages() { + + } + + /// Hook that is called when we are about to fetch and persist a new page from the API. + /// - Parameter newContinueId: Continue Id the operation will send in the next API call. + func willFetchAndSaveNewPage(newContinueId: String) { + + } + + /// Override to provide an initial continue Id to send into the first API call + var initialContinueId: String? { + return nil + } + + /// Override to allow operation to page through a filtered list (read, unread, etc.) + var filter: RemoteNotificationsAPIController.Query.Filter { + return .none + } + + // MARK: General Fetch and Save functionality + + override func execute() { + + guard shouldExecute else { + finish() + return + } + + recursivelyFetchAndSaveNotifications(continueId: initialContinueId) + } + + private func recursivelyFetchAndSaveNotifications(continueId: String? = nil) { + apiController.getAllNotifications(from: project, needsCrossWikiSummary: needsCrossWikiSummary, filter: filter, continueId: continueId) { [weak self] apiResult, error in + guard let self = self else { + return + } + + if let error = error { + self.finish(with: error) + return + } + + guard let fetchedNotifications = apiResult?.list else { + self.finish(with: RequestError.unexpectedResponse) + return + } + + var fetchedNotificationsToPersist = fetchedNotifications + var lastNotification = fetchedNotifications.last + if self.needsCrossWikiSummary { + + let notificationIsSummaryType: (RemoteNotificationsAPIController.NotificationsResult.Notification) -> Bool = { notification in + notification.id == "-1" && notification.type == "foreign" + } + + let crossWikiSummaryNotification = fetchedNotificationsToPersist.first(where: notificationIsSummaryType) + self.crossWikiSummaryNotification = crossWikiSummaryNotification + + fetchedNotificationsToPersist = fetchedNotifications.filter({ notification in + !notificationIsSummaryType(notification) + }) + lastNotification = fetchedNotificationsToPersist.last + } + + guard let lastNotification = lastNotification else { + // Empty notifications list so nothing to import. Exit early. + self.didFetchAndSaveAllPages() + self.finish() + return + } + + let backgroundContext = self.modelController.newBackgroundContext() + self.modelController.createNewNotifications(moc: backgroundContext, notificationsFetchedFromTheServer: Set(fetchedNotificationsToPersist), completion: { [weak self] result in + + guard let self = self else { + return + } + + switch result { + case .success: + + guard let newContinueId = apiResult?.continueId, + newContinueId != continueId, + self.shouldContinueToPage(lastNotification: lastNotification) else { + self.didFetchAndSaveAllPages() + self.finish() + return + } + + self.willFetchAndSaveNewPage(newContinueId: newContinueId) + self.recursivelyFetchAndSaveNotifications(continueId: newContinueId) + + case .failure(let error): + self.finish(with: error) + } + }) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsProjectOperation.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsProjectOperation.swift new file mode 100644 index 0000000..93b046d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsProjectOperation.swift @@ -0,0 +1,14 @@ +import Foundation + +class RemoteNotificationsProjectOperation: RemoteNotificationsOperation { + let project: WikimediaProject + + required init(project: WikimediaProject, apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController) { + self.project = project + super.init(apiController: apiController, modelController: modelController) + } + + required init(apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController) { + fatalError("init(apiController:modelController:) has not been implemented") + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsReauthenticateOperation.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsReauthenticateOperation.swift new file mode 100644 index 0000000..a2954f4 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsReauthenticateOperation.swift @@ -0,0 +1,38 @@ +import Foundation + +class RemoteNotificationsReauthenticateOperation: AsyncOperation { + + var appLanguageOperationError: Error? + private(set) var didReauthenticate: Bool = false + private let authManager: WMFAuthenticationManager + + init(authManager: WMFAuthenticationManager) { + self.authManager = authManager + } + + override func execute() { + + guard let error = appLanguageOperationError as? RemoteNotificationsAPIController.ResultError, + error.code == "login-required" else { + finish() + return + } + + self.authManager.loginWithSavedCredentials { [weak self] result in + + guard let self = self else { + return + } + + switch result { + case .success: + self.didReauthenticate = true + self.finish() + case .alreadyLoggedIn: + self.finish() + case .failure(let error): + self.finish(with: error) + } + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsRefreshCrossWikiOperation.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsRefreshCrossWikiOperation.swift new file mode 100644 index 0000000..bbcbea4 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsRefreshCrossWikiOperation.swift @@ -0,0 +1,88 @@ +import Foundation + +class RemoteNotificationsRefreshCrossWikiGroupOperation: RemoteNotificationsOperation { + + enum CrossWikiGroupError: LocalizedError { + case individualErrors([Error]) + + var errorDescription: String? { + + switch self { + case .individualErrors(let errors): + if let firstError = errors.first { + return (firstError as NSError).alertMessage() + } + } + + return CommonStrings.genericErrorDescription + + } + } + + var crossWikiSummaryNotification: RemoteNotificationsAPIController.NotificationsResult.Notification? + + private let internalQueue = OperationQueue() + private let finishingOperation = BlockOperation(block: {}) + + private let appLanguageProject: WikimediaProject + private let secondaryProjects: [WikimediaProject] + private let languageLinkController: MWKLanguageLinkController + + init(appLanguageProject: WikimediaProject, secondaryProjects: [WikimediaProject], languageLinkController: MWKLanguageLinkController, apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController) { + self.appLanguageProject = appLanguageProject + self.secondaryProjects = secondaryProjects + self.languageLinkController = languageLinkController + super.init(apiController: apiController, modelController: modelController) + } + + required init(apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController) { + fatalError("init(apiController:modelController:) has not been implemented") + } + + override func execute() { + + let crossWikiOperations = crossWikiOperations() + for crossWikiOperation in crossWikiOperations { + finishingOperation.addDependency(crossWikiOperation) + } + + finishingOperation.completionBlock = { + let errors = crossWikiOperations.compactMap { $0.error } + if errors.count > 0 { + self.finish(with: CrossWikiGroupError.individualErrors(errors)) + } else { + self.finish() + } + } + + internalQueue.addOperations(crossWikiOperations + [finishingOperation], waitUntilFinished: false) + } + + override func cancel() { + internalQueue.cancelAllOperations() + super.cancel() + } + + private func crossWikiOperations() -> [RemoteNotificationsRefreshCrossWikiOperation] { + + guard let crossWikiSummary = crossWikiSummaryNotification, + let crossWikiSources = crossWikiSummary.sources else { + return [] + } + + let crossWikiProjects = crossWikiSources.keys.compactMap { WikimediaProject(notificationsApiIdentifier: $0, languageLinkController: languageLinkController) } + + // extract new projects from summary object that aren't already queued up to be fetched as an app language or secondary operation + let filteredCrossWikiProjects = crossWikiProjects.filter { !([appLanguageProject] + secondaryProjects).contains($0) } + + return filteredCrossWikiProjects.map { RemoteNotificationsRefreshCrossWikiOperation(project: $0, apiController: self.apiController, modelController: self.modelController, needsCrossWikiSummary: false)} + } +} + +class RemoteNotificationsRefreshCrossWikiOperation: RemoteNotificationsPagingOperation { + + override var filter: RemoteNotificationsAPIController.Query.Filter { + return .unread + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsRefreshOperation.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsRefreshOperation.swift new file mode 100644 index 0000000..bf8e894 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/Operations/RemoteNotificationsRefreshOperation.swift @@ -0,0 +1,25 @@ +import Foundation + +class RemoteNotificationsRefreshOperation: RemoteNotificationsPagingOperation { + + override func shouldContinueToPage(lastNotification: RemoteNotificationsAPIController.NotificationsResult.Notification) -> Bool { + + let backgroundContext = self.modelController.newBackgroundContext() + var shouldContinueToPage = true + backgroundContext.performAndWait { + + // Is last (i.e. most recent) notification already in the database? If so, don't continue to page. + let fetchRequest = RemoteNotification.fetchRequest() + fetchRequest.fetchLimit = 1 + let predicate = NSPredicate(format: "key == %@", lastNotification.key) + fetchRequest.predicate = predicate + + let result = try? backgroundContext.fetch(fetchRequest) + if result?.first != nil { + shouldContinueToPage = false + } + } + + return shouldContinueToPage + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsAPIController.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsAPIController.swift new file mode 100644 index 0000000..62a5401 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsAPIController.swift @@ -0,0 +1,584 @@ +import CocoaLumberjackSwift + +public class RemoteNotificationsAPIController: Fetcher { + + // MARK: Decodable: NotificationsResult + + struct ResultError: Decodable { + let code, info: String? + } + + public struct NotificationsResult: Decodable { + + struct Query: Decodable { + + struct Notifications: Decodable { + let list: [Notification] + let continueId: String? + + enum CodingKeys: String, CodingKey { + case list + case continueId = "continue" + } + } + + let notifications: Notifications? + } + + let error: ResultError? + let query: Query? + + public struct Notification: Codable, Hashable { + + struct Timestamp: Codable, Hashable { + let utciso8601: String + let utcunix: String + + enum CodingKeys: String, CodingKey { + case utciso8601 + case utcunix + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + utciso8601 = try values.decode(String.self, forKey: .utciso8601) + do { + utcunix = String(try values.decode(Int.self, forKey: .utcunix)) + } catch { + utcunix = try values.decode(String.self, forKey: .utcunix) + } + } + } + struct Agent: Codable, Hashable { + let id: String? + let name: String? + + enum CodingKeys: String, CodingKey { + case id + case name + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + name = try values.decode(String.self, forKey: .name) + do { + id = String(try values.decode(Int.self, forKey: .id)) + } catch { + id = try values.decode(String.self, forKey: .id) + } + } + } + struct Title: Codable, Hashable { + let full: String? + let namespace: String? + let namespaceKey: Int? + let text: String? + + enum CodingKeys: String, CodingKey { + case full + case namespace + case namespaceKey = "namespace-key" + case text + } + } + + struct Message: Codable, Hashable { + let header: String? + let body: String? + let links: RemoteNotificationLinks? + } + + let wiki: String + let id: String + let type: String + let category: String + let section: String + let timestamp: Timestamp + let title: Title? + let agent: Agent? + let readString: String? + let revisionID: String? + let message: Message? + let sources: [String: [String: String]]? + + enum CodingKeys: String, CodingKey { + case wiki + case id + case type + case category + case section + case timestamp + case title = "title" + case agent + case readString = "read" + case revisionID = "revid" + case message = "*" + case sources + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(key) + } + + public static func ==(lhs: Notification, rhs: Notification) -> Bool { + return lhs.key == rhs.key && + lhs.readString == rhs.readString + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + wiki = try values.decode(String.self, forKey: .wiki) + do { + id = String(try values.decode(Int.self, forKey: .id)) + } catch { + id = try values.decode(String.self, forKey: .id) + } + + type = try values.decode(String.self, forKey: .type) + category = try values.decode(String.self, forKey: .category) + section = try values.decode(String.self, forKey: .section) + timestamp = try values.decode(Timestamp.self, forKey: .timestamp) + title = try? values.decode(Title.self, forKey: .title) + agent = try? values.decode(Agent.self, forKey: .agent) + readString = try? values.decode(String.self, forKey: .readString) + + if let intRevID = try? values.decode(Int.self, forKey: .revisionID) { + revisionID = String(intRevID) + } else { + revisionID = (try? values.decode(String.self, forKey: .revisionID)) ?? nil + } + + message = try? values.decode(Message.self, forKey: .message) + sources = try? values.decode([String: [String: String]].self, forKey: .sources) + } + } + + + } + + // MARK: Decodable: MarkReadResult + + private struct MarkReadResult: Decodable { + let query: Query? + let error: ResultError? + + var succeeded: Bool { + return query?.markAsRead?.result == .success + } + + struct Query: Decodable { + let markAsRead: MarkedAsRead? + + enum CodingKeys: String, CodingKey { + case markAsRead = "echomarkread" + } + } + struct MarkedAsRead: Decodable { + let result: Result? + } + enum Result: String, Decodable { + case success + } + } + + enum MarkReadError: LocalizedError { + case noResult + case unknown + case multiple([Error]) + + var errorDescription: String? { + + switch self { + case .multiple(let errors): + if let firstError = errors.first { + return (firstError as NSError).alertMessage() + } + case .noResult, .unknown: + return RequestError.unexpectedResponse.errorDescription ?? CommonStrings.genericErrorDescription + } + + return CommonStrings.genericErrorDescription + } + } + + // MARK: Decodable: MarkSeenResult + + struct MarkSeenResult: Decodable { + let query: Query? + let error: ResultError? + + var succeeded: Bool { + return query?.markAsSeen?.result == .success + } + + struct Query: Decodable { + let markAsSeen: MarkAsSeen? + + enum CodingKeys: String, CodingKey { + case markAsSeen = "echomarkseen" + } + } + + struct MarkAsSeen: Decodable { + let result: Result? + } + + enum Result: String, Decodable { + case success + } + } + + enum MarkSeenError: LocalizedError { + case noResult + case unknown + + var errorDescription: String? { + return RequestError.unexpectedResponse.errorDescription ?? CommonStrings.genericErrorDescription + } + } + + // MARK: Public + + public func getUnreadPushNotifications(from project: WikimediaProject, completion: @escaping (Set, Error?) -> Void) { + let completion: (NotificationsResult?, URLResponse?, Error?) -> Void = { result, _, error in + guard error == nil else { + completion([], error) + return + } + let result = result?.query?.notifications?.list ?? [] + completion(Set(result), nil) + } + request(project: project, queryParameters: Query.notifications(limit: .max, filter: .unread, notifierType: .push, continueId: nil), completion: completion) + } + + func getAllNotifications(from project: WikimediaProject, needsCrossWikiSummary: Bool = false, filter: Query.Filter = .none, continueId: String?, completion: @escaping (NotificationsResult.Query.Notifications?, Error?) -> Void) { + let completion: (NotificationsResult?, URLResponse?, Error?) -> Void = { result, _, error in + guard error == nil else { + completion(nil, error) + return + } + completion(result?.query?.notifications, result?.error) + } + + request(project: project, queryParameters: Query.notifications(from: [project], limit: .max, filter: filter, needsCrossWikiSummary: needsCrossWikiSummary, continueId: continueId), completion: completion) + } + + func markAllAsSeen(project: WikimediaProject, completion: @escaping ((Result) -> Void)) { + request(project: project, queryParameters: Query.markAllAsSeen(project: project), method: .post) { (result: MarkSeenResult?, _, error) in + if let error = error { + completion(.failure(error)) + return + } + + guard let result = result else { + assertionFailure("Expected result") + completion(.failure(MarkSeenError.noResult)) + return + } + + if let error = result.error { + completion(.failure(error)) + return + } + + if !result.succeeded { + completion(.failure(MarkSeenError.unknown)) + return + } + completion(.success(())) + } + } + + func markAllAsRead(project: WikimediaProject, completion: @escaping (Error?) -> Void) { + + request(project: project, queryParameters: Query.markAllAsRead(project: project), method: .post) { (result: MarkReadResult?, _, error) in + if let error = error { + completion(error) + return + } + guard let result = result else { + assertionFailure("Expected result; make sure MarkReadResult maps the expected result correctly") + completion(MarkReadError.noResult) + return + } + if let error = result.error { + completion(error) + return + } + if !result.succeeded { + completion(MarkReadError.unknown) + return + } + completion(nil) + } + } + + func markAsReadOrUnread(project: WikimediaProject, identifierGroups: Set, shouldMarkRead: Bool, completion: @escaping (Error?) -> Void) { + let maxNumberOfNotificationsPerRequest = 50 + let identifierGroups = Array(identifierGroups) + let split = identifierGroups.chunked(into: maxNumberOfNotificationsPerRequest) + + split.asyncCompactMap({ (identifierGroups, completion: @escaping (Error?) -> Void) in + request(project: project, queryParameters: Query.markAsReadOrUnread(identifierGroups: identifierGroups, shouldMarkRead: shouldMarkRead), method: .post) { (result: MarkReadResult?, _, error) in + if let error = error { + completion(error) + return + } + guard let result = result else { + assertionFailure("Expected result; make sure MarkReadResult maps the expected result correctly") + completion(MarkReadError.noResult) + return + } + if let error = result.error { + completion(error) + return + } + if !result.succeeded { + completion(MarkReadError.unknown) + return + } + completion(nil) + } + }) { (errors) in + if errors.isEmpty { + completion(nil) + } else { + DDLogError("\(errors.count) of \(split.count) mark as read requests failed") + completion(MarkReadError.multiple(errors)) + } + } + } + + // MARK: Private + + private func request(project: WikimediaProject?, queryParameters: Query.Parameters?, method: Session.Request.Method = .get, completion: @escaping (T?, URLResponse?, Error?) -> Void) { + + guard let url = project?.mediaWikiAPIURL(configuration: configuration, queryParameters: queryParameters) else { + completion(nil, nil, RequestError.invalidParameters) + return + } + + if method == .get { + session.jsonDecodableTask(with: url, method: .get, completionHandler: completion) + } else { + requestMediaWikiAPIAuthToken(for:url, type: .csrf) { (result) in + switch result { + case .failure(let error): + completion(nil, nil, error) + case .success(let token): + self.session.jsonDecodableTask(with: url, method: method, bodyParameters: ["token": token.value], bodyEncoding: .form, completionHandler: completion) + } + } + } + } + + // MARK: Query parameters + + struct Query { + typealias Parameters = [String: Any] + + enum Limit { + case max + case numeric(Int) + + var value: String { + switch self { + case .max: + return "max" + case .numeric(let number): + return "\(number)" + } + } + } + + enum Filter: String { + case read = "read" + case unread = "!read" + case none = "read|!read" + } + + enum NotifierType: String { + case web + case push + case email + } + + static func notifications(from projects: [WikimediaProject] = [], limit: Limit = .max, filter: Filter = .none, notifierType: NotifierType? = nil, needsCrossWikiSummary: Bool = false, continueId: String?) -> Parameters { + var dictionary: [String: Any] = ["action": "query", + "format": "json", + "formatversion": "2", + "notformat": "model", + "meta": "notifications", + "notlimit": limit.value, + "notfilter": filter.rawValue] + + if let continueId = continueId { + dictionary["notcontinue"] = continueId + } + + if let notifierType = notifierType { + dictionary["notnotifiertypes"] = notifierType.rawValue + } + + if needsCrossWikiSummary { + dictionary["notcrosswikisummary"] = 1 + } + + let wikis = projects.map { $0.notificationsApiWikiIdentifier } + dictionary["notwikis"] = wikis.isEmpty ? "*" : wikis.joined(separator: "|") + + return dictionary + } + + static func markAsReadOrUnread(identifierGroups: [RemoteNotification.IdentifierGroup], shouldMarkRead: Bool) -> Parameters? { + let IDs = identifierGroups.compactMap { $0.id } + + var dictionary = ["action": "echomarkread", + "format": "json"] + if shouldMarkRead { + dictionary["list"] = IDs.joined(separator: "|") + } else { + dictionary["unreadlist"] = IDs.joined(separator: "|") + } + + return dictionary + } + + static func markAllAsRead(project: WikimediaProject) -> Parameters? { + let dictionary = ["action": "echomarkread", + "all": "true", + "wikis": project.notificationsApiWikiIdentifier, + "format": "json"] + return dictionary + } + + static func markAllAsSeen(project: WikimediaProject) -> Parameters? { + let dictionary = ["action": "echomarkseen", + "wikis": project.notificationsApiWikiIdentifier, + "format": "json", + "type": "all"] + return dictionary + } + } +} + +extension RemoteNotificationsAPIController.ResultError: LocalizedError { + var errorDescription: String? { + return info + } +} + +// MARK: Public Notification Extensions + +public extension RemoteNotificationsAPIController.NotificationsResult.Notification { + + var key: String { + return "\(wiki)-\(id)" + } + + var date: Date? { + return DateFormatter.wmf_iso8601()?.date(from: timestamp.utciso8601) + } + + var pushContentText: String? { + return self.message?.header?.removingHTML + } + + var namespaceKey: Int? { + return self.title?.namespaceKey + } + + var titleFull: String? { + return self.title?.full + } + + func isNewerThan(timeAgo: TimeInterval) -> Bool { + guard let date = date else { + return false + } + + return date > Date().addingTimeInterval(-timeAgo) + } + + var namespace: PageNamespace? { + return PageNamespace(namespaceValue: title?.namespaceKey) + } +} + +// MARK: Test Helpers + +#if TEST + +extension RemoteNotificationsAPIController.NotificationsResult.Notification { + + init?(project: WikimediaProject, titleText: String, titleNamespace: PageNamespace, remoteNotificationType: RemoteNotificationType, date: Date, customID: String? = nil) { + + switch remoteNotificationType { + case .userTalkPageMessage: + self.category = "edit-user-talk" + self.type = "edit-user-talk" + self.section = "alert" + case .editReverted: + self.category = "reverted" + self.type = "reverted" + self.section = "alert" + default: + assertionFailure("Haven't set up test models for this type.") + return nil + } + + self.wiki = project.notificationsApiWikiIdentifier + + let identifier = customID ?? UUID().uuidString + self.id = identifier + + let timestamp = Timestamp(date: date) + self.timestamp = timestamp + self.title = Title(titleText: titleText, titleNamespace: titleNamespace) + self.agent = Agent() + + self.revisionID = nil + self.readString = nil + + self.message = Message(identifier: identifier) + self.sources = nil + } +} + +extension RemoteNotificationsAPIController.NotificationsResult.Notification.Timestamp { + init(date: Date) { + let dateString8601 = DateFormatter.wmf_iso8601().string(from: date) + let unixTimeInterval = date.timeIntervalSince1970 + self.utciso8601 = dateString8601 + self.utcunix = String(unixTimeInterval) + } +} + +extension RemoteNotificationsAPIController.NotificationsResult.Notification.Title { + init(titleText: String, titleNamespace: PageNamespace) { + + let namespaceText = titleNamespace.canonicalName + self.full = "\(namespaceText):\(titleText)" + self.namespace = titleNamespace.canonicalName.denormalizedPageTitle + self.namespaceKey = titleNamespace.rawValue + self.text = titleText + } +} + +extension RemoteNotificationsAPIController.NotificationsResult.Notification.Agent { + init() { + self.id = String(12345) + self.name = "Test Agent Name" + } +} + +extension RemoteNotificationsAPIController.NotificationsResult.Notification.Message { + init(identifier: String) { + self.header = "\(identifier)" + self.body = "Test body text for identifier: \(identifier)" + let primaryLink = RemoteNotificationLink(type: nil, url: URL(string:"https://en.wikipedia.org/wiki/Cat")!, label: "Label for primary link") + self.links = RemoteNotificationLinks(primary: primaryLink, secondary: nil, legacyPrimary: primaryLink) + } +} + +#endif diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsController.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsController.swift new file mode 100644 index 0000000..f08f3d5 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsController.swift @@ -0,0 +1,635 @@ +import CocoaLumberjackSwift +import Foundation + +public enum RemoteNotificationsControllerError: LocalizedError { + case databaseUnavailable + case attemptingToRefreshBeforeDeadline + case failurePullingAppLanguage + + public var errorDescription: String? { + return CommonStrings.genericErrorDescription + } +} + +@objc public final class RemoteNotificationsController: NSObject { + private let apiController: RemoteNotificationsAPIController + private let refreshDeadlineController = RemoteNotificationsRefreshDeadlineController() + private let languageLinkController: MWKLanguageLinkController + private let authManager: WMFAuthenticationManager + + private var _modelController: RemoteNotificationsModelController? + private var modelController: RemoteNotificationsModelController? { + get { + + guard let modelController = _modelController else { + DDLogError("Missing RemoteNotificationsModelController. Confirm Core Data stack was successfully set up.") + return nil + } + + return modelController + } + set { + _modelController = newValue + } + } + private var _operationsController: RemoteNotificationsOperationsController? + private var operationsController: RemoteNotificationsOperationsController? { + get { + + guard let operationsController = _operationsController else { + DDLogError("Missing RemoteNotificationsOperationsController. Confirm Core Data stack was successfully set up in RemoteNotificationsModelController.") + return nil + } + + return operationsController + } + set { + _operationsController = newValue + } + } + + public var isLoadingNotifications: Bool { + return operationsController?.isLoadingNotifications ?? false + } + + public var areFiltersEnabled: Bool { + return filterState.readStatus != .all || filterState.offProjects.count != 0 || filterState.offTypes.count != 0 + } + + public static let didUpdateFilterStateNotification = NSNotification.Name(rawValue: "RemoteNotificationsControllerDidUpdateFilterState") + + public let configuration: Configuration + + @objc public required init(session: Session, configuration: Configuration, languageLinkController: MWKLanguageLinkController, authManager: WMFAuthenticationManager) { + + self.apiController = RemoteNotificationsAPIController(session: session, configuration: configuration) + self.configuration = configuration + self.authManager = authManager + self.languageLinkController = languageLinkController + + super.init() + + do { + modelController = try RemoteNotificationsModelController(containerURL: FileManager.default.wmf_containerURL()) + } catch let error { + DDLogError("Failed to initialize RemoteNotificationsModelController: \(error)") + modelController = nil + } + + NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(authManagerDidLogIn), name:WMFAuthenticationManager.didLogInNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(authManagerDidLogOut), name: WMFAuthenticationManager.didLogOutNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(modelControllerDidLoadPersistentStores(_:)), name: RemoteNotificationsModelController.didLoadPersistentStoresNotification, object: nil) + + } + + // MARK: NSNotification Listeners + + @objc private func modelControllerDidLoadPersistentStores(_ note: Notification) { + + guard let modelController = modelController else { + return + } + + if let object = note.object, let error = object as? Error { + DDLogError("RemoteNotificationsModelController failed to load persistent stores with error \(error); not instantiating RemoteNotificationsOperationsController") + return + } + + operationsController = RemoteNotificationsOperationsController(languageLinkController: languageLinkController, authManager: authManager, apiController: apiController, modelController: modelController) + + populateFilterStateFromPersistence() + } + + @objc private func applicationDidBecomeActive() { + loadNotifications(force: false) + } + + @objc private func authManagerDidLogOut() { + do { + filterState = RemoteNotificationsFilterState(readStatus: .all, offTypes: [], offProjects: []) + allInboxProjects = [] + try modelController?.resetDatabaseAndSharedCache() + } catch let error { + DDLogError("Error resetting notifications database on logout: \(error)") + } + + } + + @objc private func authManagerDidLogIn() { + loadNotifications(force: true) + } + + // MARK: Public + + /// Fetches notifications from the server and saves them into the local database. Updates local database on a backgroundContext. + /// - Parameters: + /// - force: Flag to force an API call, otherwise this will exit early if it's been less than 30 seconds since the last load attempt. + /// - completion: Completion block called once refresh attempt is complete. + public func loadNotifications(force: Bool, completion: ((Result) -> Void)? = nil) { + + guard let operationsController = operationsController else { + completion?(.failure(RemoteNotificationsControllerError.databaseUnavailable)) + return + } + + guard authManager.isLoggedIn else { + completion?(.failure(RequestError.unauthenticated)) + return + } + + if !force && !refreshDeadlineController.shouldRefresh { + completion?(.failure(RemoteNotificationsControllerError.attemptingToRefreshBeforeDeadline)) + return + } + + operationsController.loadNotifications { [weak self] result in + + guard let self = self else { + return + } + + switch result { + case .success: + do { + try self.updateAllInboxProjects() + completion?(.success(())) + } catch let error { + completion?(.failure(error)) + } + case .failure(let error): + completion?(.failure(error)) + } + } + + refreshDeadlineController.reset() + } + + /// Triggers fetching notifications from the server and saving them into the local database with no completion handler. Used as a bridge for Objective-C use as the `Result` type is unavailable there. + /// - Parameter force: Flag to force an API call, otherwise this will exit early if it's been less than 30 seconds since the last load attempt. + @objc public func triggerLoadNotifications(force: Bool) { + loadNotifications(force: force) + } + + /// Marks notifications as read or unread in the local database and on the server. Errors are not returned. Updates local database on a backgroundContext. + /// - Parameters: + /// - identifierGroups: Set of IdentifierGroup objects to identify the correct notification. + /// - shouldMarkRead: Boolean for marking as read or unread. + public func markAsReadOrUnread(identifierGroups: Set, shouldMarkRead: Bool, completion: ((Result) -> Void)? = nil) { + + guard let operationsController = operationsController else { + completion?(.failure(RemoteNotificationsControllerError.databaseUnavailable)) + return + } + + guard authManager.isLoggedIn else { + completion?(.failure(RequestError.unauthenticated)) + return + } + + operationsController.markAsReadOrUnread(identifierGroups: identifierGroups, shouldMarkRead: shouldMarkRead, languageLinkController: languageLinkController, completion: completion) + } + + + /// Asks server to mark all notifications as read for projects that contain local unread notifications. Errors are not returned. Updates local database on a backgroundContext. + public func markAllAsRead(completion: ((Result) -> Void)? = nil) { + + guard let operationsController = operationsController else { + completion?(.failure(RemoteNotificationsControllerError.databaseUnavailable)) + return + } + + guard authManager.isLoggedIn else { + completion?(.failure(RequestError.unauthenticated)) + return + } + + operationsController.markAllAsRead(languageLinkController: languageLinkController, completion: completion) + } + + /// Asks server to mark all notifications as seen for the primary app language + public func markAllAsSeen(completion: @escaping ((Result) -> Void)) { + + guard authManager.isLoggedIn else { + completion(.failure(RequestError.unauthenticated)) + return + } + + guard let appLanguage = languageLinkController.appLanguage else { + completion(.failure(RemoteNotificationsControllerError.failurePullingAppLanguage)) + return + } + + let appLanguageProject = WikimediaProject.wikipedia(appLanguage.languageCode, appLanguage.localizedName, appLanguage.languageVariantCode) + apiController.markAllAsSeen(project: appLanguageProject, completion: completion) + } + + /// Passthrough method to listen for NSManagedObjectContextObjectsDidChange notifications on the viewContext, in order to encapsulate viewContext within the WMF Framework. + /// - Parameters: + /// - observer: NSNotification observer + /// - selector: Selector to call on the observer once the NSNotification fires + public func addObserverForViewContextChanges(observer: AnyObject, selector: + Selector) { + + guard let viewContext = modelController?.viewContext else { + return + } + + NotificationCenter.default.addObserver(observer, selector: selector, name: Notification.Name.NSManagedObjectContextObjectsDidChange, object: viewContext) + } + + /// Fetches notifications from the local database. Uses the viewContext and must be called from the main thread + /// - Parameters: + /// - fetchLimit: Number of notifications to fetch. Defaults to 50. + /// - fetchOffset: Offset for fetching notifications. Use when fetching later pages of data + /// - Returns: Array of RemoteNotifications + public func fetchNotifications(fetchLimit: Int = 50, fetchOffset: Int = 0, completion: @escaping (Result<[RemoteNotification], Error>) -> Void) { + guard let modelController = modelController else { + return completion(.failure(RemoteNotificationsControllerError.databaseUnavailable)) + } + + let fetchFromDatabase: () -> Void = { [weak self] in + guard let self = self else { + return + } + + let predicate = self.predicateForFilterSavedState(self.filterState) + + do { + let notifications = try modelController.fetchNotifications(fetchLimit: fetchLimit, fetchOffset: fetchOffset, predicate: predicate) + completion(.success(notifications)) + } catch let error { + completion(.failure(error)) + } + } + + + guard !isFullyImported else { + fetchFromDatabase() + return + } + + loadNotifications(force: true) { result in + + switch result { + case .success: + fetchFromDatabase() + case .failure(let error): + completion(.failure(error)) + } + } + } + + /// Fetches a count of unread notifications from the local database. Uses the viewContext and must be called from the main thread + @objc public func numberOfUnreadNotifications() throws -> NSNumber { + + guard let modelController = modelController else { + throw RemoteNotificationsControllerError.databaseUnavailable + } + + let count = try modelController.numberOfUnreadNotifications() + return NSNumber(value: count) + } + + /// Fetches a count of all notifications from the local database. Uses the viewContext and must be called from the main thread + public func numberOfAllNotifications() throws -> Int { + + guard let modelController = modelController else { + throw RemoteNotificationsControllerError.databaseUnavailable + } + + return try modelController.numberOfAllNotifications() + } + + /// List of all possible inbox projects available Notifications Center. Used for populating the Inbox screen and the project count toolbar + public private(set) var allInboxProjects: Set = [] + + /// A count of showing inbox projects (i.e. allInboxProjects minus those toggled off in the inbox filter screen) + public var countOfShowingInboxProjects: Int { + let filteredProjects = filterState.offProjects + return allInboxProjects.subtracting(filteredProjects).count + } + + @objc public func updateCacheWithCurrentUnreadNotificationsCount() throws { + let currentCount = try numberOfUnreadNotifications().intValue + let sharedCache = SharedContainerCache(fileName: SharedContainerCacheCommonNames.pushNotificationsCache, defaultCache: { PushNotificationsCache(settings: .default, notifications: []) }) + var pushCache = sharedCache.loadCache() + pushCache.currentUnreadCount = currentCount + sharedCache.saveCache(pushCache) + } + + public var filterPredicate: NSPredicate? { + predicateForFilterSavedState(filterState) + } + + public var filterState: RemoteNotificationsFilterState = RemoteNotificationsFilterState(readStatus: .all, offTypes: [], offProjects: []) { + didSet { + + guard let modelController = modelController else { + return + } + + // save to library + modelController.setFilterSettingsToLibrary(dictionary: filterState.serialize()) + + NotificationCenter.default.post(name: RemoteNotificationsController.didUpdateFilterStateNotification, object: nil) + } + } + + public var isFullyImported: Bool { + + guard let modelController = modelController else { + return false + } + + let appLanguageProjects = languageLinkController.preferredLanguages.map { WikimediaProject.wikipedia($0.languageCode, $0.localizedName, $0.languageVariantCode) } + for project in appLanguageProjects { + if !modelController.isProjectAlreadyImported(project: project) { + return false + } + } + + return true + } + + // MARK: Internal + + @objc func deleteLegacyDatabaseFiles() throws { + + guard let modelController = modelController else { + throw RemoteNotificationsControllerError.databaseUnavailable + } + + try modelController.deleteLegacyDatabaseFiles() + } + + // MARK: Private + + /// Pulls filter state from local persistence and saves it in memory + private func populateFilterStateFromPersistence() { + guard let modelController = modelController, + let persistentFiltersDict = modelController.getFilterSettingsFromLibrary(), + let persistentFilters = RemoteNotificationsFilterState(nsDictionary: persistentFiltersDict, languageLinkController: languageLinkController) else { + return + } + + self.filterState = persistentFilters + } + + /// Fetches from the local database all projects that contain a local notification on device. Uses the viewContext and must be called from the main thread. + /// - Returns: Array of WikimediaProject + private func projectsFromLocalNotifications() throws -> Set { + guard let modelController = modelController else { + return [] + } + + let wikis = try modelController.distinctWikis(predicate: nil) + let projects = wikis.compactMap { WikimediaProject(notificationsApiIdentifier: $0, languageLinkController: languageLinkController) } + return Set(projects) + } + + private func predicateForFilterSavedState(_ filterState: RemoteNotificationsFilterState) -> NSPredicate? { + + var readStatusPredicate: NSPredicate? + let readStatus = filterState.readStatus + + switch readStatus { + case .all: + readStatusPredicate = nil + case .read: + readStatusPredicate = NSPredicate(format: "isRead == %@", NSNumber(value: true)) + case .unread: + readStatusPredicate = NSPredicate(format: "isRead == %@", NSNumber(value: false)) + } + + let offTypes = filterState.offTypes + let onTypes = RemoteNotificationFilterType.orderingForFilters.filter {!offTypes.contains($0)} + + var typePredicates: [NSPredicate] = [] + let otherIsOff = offTypes.contains(.other) + + if onTypes.isEmpty { + return NSPredicate(format: "FALSEPREDICATE") + } + + if otherIsOff { + typePredicates = onTypes.compactMap { settingType in + let categoryStrings = RemoteNotificationFilterType.categoryStringsForFilterType(type: settingType) + let typeStrings = RemoteNotificationFilterType.typeStringForFilterType(type: settingType) + + guard categoryStrings.count > 0 && typeStrings.count > 0 else { + return nil + } + + return NSPredicate(format: "(categoryString IN %@ AND typeString IN %@)", categoryStrings, typeStrings) + } + } else { + typePredicates = offTypes.compactMap { settingType in + let categoryStrings = RemoteNotificationFilterType.categoryStringsForFilterType(type: settingType) + let typeStrings = RemoteNotificationFilterType.typeStringForFilterType(type: settingType) + + guard categoryStrings.count > 0 && typeStrings.count > 0 else { + return nil + } + + return NSPredicate(format: "NOT (categoryString IN %@ AND typeString IN %@)", categoryStrings, typeStrings) + } + } + + let offProjects = filterState.offProjects + let offProjectPredicates: [NSPredicate] = offProjects.compactMap { return NSPredicate(format: "NOT (wiki == %@)", $0.notificationsApiWikiIdentifier) } + + guard readStatusPredicate != nil || typePredicates.count > 0 || offProjectPredicates.count > 0 else { + return nil + } + + var combinedOffTypePredicate: NSPredicate? = nil + if typePredicates.count > 0 { + if otherIsOff { + combinedOffTypePredicate = NSCompoundPredicate(orPredicateWithSubpredicates: typePredicates) + } else { + combinedOffTypePredicate = NSCompoundPredicate(andPredicateWithSubpredicates: typePredicates) + } + } + + var combinedOffProjectPredicate: NSPredicate? = nil + if offProjectPredicates.count > 0 { + combinedOffProjectPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: offProjectPredicates) + } + + let finalPredicates = [readStatusPredicate, combinedOffTypePredicate, combinedOffProjectPredicate].compactMap { $0 } + + return finalPredicates.count > 0 ? NSCompoundPredicate(andPredicateWithSubpredicates: finalPredicates) : nil + } + + /// Updates value of allInboxProjects by gathering list of static projects, app language projects, and local notifications projects. Involves a fetch to the local database. Uses the viewContext and must be called from the main thread + private func updateAllInboxProjects() throws { + let sideProjects: Set = [.commons, .wikidata] + + let appLanguageProjects = languageLinkController.preferredLanguages.map { WikimediaProject.wikipedia($0.languageCode, $0.localizedName, $0.languageVariantCode) } + + var inboxProjects = sideProjects.union(appLanguageProjects) + let localProjects = try projectsFromLocalNotifications() + + for localProject in localProjects { + inboxProjects.insert(localProject) + } + + self.allInboxProjects = inboxProjects + } +} + +public struct RemoteNotificationsFilterState: Equatable { + + public enum ReadStatus: Int, CaseIterable { + case all + case read + case unread + + var localizedDescription: String { + switch self { + case .all: + return CommonStrings.notificationsCenterAllNotificationsStatus + case .read: + return CommonStrings.notificationsCenterReadNotificationsStatus + case .unread: + return CommonStrings.notificationsCenterUnreadNotificationsStatus + } + } + } + + public let readStatus: ReadStatus + public let offTypes: Set + public let offProjects: Set + + public init(readStatus: ReadStatus, offTypes: Set, offProjects: Set) { + self.readStatus = readStatus + self.offTypes = offTypes + self.offProjects = offProjects + } + + private var isReadStatusOrTypeFiltered: Bool { + return (readStatus != .all || !offTypes.isEmpty) + } + + public var stateDescription: String { + let filteredBy = WMFLocalizedString("notifications-center-status-filtered-by", value: "Filtered by", comment: "Status header text in Notifications Center displayed when filtering notifications.") + + let allNotifications = WMFLocalizedString("notifications-center-status-all-notifications", value: "All notifications", comment: "Status header text in Notifications Center displayed when viewing unfiltered list of notifications.") + + let headerText = isReadStatusOrTypeFiltered ? filteredBy : allNotifications + return headerText + } + + public static var detailDescriptionHighlightDelineator = "**" + + public func detailDescription(totalProjectCount: Int, showingProjectCount: Int) -> String? { + // Generic templates + + let doubleConcatenationTemplate = WMFLocalizedString("notifications-center-status-double-concatenation", value: "%1$@ in %2$@", comment: "Notifications Center status description. %1$@ is replaced with the currently applied filters and %2$@ is replaced with the count of projects/inboxes.") + + let tripleConcatenationTemplate = WMFLocalizedString("notifications-center-status-triple-concatenation", value: "%1$@ and %2$@ in %3$@", comment: "Notifications Center status description. %1$@ is replaced with the currently applied read status filter, %2$@ is replaced with the count of notification type filters, and %3$@ is replaced with the count of projects/inboxes.") + + // Specific plurals + + let inProjects = WMFLocalizedString("notifications-center-status-in-projects", value: "{{PLURAL:%1$d|1=In 1 project|In %1$d projects}}", comment: "Notifications Center status description when filtering by projects/inboxes. %1$d is replaced by the count of local projects.") + + let projectsPlain = WMFLocalizedString("notifications-center-status-in-projects-plain", value: "{{PLURAL:%1$d|1=1 project|%1$d projects}}", comment: "Notifications Center status description when filtering by projects/inboxes, without preposition. %1$d is replaced by the count of local projects.") + + let typesPlain = WMFLocalizedString("notifications-center-status-in-types", value: "{{PLURAL:%1$d|1=1 type|%1$d types}}", comment: "Notifications Center status description when filtering by types. %1$d is replaced by the count of filtered types.") + + var descriptionString: String? + + switch (readStatus, offTypes.count, offProjects.count) { + case (.all, 0, 0): + // No filtering + descriptionString = String.localizedStringWithFormat(inProjects, totalProjectCount) + case (.all, 1..., 0): + // Only filtering by type + let typesString = String.localizedStringWithFormat(typesPlain, offTypes.count).highlightDelineated + let totalProjectString = String.localizedStringWithFormat(projectsPlain, totalProjectCount) + descriptionString = String.localizedStringWithFormat(doubleConcatenationTemplate, typesString, totalProjectString) + case (.all, 0, 1...): + // Only filtering by project/inbox + descriptionString = String.localizedStringWithFormat(inProjects, showingProjectCount).highlightDelineated + case (.read, 0, 0), (.unread, 0, 0): + // Only filtering by read status + let totalProjectString = String.localizedStringWithFormat(projectsPlain, totalProjectCount) + descriptionString = String.localizedStringWithFormat(doubleConcatenationTemplate, readStatus.localizedDescription.highlightDelineated, totalProjectString) + case (.read, 1..., 0), (.unread, 1..., 0): + // Filtering by read status and type + let typesString = String.localizedStringWithFormat(typesPlain, offTypes.count).highlightDelineated + let totalProjectString = String.localizedStringWithFormat(projectsPlain, totalProjectCount) + descriptionString = String.localizedStringWithFormat(tripleConcatenationTemplate, readStatus.localizedDescription.highlightDelineated, typesString, totalProjectString) + case (.read, 0, 1...), (.unread, 0, 1...): + // Filtering by read status and project/inbox + let projectString = String.localizedStringWithFormat(projectsPlain, showingProjectCount).highlightDelineated + descriptionString = String.localizedStringWithFormat(doubleConcatenationTemplate, readStatus.localizedDescription.highlightDelineated, projectString) + case (let readStatus, 1..., 1...): + // Filtering by type, project/inbox, and potentially read status + switch readStatus { + case .all: + // Filtering by type and project/inbox + let typesString = String.localizedStringWithFormat(typesPlain, offTypes.count).highlightDelineated + let projectString = String.localizedStringWithFormat(projectsPlain, showingProjectCount).highlightDelineated + descriptionString = String.localizedStringWithFormat(doubleConcatenationTemplate, typesString, projectString) + case .read, .unread: + // Filtering by read status, type, and project/inbox + let readString = readStatus.localizedDescription.highlightDelineated + let typesString = String.localizedStringWithFormat(typesPlain, offTypes.count).highlightDelineated + let projectString = String.localizedStringWithFormat(projectsPlain, showingProjectCount).highlightDelineated + descriptionString = String.localizedStringWithFormat(tripleConcatenationTemplate, readString, typesString, projectString) + } + default: + break + } + + return descriptionString + } + + private let readStatusKey = "readStatus" + private let offTypesKey = "offTypes" + private let offProjectsKey = "offProjects" + + func serialize() -> NSDictionary? { + let mutableDictionary = NSMutableDictionary() + let numReadStatus = NSNumber(value: readStatus.rawValue) + mutableDictionary.setValue(numReadStatus, forKey: readStatusKey) + let offTypeIdentifiers = offTypes.compactMap { $0.filterIdentifier as NSString? } + mutableDictionary.setValue(NSArray(array: offTypeIdentifiers), forKey: offTypesKey) + let offProjectIdentifiers = offProjects.compactMap { $0.notificationsApiWikiIdentifier as NSString? } + mutableDictionary.setValue(NSArray(array: offProjectIdentifiers), forKey: offProjectsKey) + + return mutableDictionary.copy() as? NSDictionary + } + + init?(nsDictionary: NSDictionary, languageLinkController: MWKLanguageLinkController) { + + guard let dictionary = nsDictionary as? [String: AnyObject] else { + return nil + } + + guard let numReadStatus = dictionary[readStatusKey] as? NSNumber, + let readStatus = ReadStatus(rawValue: numReadStatus.intValue), + let offTypeIdentifiers = dictionary[offTypesKey] as? [NSString], + let offProjectApiIdentifiers = dictionary[offProjectsKey] as? [NSString] else { + return nil + } + + let offTypes = offTypeIdentifiers.compactMap { RemoteNotificationFilterType(from: $0 as String) } + let offProjects = offProjectApiIdentifiers.compactMap { WikimediaProject(notificationsApiIdentifier: $0 as String, languageLinkController: languageLinkController) } + + self.readStatus = readStatus + self.offTypes = Set(offTypes) + self.offProjects = Set(offProjects) + } +} + +fileprivate extension String { + + /// Delineated section of string to be highlighted in attributed string + var highlightDelineated: String { + return RemoteNotificationsFilterState.detailDescriptionHighlightDelineator + self + RemoteNotificationsFilterState.detailDescriptionHighlightDelineator + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsOperationsController.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsOperationsController.swift new file mode 100644 index 0000000..1d513fa --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsOperationsController.swift @@ -0,0 +1,262 @@ +import CocoaLumberjackSwift + +enum RemoteNotificationsOperationsError: LocalizedError { + case failurePullingAppLanguage + case individualErrors([Error]) + + var errorDescription: String? { + + switch self { + case .individualErrors(let errors): + if let firstError = errors.first { + return (firstError as NSError).alertMessage() + } + default: + break + } + + return CommonStrings.genericErrorDescription + } +} + +public extension Notification.Name { + static let NotificationsCenterLoadingDidStart = Notification.Name("NotificationsCenterLoadingDidStart") // fired when notifications have begun importing or refreshing + static let NotificationsCenterLoadingDidEnd = Notification.Name("NotificationsCenterLoadingDidEnd") // fired when notifications have ended importing or refreshing +} + +class RemoteNotificationsOperationsController: NSObject { + private let apiController: RemoteNotificationsAPIController + private let modelController: RemoteNotificationsModelController + private let operationQueue: OperationQueue + private let languageLinkController: MWKLanguageLinkController + private let authManager: WMFAuthenticationManager + private(set) var isLoadingNotifications = false + private var loadingNotificationsCompletionBlocks: [(Result) -> Void] = [] + + required init(languageLinkController: MWKLanguageLinkController, authManager: WMFAuthenticationManager, apiController: RemoteNotificationsAPIController, modelController: RemoteNotificationsModelController) { + self.apiController = apiController + self.modelController = modelController + + operationQueue = OperationQueue() + + self.languageLinkController = languageLinkController + self.authManager = authManager + + super.init() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + // MARK: Public + + /// Kicks off operations to fetch and persist read and unread history of notifications from app languages, Commons, and Wikidata, + other projects with unread notifications. Designed to automatically page and fully import once per installation, then only fetch new notifications for each project when called after that. Will not attempt if loading is already in progress. Must be called from main thread. + /// - Parameter completion: Block to run once operations have completed. Dispatched to main thread. + func loadNotifications(_ completion: ((Result) -> Void)? = nil) { + assert(Thread.isMainThread) + + if let completion = completion { + loadingNotificationsCompletionBlocks.append(completion) + } + + // Purposefully not calling completion block here, because we are tracking it in line above. It will be called when currently running loading operations complete. + guard !isLoadingNotifications else { + return + } + + isLoadingNotifications = true + NotificationCenter.default.post(name: Notification.Name.NotificationsCenterLoadingDidStart, object: nil) + + kickoffPagingOperations { [weak self] result in + DispatchQueue.main.async { + self?.isLoadingNotifications = false + self?.loadingNotificationsCompletionBlocks.forEach { completionBlock in + completionBlock(result) + } + + self?.loadingNotificationsCompletionBlocks.removeAll() + + NotificationCenter.default.post(name: Notification.Name.NotificationsCenterLoadingDidEnd, object: nil) + } + } + } + + func markAsReadOrUnread(identifierGroups: Set, shouldMarkRead: Bool, languageLinkController: MWKLanguageLinkController, completion: ((Result) -> Void)? = nil) { + assert(Thread.isMainThread) + + // sort identifier groups into dictionary keyed by wiki + let requestDictionary: [String: Set] = identifierGroups.reduce([String: Set]()) { partialResult, identifierGroup in + + var result = partialResult + guard let wiki = identifierGroup.wiki else { + return result + } + + result[wiki, default: Set()].insert(identifierGroup) + + return result + } + + // turn into array of operations + let operations: [RemoteNotificationsMarkReadOrUnreadOperation] = requestDictionary.compactMap { element in + + let wiki = element.key + guard let project = WikimediaProject(notificationsApiIdentifier: wiki, languageLinkController: languageLinkController) else { + return nil + } + + return RemoteNotificationsMarkReadOrUnreadOperation(project: project, apiController: apiController, modelController: modelController, identifierGroups: identifierGroups, shouldMarkRead: shouldMarkRead) + } + + let completionOperation = BlockOperation { + DispatchQueue.main.async { + let errors = operations.compactMap { $0.error } + if errors.count > 0 { + completion?(.failure(RemoteNotificationsOperationsError.individualErrors(errors))) + } else { + completion?(.success(())) + } + } + } + + for operation in operations { + completionOperation.addDependency(operation) + } + + operationQueue.addOperations(operations + [completionOperation], waitUntilFinished: false) + } + + func markAllAsRead(languageLinkController: MWKLanguageLinkController, completion: ((Result) -> Void)? = nil) { + assert(Thread.isMainThread) + + let wikisWithUnreadNotifications: Set + do { + wikisWithUnreadNotifications = try modelController.distinctWikisWithUnreadNotifications() + } catch let error { + completion?(.failure(error)) + return + } + + let projects = wikisWithUnreadNotifications.compactMap { WikimediaProject(notificationsApiIdentifier: $0, languageLinkController: self.languageLinkController) } + + let operations = projects.map { RemoteNotificationsMarkAllAsReadOperation(project: $0, apiController: self.apiController, modelController: self.modelController) } + + let completionOperation = BlockOperation { + DispatchQueue.main.async { + let errors = operations.compactMap { $0.error } + if errors.count > 0 { + completion?(.failure(RemoteNotificationsOperationsError.individualErrors(errors))) + } else { + completion?(.success(())) + } + } + } + + for operation in operations { + completionOperation.addDependency(operation) + } + + self.operationQueue.addOperations(operations + [completionOperation], waitUntilFinished: false) + } + + // MARK: Private + + /// Generates the correct paging operation (Import or Refresh) based on a project's persisted imported state. + /// - Parameter project: WikimediaProject to evaluate + /// - Parameter isAppLanguageProject: Boolean if this project is for the app primary language + /// - Returns: Appropriate RemoteNotificationsPagingOperation subclass instance + private func pagingOperationForProject(_ project: WikimediaProject, isAppLanguageProject: Bool) -> RemoteNotificationsPagingOperation { + + if modelController.isProjectAlreadyImported(project: project) { + return RemoteNotificationsRefreshOperation(project: project, apiController: self.apiController, modelController: modelController, needsCrossWikiSummary: isAppLanguageProject) + } else { + return RemoteNotificationsImportOperation(project: project, apiController: self.apiController, modelController: modelController, needsCrossWikiSummary: isAppLanguageProject) + } + } + + private func secondaryProjects(appLanguage: MWKLanguageLink) -> [WikimediaProject] { + + let otherLanguages = languageLinkController.preferredLanguages.filter { $0.languageCode != appLanguage.languageCode } + + var secondaryProjects: [WikimediaProject] = otherLanguages.map { .wikipedia($0.languageCode, $0.localizedName, $0.languageVariantCode) } + secondaryProjects.append(.commons) + secondaryProjects.append(.wikidata) + + return secondaryProjects + } + + /// Method that instantiates the appropriate paging operations for fetching & persisting remote notifications and adds them to the operation queue. Must be called from main thread. + /// - Parameters: + /// - completion: Block to run after operations have completed. + private func kickoffPagingOperations(completion: @escaping (Result) -> Void) { + assert(Thread.isMainThread) + + guard let appLanguage = languageLinkController.appLanguage else { + completion(.failure(RemoteNotificationsOperationsError.failurePullingAppLanguage)) + return + } + + let appLanguageProject = WikimediaProject.wikipedia(appLanguage.languageCode, appLanguage.localizedName, appLanguage.languageVariantCode) + let secondaryProjects = secondaryProjects(appLanguage: appLanguage) + + // basic operations first - primary language then secondary (languages, commons & wikidata) + let appLanguageOperation = pagingOperationForProject(appLanguageProject, isAppLanguageProject: true) + let secondaryOperations = secondaryProjects.map { project in + pagingOperationForProject(project, isAppLanguageProject: false) + } + + // BEGIN: chained cross wiki operations + // this generates additional API calls to fetch extra unread messages by inspecting the app language operation's cross wiki summary notification object in its response + let crossWikiGroupOperation = RemoteNotificationsRefreshCrossWikiGroupOperation(appLanguageProject: appLanguageProject, secondaryProjects: secondaryProjects, languageLinkController: languageLinkController, apiController: apiController, modelController: modelController) + let crossWikiAdapterOperation = BlockOperation { [weak crossWikiGroupOperation] in + crossWikiGroupOperation?.crossWikiSummaryNotification = appLanguageOperation.crossWikiSummaryNotification + } + crossWikiAdapterOperation.addDependency(appLanguageOperation) + crossWikiGroupOperation.addDependency(crossWikiAdapterOperation) + // END: chained cross wiki operations + + // BEGIN: chained reauthentication operations + // these will ask the authManager to reauthenticate if the app language operation has an unauthenticaated error code in it's response + // then it will cancel existing operations running and recursively call kickoffPagingOperations again + let reauthenticateOperation = RemoteNotificationsReauthenticateOperation(authManager: authManager) + let reauthenticateAdapterOperation = BlockOperation { [weak reauthenticateOperation] in + reauthenticateOperation?.appLanguageOperationError = appLanguageOperation.error + } + reauthenticateAdapterOperation.addDependency(appLanguageOperation) + reauthenticateOperation.addDependency(reauthenticateAdapterOperation) + let recursiveKickoffOperation = BlockOperation { [weak self] in + + guard let self = self else { + return + } + + if reauthenticateOperation.didReauthenticate { + DispatchQueue.main.async { + self.operationQueue.cancelAllOperations() + self.kickoffPagingOperations(completion: completion) + } + } + } + recursiveKickoffOperation.addDependency(reauthenticateOperation) + // END: chained reauthentication operations + + let finalListOfOperations = [appLanguageOperation, crossWikiAdapterOperation, crossWikiGroupOperation, reauthenticateAdapterOperation, reauthenticateOperation, recursiveKickoffOperation] + secondaryOperations + + let completionOperation = BlockOperation { + + let errors = finalListOfOperations.compactMap { ($0 as? AsyncOperation)?.error } + if errors.count > 0 { + completion(.failure(RemoteNotificationsOperationsError.individualErrors(errors))) + } else { + completion(.success(())) + } + } + + for operation in finalListOfOperations { + completionOperation.addDependency(operation) + } + + self.operationQueue.addOperations(finalListOfOperations + [completionOperation], waitUntilFinished: false) + } +} diff --git a/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsRefreshDeadlineController.swift b/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsRefreshDeadlineController.swift new file mode 100644 index 0000000..ff79e35 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Remote Notifications/RemoteNotificationsRefreshDeadlineController.swift @@ -0,0 +1,23 @@ +import Foundation + +final class RemoteNotificationsRefreshDeadlineController { + private let deadline: TimeInterval = 30 // 30 seconds + private var now: CFAbsoluteTime { + return CFAbsoluteTimeGetCurrent() + } + + public var shouldRefresh: Bool { + + guard let lastRefreshTime = lastRefreshTime else { + return true + } + + return now - lastRefreshTime > deadline + } + + private var lastRefreshTime: CFAbsoluteTime? + + public func reset() { + lastRefreshTime = now + } +} diff --git a/Apps/Wikipedia/WMF Framework/RepeatingTimer.swift b/Apps/Wikipedia/WMF Framework/RepeatingTimer.swift new file mode 100644 index 0000000..5bc267d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/RepeatingTimer.swift @@ -0,0 +1,53 @@ +public class RepeatingTimer { + private let source: DispatchSourceTimer + private let semaphore: DispatchSemaphore = DispatchSemaphore(value: 1) + private var isSet: Bool = true + + public init(_ intervalInSeconds: TimeInterval, afterDelay delayInSeconds: TimeInterval? = nil, leeway leewayInSeconds: TimeInterval? = nil, on queue: DispatchQueue = DispatchQueue.global(qos: .background), _ handler: @escaping () -> Void) { + + let interval = DispatchTimeInterval.nanoseconds(Int(intervalInSeconds * TimeInterval(NSEC_PER_SEC))) + + let delay: DispatchTimeInterval + if let delayInSeconds = delayInSeconds { + delay = DispatchTimeInterval.nanoseconds(Int(delayInSeconds * TimeInterval(NSEC_PER_SEC))) + } else { + delay = interval + } + + let leeway: DispatchTimeInterval + if let leewayInSeconds = leewayInSeconds { + leeway = DispatchTimeInterval.nanoseconds(Int(leewayInSeconds * TimeInterval(NSEC_PER_SEC))) + } else { + leeway = DispatchTimeInterval.nanoseconds(Int(0.1 * intervalInSeconds * TimeInterval(NSEC_PER_SEC))) + } + + self.source = DispatchSource.makeTimerSource(queue: queue) + self.source.schedule(deadline: DispatchTime.now() + delay, repeating: interval, leeway: leeway) + self.source.setEventHandler(handler: handler) + self.source.resume() + } + + public func resume() { + semaphore.wait() + defer { + semaphore.signal() + } + guard !isSet else { + return + } + isSet = true + self.source.resume() + } + + public func pause() { + semaphore.wait() + defer { + semaphore.signal() + } + guard isSet else { + return + } + isSet = false + source.suspend() + } +} diff --git a/Apps/Wikipedia/WMF Framework/RetryBlockTask.swift b/Apps/Wikipedia/WMF Framework/RetryBlockTask.swift new file mode 100644 index 0000000..df69922 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/RetryBlockTask.swift @@ -0,0 +1,52 @@ +import Foundation + +/// Retry executing a block a number of times waiting for a success or failure. +public class RetryBlockTask { + + private let queue = DispatchQueue(label: "org.wikimedia.wikipedia.RetryBlockTask") + + private var retryCount: Int + private let retryInterval: TimeInterval + private let block: () -> Bool + private var completionHandler: ((Bool) -> Void)? + + /// Creates a task that will retry executing a block a number of times, completing early if the block return `true`. + /// - Parameters: + /// - retryCount: Maximum number of times to execute the block. The task completes early if the block returns `true`. + /// - retryInterval: Time (in seconds) between block execution attempts. + /// - block: A block to execute, which returns `true` if completed successfully or `false` to retry. The task does not attempt to retry if `true` is returned. + public init(retryCount: Int = 3, retryInterval: TimeInterval = 1.5, block: @escaping () -> Bool) { + self.retryCount = retryCount + self.retryInterval = retryInterval + self.block = block + } + + /// Start the task. + /// - Parameter completionHandler: At the end of execution, returns `true` if the block has indicated it has completed successfully or `false` if not. + public func start(completionHandler: @escaping (Bool) -> Void) { + self.completionHandler = completionHandler + tick() + } + + private func tick() { + queue.async { [weak self] in + guard let self = self else { return } + + let success = self.block() + if success || self.retryCount <= 1 { + self.completionHandler?(success) + return + } + + self.retryCount -= 1 + self.queue.asyncAfter(deadline: .now() + self.retryInterval) { [weak self] in + self?.tick() + } + } + } + + deinit { + completionHandler = nil + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Router.swift b/Apps/Wikipedia/WMF Framework/Router.swift new file mode 100644 index 0000000..0feb427 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Router.swift @@ -0,0 +1,258 @@ +@objc(WMFRouter) +public class Router: NSObject { + public enum Destination: Equatable { + case inAppLink(_: URL) + case externalLink(_: URL) + case article(_: URL) + case articleHistory(_: URL, articleTitle: String) + case articleDiffCompare(_: URL, fromRevID: Int?, toRevID: Int?) + case articleDiffSingle(_: URL, fromRevID: Int?, toRevID: Int?) + case talk(_: URL) + case userTalk(_: URL) + case search(_: URL, term: String?) + case audio(_: URL) + case onThisDay(_: Int?) + case readingListsImport(encodedPayload: String) + case login + } + + unowned let configuration: Configuration + + required init(configuration: Configuration) { + self.configuration = configuration + } + + // MARK: Public + + /// Gets the appropriate in-app destination for a given URL + public func destination(for url: URL, loggedInUsername: String?) -> Destination { + + guard let siteURL = url.wmf_site, + let project = WikimediaProject(siteURL: siteURL) else { + + guard url.isWikimediaHostedAudioFileLink else { + return webViewDestinationForHostURL(url) + } + + return .audio(url.byMakingAudioFileCompatibilityAdjustments) + } + + return destinationForHostURL(url, project: project, loggedInUsername: loggedInUsername) + } + + public func doesOpenInBrowser(for url: URL, loggedInUsername: String?) -> Bool { + return [.externalLink(url), .inAppLink(url)].contains(destination(for: url, loggedInUsername: loggedInUsername)) + } + + + // MARK: Internal and Private + + private let mobilediffRegexCompare = try! NSRegularExpression(pattern: "^mobilediff/([0-9]+)\\.\\.\\.([0-9]+)", options: .caseInsensitive) + private let mobilediffRegexSingle = try! NSRegularExpression(pattern: "^mobilediff/([0-9]+)", options: .caseInsensitive) + private let historyRegex = try! NSRegularExpression(pattern: "^history/(.*)", options: .caseInsensitive) + + internal func destinationForWikiResourceURL(_ url: URL, project: WikimediaProject, loggedInUsername: String?) -> Destination? { + guard let path = url.wikiResourcePath else { + return nil + } + + let language = project.languageCode ?? "en" + let namespaceAndTitle = path.namespaceAndTitleOfWikiResourcePath(with: language) + let namespace = namespaceAndTitle.0 + let title = namespaceAndTitle.1 + + switch namespace { + case .talk: + if FeatureFlags.needsNewTalkPage && project.supportsNativeUserTalkPages { + return .talk(url) + } else { + return nil + } + case .userTalk: + return project.supportsNativeUserTalkPages ? .userTalk(url) : nil + case .special: + + // TODO: Fix to work across languages, not just EN. Fetch special page aliases per site and add to a set of local json files. + // https://en.wikipedia.org/w/api.php?action=query&format=json&meta=siteinfo&formatversion=2&siprop=specialpagealiases + if language.uppercased() == "EN" || language.uppercased() == "TEST", + title == "MyTalk", + let username = loggedInUsername, + let newURL = url.wmf_URL(withTitle: "User_talk:\(username)") { + return .userTalk(newURL) + } + + if language.uppercased() == "EN" || language.uppercased() == "TEST", + title == "MyContributions", + let username = loggedInUsername, + let newURL = url.wmf_URL(withPath: "/wiki/Special:Contributions/\(username)", isMobile: true) { + return .inAppLink(newURL) + } + + if language.uppercased() == "EN" || language.uppercased() == "TEST", + title == "UserLogin" { + return .login + } + + if title == "ReadingLists", + let components = URLComponents(url: url, resolvingAgainstBaseURL: false), + let firstQueryItem = components.queryItems?.first, + firstQueryItem.name == "limport", + let encodedPayload = firstQueryItem.value { + + return .readingListsImport(encodedPayload: encodedPayload) + } + + guard project.supportsNativeDiffPages else { + return nil + } + + if let compareDiffMatch = mobilediffRegexCompare.firstMatch(in: title), + let fromRevID = Int(mobilediffRegexCompare.replacementString(for: compareDiffMatch, in: title, offset: 0, template: "$1")), + let toRevID = Int(mobilediffRegexCompare.replacementString(for: compareDiffMatch, in: title, offset: 0, template: "$2")) { + + return .articleDiffCompare(url, fromRevID: fromRevID, toRevID: toRevID) + } + if let singleDiffMatch = mobilediffRegexSingle.firstReplacementString(in: title), + let toRevID = Int(singleDiffMatch) { + return .articleDiffSingle(url, fromRevID: nil, toRevID: toRevID) + } + + if let articleTitle = historyRegex.firstReplacementString(in: title)?.normalizedPageTitle { + return .articleHistory(url, articleTitle: articleTitle) + } + + return nil + case .main: + + guard project.mainNamespaceGoesToNativeArticleView else { + return nil + } + + return WikipediaURLTranslations.isMainpageTitle(title, in: language) ? nil : Destination.article(url) + case .wikipedia: + + guard project.considersWResourcePathsForRouting else { + return nil + } + + let onThisDayURLSnippet = "On_this_day" + if title.uppercased().contains(onThisDayURLSnippet.uppercased()) { + // URL in form of https://en.wikipedia.org/wiki/Wikipedia:On_this_day/Today?3. Take bit past question mark. + if let selected = url.query { + return .onThisDay(Int(selected)) + } else { + return .onThisDay(nil) + } + } else { + fallthrough + } + default: + return nil + } + } + + internal func destinationForWResourceURL(_ url: URL, project: WikimediaProject) -> Destination? { + + guard project.considersWResourcePathsForRouting, + let path = url.wResourcePath else { + return nil + } + + guard var components = URLComponents(string: path) else { + return nil + } + components.query = url.query + guard components.path.lowercased() == "index.php" else { + return nil + } + guard let queryItems = components.queryItems else { + return nil + } + + var params: [String: String] = [:] + params.reserveCapacity(queryItems.count) + for item in queryItems { + params[item.name] = item.value + } + + if let search = params["search"] { + return .search(url, term: search) + } + + let maybeTitle = params["title"] + let maybeDiff = params["diff"] + let maybeOldID = params["oldid"] + let maybeType = params["type"] + let maybeAction = params["action"] + let maybeDir = params["dir"] + let maybeLimit = params["limit"] + + guard let title = maybeTitle else { + return nil + } + + let language = project.languageCode ?? "en" + + if language.uppercased() == "EN" || language.uppercased() == "TEST", + title == "Special:UserLogin" { + return .login + } + + if maybeLimit != nil, + maybeDir != nil, + let action = maybeAction, + action == "history" { + // TODO: push history 'slice' + return .articleHistory(url, articleTitle: title) + } else if let action = maybeAction, + action == "history" { + return .articleHistory(url, articleTitle: title) + } else if let type = maybeType, + type == "revision", + let diffString = maybeDiff, + let oldIDString = maybeOldID, + let toRevID = Int(diffString), + let fromRevID = Int(oldIDString) { + return .articleDiffCompare(url, fromRevID: fromRevID, toRevID: toRevID) + } else if let diff = maybeDiff, + diff == "prev", + let oldIDString = maybeOldID, + let toRevID = Int(oldIDString) { + return .articleDiffCompare(url, fromRevID: nil, toRevID: toRevID) + } else if let diff = maybeDiff, + diff == "next", + let oldIDString = maybeOldID, + let fromRevID = Int(oldIDString) { + return .articleDiffCompare(url, fromRevID: fromRevID, toRevID: nil) + } else if let oldIDString = maybeOldID, + let toRevID = Int(oldIDString) { + return .articleDiffSingle(url, fromRevID: nil, toRevID: toRevID) + } + + return nil + } + + internal func destinationForHostURL(_ url: URL, project: WikimediaProject, loggedInUsername: String?) -> Destination { + let canonicalURL = url.canonical + + if let wikiResourcePathInfo = destinationForWikiResourceURL(canonicalURL, project: project, loggedInUsername: loggedInUsername) { + return wikiResourcePathInfo + } + + if let wResourcePathInfo = destinationForWResourceURL(canonicalURL, project: project) { + return wResourcePathInfo + } + + return webViewDestinationForHostURL(url) + } + + internal func webViewDestinationForHostURL(_ url: URL) -> Destination { + let canonicalURL = url.canonical + + if configuration.hostCanRouteToInAppWebView(url.host) { + return .inAppLink(canonicalURL) + } else { + return .externalLink(url) + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/SetupButton.swift b/Apps/Wikipedia/WMF Framework/SetupButton.swift new file mode 100644 index 0000000..d429f33 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/SetupButton.swift @@ -0,0 +1,22 @@ +import UIKit + +open class SetupButton: UIButton { + + // Subclassers should override setup instead of any of the initializers. Subclassers must call super.setup() + open func setup() { + } + + // MARK: - Initializers + // Don't override these initializers, use setup() instead + + public override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + +} diff --git a/Apps/Wikipedia/WMF Framework/SetupView.swift b/Apps/Wikipedia/WMF Framework/SetupView.swift new file mode 100644 index 0000000..5cdcc1c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/SetupView.swift @@ -0,0 +1,22 @@ +import UIKit + +open class SetupView: UIView { + + // Subclassers should override setup instead of any of the initializers. Subclassers must call super.setup() + open func setup() { + } + + // MARK: - Initializers + // Don't override these initializers, use setup() instead + + public override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + +} diff --git a/Apps/Wikipedia/WMF Framework/SharedContainerCache.swift b/Apps/Wikipedia/WMF Framework/SharedContainerCache.swift new file mode 100644 index 0000000..9426969 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/SharedContainerCache.swift @@ -0,0 +1,93 @@ +import Foundation + +@objc public class SharedContainerCacheCommonNames: NSObject { + @objc public static let pushNotificationsCache = "Push Notifications Cache" + @objc public static let talkPageCache = "Talk Page Cache" + public static let widgetCache = "Widget Cache" +} + +public final class SharedContainerCache: SharedContainerCacheHousekeepingProtocol { + + private let fileName: String + private let subdirectoryPathComponent: String? + private let defaultCache: () -> T + + public init(fileName: String, subdirectoryPathComponent: String? = nil, defaultCache: @escaping () -> T) { + self.fileName = fileName + self.subdirectoryPathComponent = subdirectoryPathComponent + self.defaultCache = defaultCache + } + + private static var cacheDirectoryContainerURL: URL { + FileManager.default.wmf_containerURL() + } + + private var cacheDataFileURL: URL { + let baseURL = subdirectoryURL() ?? Self.cacheDirectoryContainerURL + return baseURL.appendingPathComponent(fileName).appendingPathExtension("json") + } + + private func subdirectoryURL() -> URL? { + guard let subdirectoryPathComponent = subdirectoryPathComponent else { + return nil + } + return Self.cacheDirectoryContainerURL.appendingPathComponent(subdirectoryPathComponent, isDirectory: true) + } + + public func loadCache() -> T { + if let data = try? Data(contentsOf: cacheDataFileURL), let decodedCache = try? JSONDecoder().decode(T.self, from: data) { + return decodedCache + } + return defaultCache() + } + + public func saveCache(_ cache: T) { + let encoder = JSONEncoder() + guard let encodedCache = try? encoder.encode(cache) else { + return + } + + if let subdirectoryURL = subdirectoryURL() { + try? FileManager.default.createDirectory(at: subdirectoryURL, withIntermediateDirectories: true, attributes: nil) + + } + + try? encodedCache.write(to: cacheDataFileURL) + } + + /// Persist only the last 50 visited talk pages + @objc public static func deleteStaleCachedItems(in subdirectoryPathComponent: String) { + let folderURL = cacheDirectoryContainerURL.appendingPathComponent(subdirectoryPathComponent) + + if let urlArray = try? FileManager.default.contentsOfDirectory(at: folderURL, + includingPropertiesForKeys: [.contentModificationDateKey], + options: .skipsHiddenFiles) { + let maxCacheSize = 50 + if urlArray.count > maxCacheSize { + let sortedArray = urlArray.map { url in + (url, (try? url.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast) + }.sorted(by: {$0.1 > $1.1 }) + .map { $0.0 } + + let itemsToDelete = Array(sortedArray.suffix(from: maxCacheSize)) + for urlItem in itemsToDelete { + try? FileManager.default.removeItem(at: urlItem) + } + } + } + + } +} + +@objc public protocol SharedContainerCacheHousekeepingProtocol: AnyObject { + static func deleteStaleCachedItems(in subdirectoryPathComponent: String) +} + +@objc public class SharedContainerCacheClearFeaturedArticleWrapper: NSObject { + @objc public static func clearOutFeaturedArticleWidgetCache() { + let sharedCache = SharedContainerCache(fileName: SharedContainerCacheCommonNames.widgetCache, defaultCache: { WidgetCache(settings: .default, featuredContent: nil) }) + var updatedCache = sharedCache.loadCache() + updatedCache.featuredContent = nil + sharedCache.saveCache(updatedCache) + } +} diff --git a/Apps/Wikipedia/WMF Framework/Significant Events Endpoint/ArticleAsLivingDocViewModels.swift b/Apps/Wikipedia/WMF Framework/Significant Events Endpoint/ArticleAsLivingDocViewModels.swift new file mode 100644 index 0000000..e19865f --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Significant Events Endpoint/ArticleAsLivingDocViewModels.swift @@ -0,0 +1,1614 @@ +import Foundation + +// tonitodo: It makes more sense for this to live in the app. Can we move out of WMF? + +public struct ArticleAsLivingDocViewModel { + + public let nextRvStartId: UInt? + public let sha: String? + public let sections: [SectionHeader] + public let articleInsertHtmlSnippets: [String] + public let lastUpdatedTimestamp: String? + public let summaryText: String? + + private let isoDateFormatter = ISO8601DateFormatter() + + public init(nextRvStartId: UInt?, sha: String?, sections: [SectionHeader], summaryText: String?, articleInsertHtmlSnippets: [String], lastUpdatedTimestamp: String?) { + self.nextRvStartId = nextRvStartId + self.sha = sha + self.sections = sections + self.summaryText = summaryText + self.articleInsertHtmlSnippets = articleInsertHtmlSnippets + self.lastUpdatedTimestamp = lastUpdatedTimestamp + } + + public init?(significantEvents: SignificantEvents, traitCollection: UITraitCollection, theme: Theme) { + guard let dayMonthNumberYearDateFormatter = DateFormatter.wmf_monthNameDayOfMonthNumberYear() else { + assertionFailure("Unable to generate date formatters for Significant Events View Models") + return nil + } + + self.nextRvStartId = significantEvents.nextRvStartId + self.sha = significantEvents.sha + + // initialize summary text + var summaryText: String? = nil + if let earliestDate = isoDateFormatter.date(from: significantEvents.summary.earliestTimestampString) { + + let currentDate = Date() + let calendar = NSCalendar.current + let unitFlags:Set = [.day] + let components = calendar.dateComponents(unitFlags, from: earliestDate, to: currentDate) + if let numberOfDays = components.day { + summaryText = String.localizedStringWithFormat(CommonStrings.articleAsLivingDocSummaryTitle, + significantEvents.summary.numChanges, + significantEvents.summary.numUsers, + numberOfDays) + } + } + self.summaryText = summaryText + + // loop through typed events, turn into view models and segment off into sections + var currentSectionEvents: [TypedEvent] = [] + var sections: [SectionHeader] = [] + + var maybeCurrentTimestamp: Date? + var maybePreviousTimestamp: Date? + + for originalEvent in significantEvents.typedEvents { + + var maybeEvent: TypedEvent? = nil + if let smallEventViewModel = Event.Small(typedEvents: [originalEvent]) { + maybeEvent = .small(smallEventViewModel) + } else if let largeEventViewModel = Event.Large(typedEvent: originalEvent) { + + // this is just an optimization to have collection view height calculations sooner so it doesn't happen while the user is scrolling + largeEventViewModel.calculateSideScrollingCollectionViewHeightForTraitCollection(traitCollection, theme: theme) + maybeEvent = .large(largeEventViewModel) + } + + guard let event = maybeEvent else { + assertionFailure("Unable to instantiate event view model, skipping event") + continue + } + + switch originalEvent { + case .large(let largeChange): + maybeCurrentTimestamp = isoDateFormatter.date(from: largeChange.timestampString) + case .newTalkPageTopic(let newTalkPageTopic): + maybeCurrentTimestamp = isoDateFormatter.date(from: newTalkPageTopic.timestampString) + case .vandalismRevert(let vandalismRevert): + maybeCurrentTimestamp = isoDateFormatter.date(from: vandalismRevert.timestampString) + case .small(let smallChange): + maybeCurrentTimestamp = isoDateFormatter.date(from: smallChange.timestampString) + } + + guard let currentTimestamp = maybeCurrentTimestamp else { + assertionFailure("Significant Events - Unable to determine event timestamp, skipping event.") + continue + } + + if let previousTimestamp = maybePreviousTimestamp { + let calendar = NSCalendar.current + if !calendar.isDate(previousTimestamp, inSameDayAs: currentTimestamp) { + // multiple days have passed since last event, package up current sections into new section + let section = SectionHeader(timestamp: previousTimestamp, typedEvents: currentSectionEvents, subtitleDateFormatter: dayMonthNumberYearDateFormatter) + sections.append(section) + currentSectionEvents.removeAll() + currentSectionEvents.append(event) + maybePreviousTimestamp = currentTimestamp + } else { + currentSectionEvents.append(event) + maybePreviousTimestamp = currentTimestamp + } + } else { + currentSectionEvents.append(event) + maybePreviousTimestamp = currentTimestamp + } + } + + // capture any final currentSectionEvents into new section + if let currentTimestamp = maybeCurrentTimestamp { + let section = SectionHeader(timestamp: currentTimestamp, typedEvents: currentSectionEvents, subtitleDateFormatter: dayMonthNumberYearDateFormatter) + sections.append(section) + currentSectionEvents.removeAll() + } + + // collapse sibling small event view models + var finalSections: [SectionHeader] = [] + for section in sections { + var collapsedEventViewModels: [TypedEvent] = [] + var currentSmallChanges: [SignificantEvents.Event.Small] = [] + for event in section.typedEvents { + switch event { + case .small(let smallEventViewModel): + currentSmallChanges.append(contentsOf: smallEventViewModel.smallChanges) + default: + if currentSmallChanges.count > 0 { + + collapsedEventViewModels.append(.small(Event.Small(smallChanges: currentSmallChanges))) + currentSmallChanges.removeAll() + } + collapsedEventViewModels.append(event) + continue + } + } + + // add any final small changes + if currentSmallChanges.count > 0 { + collapsedEventViewModels.append(.small(Event.Small(smallChanges: currentSmallChanges))) + currentSmallChanges.removeAll() + } + + let collapsedSection = SectionHeader(timestamp: section.timestamp, typedEvents: collapsedEventViewModels, subtitleDateFormatter: dayMonthNumberYearDateFormatter) + finalSections.append(collapsedSection) + } + + finalSections = ArticleAsLivingDocViewModel.collapseSmallEvents(from: finalSections) + self.sections = finalSections + + // grab first 3 large event html snippets + var articleInsertHtmlSnippets: [String] = [] + var lastUpdatedTimestamp: String? + let htmlSnippetCountMax = 3 + + outerLoop: for (sectionIndex, section) in finalSections.enumerated() { + for (itemIndex, event) in section.typedEvents.enumerated() { + switch event { + case .small(let smallEvent): + if lastUpdatedTimestamp == nil { + lastUpdatedTimestamp = smallEvent.timestampForDisplay() + } + case .large(let largeEvent): + if lastUpdatedTimestamp == nil { + lastUpdatedTimestamp = largeEvent.fullyRelativeTimestampForDisplay() + } + let indexPath = IndexPath(item: itemIndex, section: sectionIndex) + if let htmlSnippet = largeEvent.articleInsertHtmlSnippet(isFirst: articleInsertHtmlSnippets.count == 0, isLast: articleInsertHtmlSnippets.count == htmlSnippetCountMax - 1, indexPath: indexPath) { + if articleInsertHtmlSnippets.count < htmlSnippetCountMax { + articleInsertHtmlSnippets.append(htmlSnippet) + } else { + if lastUpdatedTimestamp != nil { + break outerLoop + } + } + } + } + } + } + + self.articleInsertHtmlSnippets = articleInsertHtmlSnippets + self.lastUpdatedTimestamp = lastUpdatedTimestamp + } + + /// Collapses sequential sections that contain only small events into one section, including a date range that represents the collected events + static func collapseSmallEvents(from sections: [SectionHeader]) -> [SectionHeader] { + guard let dayMonthNumberYearDateFormatter = DateFormatter.wmf_monthNameDayOfMonthNumberYear(), let isoDateFormatter = DateFormatter.wmf_iso8601() else { + return sections + } + + let enumeratedSections = sections.enumerated() + var mutatedSections: [SectionHeader] = [] + var rangesToCollapse: [ClosedRange] = [] + + for (outerIndex, outerSection) in enumeratedSections { + let startIndex = outerIndex + var endIndex = outerIndex + + if outerSection.containsOnlySmallEvents { + for (innerIndex, innerSection) in enumeratedSections { + guard innerIndex >= startIndex + 1, !rangesToCollapse.contains(where: {$0.contains(innerIndex) }) else { + continue + } + + if innerSection.containsOnlySmallEvents { + endIndex = innerIndex + } else { + break + } + } + } + + if startIndex != endIndex { + // This range is eligible to be collapsed + rangesToCollapse.append(startIndex...endIndex) + } + } + + var typedEvents: [TypedEvent] = [] + + typealias CollapsedSection = (section: SectionHeader, sectionHashes: [Int]) + + var collapsedSections: [CollapsedSection] = [] + var collapsedSectionHashes: [Int] = [] + + // Create new sections for each collapsed range + for range in rangesToCollapse { + for sectionElement in sections[range] { + typedEvents.append(contentsOf: sectionElement.typedEvents) + } + + collapsedSectionHashes.append(contentsOf: sections[range].compactMap { $0.hashValue }) + + if let startIndex = range.first { + let smallChanges = typedEvents.flatMap { $0.smallChanges } + let collapsedSmallEvent = Event.Small(smallChanges: smallChanges) + let smallTypedEvent = TypedEvent.small(collapsedSmallEvent) + + let smallChangeDates = smallChanges.compactMap { isoDateFormatter.date(from: $0.timestampString) } + var dateRange: DateInterval? + if let minDate = smallChangeDates.min(), let maxDate = smallChangeDates.max() { + dateRange = DateInterval(start: minDate, end: maxDate) + } + + let section = SectionHeader(timestamp: sections[startIndex].timestamp, typedEvents: [smallTypedEvent], subtitleDateFormatter: dayMonthNumberYearDateFormatter, dateRange: dateRange) + collapsedSections.append((section, collapsedSectionHashes)) + } + + typedEvents = [] + collapsedSectionHashes = [] + } + + var newlyCollapsedSectionHashes: [Int] = [] + + // Returns the small event collapsed section that represents the `sectionHash`, if one exists + func firstCollapsedSectionContaining(sectionHash: Int) -> CollapsedSection? { + return collapsedSections + .first { collapsedSection in collapsedSection.sectionHashes.contains(sectionHash) } + } + + // Reconstruct sections with newly eligible small event sections collapsed in proper order + for section in sections { + if let collapsedSection = firstCollapsedSectionContaining(sectionHash: section.hashValue), !newlyCollapsedSectionHashes.contains(section.hashValue) { + mutatedSections.append(collapsedSection.section) + newlyCollapsedSectionHashes.append(contentsOf: collapsedSection.sectionHashes) + } + + if !newlyCollapsedSectionHashes.contains(section.hashValue) { + mutatedSections.append(section) + } + } + + return mutatedSections + } + + static func eventDisplayTimestamp(timestampString: String) -> String? { + let isoDateFormatter = ISO8601DateFormatter() + + guard + let shortFormatter = DateFormatter.wmf_24hshortTimeWithUTCTimeZone(), + let date = isoDateFormatter.date(from: timestampString) else { + return nil + } + + let calendar = NSCalendar.current + let components = calendar.dateComponents([.hour, .minute], from: date, to: Date()) + + if let hours = components.hour, let minutes = components.minute { + switch hours { + case ..<1: + return String.localizedStringWithFormat(WMFLocalizedDateFormatStrings.minutesAgo(), minutes) + case ..<24: + return String.localizedStringWithFormat(WMFLocalizedDateFormatStrings.hoursAgo(), hours) + default: + break + } + } + + return shortFormatter.string(from: date) + } + + static func displayTimestamp(timestampString: String, fullyRelative: Bool) -> String? { + if let isoDateFormatter = DateFormatter.wmf_iso8601(), + let timeDateFormatter = DateFormatter.wmf_24hshortTimeWithUTCTimeZone(), + let date = isoDateFormatter.date(from: timestampString) { + if fullyRelative { + let relativeTime = (date as NSDate).wmf_fullyLocalizedRelativeDateStringFromLocalDateToNow() + return relativeTime + } else { + let calendar = NSCalendar.current + let unitFlags:Set = [.day] + let components = calendar.dateComponents(unitFlags, from: date, to: Date()) + if let numberOfDays = components.day { + switch numberOfDays { + case 0: + let relativeTime = (date as NSDate).wmf_fullyLocalizedRelativeDateStringFromLocalDateToNow() + return relativeTime + default: + let shortTime = timeDateFormatter.string(from: date) + return shortTime + } + } + } + } + + return nil + } +} + +// MARK: SectionHeader + +public extension ArticleAsLivingDocViewModel { + + class SectionHeader: Hashable { + public let title: String + public let subtitleTimestampDisplay: String + public let timestamp: Date + public let dateRange: DateInterval? + public var typedEvents: [TypedEvent] + + private let sectionTimestampIdentifier: String + + private static let calendar: Calendar = Calendar.current + + private static let relativeDateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.doesRelativeDateFormatting = true + formatter.timeStyle = .none + formatter.dateStyle = .short + return formatter + }() + + private static let dayMonthYearFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.setLocalizedDateFormatFromTemplate("MMMM d, yyyy") + return formatter + }() + + private static let dayMonthFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.setLocalizedDateFormatFromTemplate("MMMM d") + return formatter + }() + + public init(timestamp: Date, typedEvents: [TypedEvent], subtitleDateFormatter: DateFormatter, dateRange: DateInterval? = nil) { + + // If today or yesterday, show friendly localized relative format ("Today", "Yesterday") + func relativelyFormat(date: Date) -> String { + let nsTimestamp = timestamp as NSDate + if SectionHeader.calendar.isDateInToday(date) || SectionHeader.calendar.isDateInYesterday(date) { + return SectionHeader.relativeDateFormatter.string(from: date) + } else { + return nsTimestamp.wmf_fullyLocalizedRelativeDateStringFromLocalDateToNow() + } + } + + self.title = relativelyFormat(date: timestamp) + + if let dateRange = dateRange { + var dateRangeStrings: [String] = [] + + let startDate = dateRange.start + let endDate = dateRange.end + + if SectionHeader.calendar.isDateInToday(endDate) || SectionHeader.calendar.isDateInYesterday(endDate) { + let endString = relativelyFormat(date: endDate) + let startString = SectionHeader.dayMonthYearFormatter.string(from: startDate) + dateRangeStrings.append(contentsOf: [endString, startString]) + } else { + let endString = SectionHeader.dayMonthFormatter.string(from: endDate) + let startString = SectionHeader.dayMonthYearFormatter.string(from: startDate) + dateRangeStrings.append(contentsOf: [endString, startString]) + } + + self.subtitleTimestampDisplay = dateRangeStrings.joined(separator: " - ") + } else { + self.subtitleTimestampDisplay = SectionHeader.dayMonthYearFormatter.string(from: timestamp) + } + + self.dateRange = dateRange + self.timestamp = timestamp + self.typedEvents = typedEvents + self.sectionTimestampIdentifier = subtitleDateFormatter.string(from: timestamp) + } + + public static func == (lhs: ArticleAsLivingDocViewModel.SectionHeader, rhs: ArticleAsLivingDocViewModel.SectionHeader) -> Bool { + return lhs.sectionTimestampIdentifier == rhs.sectionTimestampIdentifier + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(sectionTimestampIdentifier) + hasher.combine(typedEvents) + } + + // MARK: - Helpers + + public var containsOnlySmallEvents: Bool { + typedEvents.count == 1 && typedEvents.allSatisfy { event in event.isSmall } + } + + } +} + +// MARK: Events + +public extension ArticleAsLivingDocViewModel { + + enum TypedEvent: Hashable { + case small(Event.Small) + case large(Event.Large) + + public static func == (lhs: ArticleAsLivingDocViewModel.TypedEvent, rhs: ArticleAsLivingDocViewModel.TypedEvent) -> Bool { + switch lhs { + case .large(let leftLargeEvent): + switch rhs { + case .large(let rightLargeEvent): + return leftLargeEvent == rightLargeEvent + default: + return false + } + case .small(let leftSmallEvent): + switch rhs { + case .small(let rightSmallEvent): + return leftSmallEvent == rightSmallEvent + default: + return false + } + + } + } + + public func hash(into hasher: inout Hasher) { + switch self { + case .small(let smallEvent): + smallEvent.smallChanges.forEach { hasher.combine($0.revId) } + case .large(let largeEvent): + hasher.combine(largeEvent.revId) + hasher.combine(largeEvent.wereThanksSent) + } + } + + // MARK: - Helpers + + public var isSmall: Bool { + switch self { + case .small: + return true + default: + return false + } + } + + public var smallChanges: [SignificantEvents.Event.Small] { + switch self { + case .small(let small): + return small.smallChanges + default: + return [] + } + } + } +} + +public extension ArticleAsLivingDocViewModel { + + struct Event { + + public class Small: Equatable { + + private var lastTraitCollection: UITraitCollection? + private var lastTheme: Theme? + public lazy var eventDescription = { + return String.localizedStringWithFormat( + CommonStrings.smallChangeDescription, + smallChanges.count) + }() + public let smallChanges: [SignificantEvents.Event.Small] + public var loggingPosition = 0 + + public init?(typedEvents: [SignificantEvents.TypedEvent]) { + var smallChanges: [SignificantEvents.Event.Small] = [] + for event in typedEvents { + switch event { + case .small(let smallChange): + smallChanges.append(smallChange) + default: + return nil + } + } + + guard smallChanges.count > 0 else { + return nil + } + + self.smallChanges = smallChanges + } + + public init(smallChanges: [SignificantEvents.Event.Small]) { + self.smallChanges = smallChanges + } + + public static func == (lhs: ArticleAsLivingDocViewModel.Event.Small, rhs: ArticleAsLivingDocViewModel.Event.Small) -> Bool { + return lhs.smallChanges == rhs.smallChanges + } + + // Only used in the html portion of the feature + func timestampForDisplay() -> String? { + + guard let timestampString = smallChanges.first?.timestampString else { + return nil + } + + let displayTimestamp = ArticleAsLivingDocViewModel.displayTimestamp(timestampString: timestampString, fullyRelative: true) + + return displayTimestamp + } + } + + public class Large: Equatable { + + public enum ChangeDetail { + case snippet(Snippet) // use for a basic horizontally scrolling snippet cell (will contain talk page topic snippets, added text snippets, article description updated snippets) + case reference(Reference) + } + + public struct Snippet { + public let description: NSAttributedString + } + + public struct Reference { + public let type: String + public let description: NSAttributedString + public let accessDateYearDisplay: String? + + init?(type: String, description: NSAttributedString?, accessDateYearDisplay: String?) { + guard let description = description else { + return nil + } + + self.type = type + self.description = description + self.accessDateYearDisplay = accessDateYearDisplay + } + } + + public enum UserType { + case standard + case anonymous + case bot + } + + public enum ButtonsToDisplay { + case thankAndViewChanges(userId: UInt, revisionId: UInt) + case viewDiscussion(sectionName: String?) + } + + public let typedEvent: SignificantEvents.TypedEvent + + private var lastTraitCollection: UITraitCollection? + private var lastTheme: Theme? + + private(set) var eventDescription: NSAttributedString? + private(set) var sideScrollingCollectionViewHeight: CGFloat? + private(set) var changeDetails: [ChangeDetail]? + private(set) var displayTimestamp: String? + private(set) var userInfo: NSAttributedString? + let userId: UInt + public let userType: UserType + public let buttonsToDisplay: ButtonsToDisplay + public let revId: UInt + public let parentId: UInt + public var wereThanksSent = false + public var loggingPosition = 0 + + init?(typedEvent: SignificantEvents.TypedEvent) { + + let userGroups: [String]? + switch typedEvent { + case .newTalkPageTopic(let newTalkPageTopic): + self.userId = newTalkPageTopic.userId + userGroups = newTalkPageTopic.userGroups + + if let talkPageSection = newTalkPageTopic.section { + self.buttonsToDisplay = .viewDiscussion(sectionName: Self.sectionTitleWithWikitextAndHtmlStripped(originalTitle: talkPageSection)) + } else { + self.buttonsToDisplay = .viewDiscussion(sectionName: nil) + } + + case .large(let largeChange): + self.userId = largeChange.userId + userGroups = largeChange.userGroups + self.buttonsToDisplay = .thankAndViewChanges(userId: largeChange.userId, revisionId: largeChange.revId) + case .vandalismRevert(let vandalismRevert): + self.userId = vandalismRevert.userId + userGroups = vandalismRevert.userGroups + self.buttonsToDisplay = .thankAndViewChanges(userId: vandalismRevert.userId, revisionId: vandalismRevert.revId) + case .small: + return nil + } + + if let userGroups = userGroups, + userGroups.contains("bot") { + userType = .bot + } else if self.userId == 0 { + userType = .anonymous + } else { + userType = .standard + } + + self.typedEvent = typedEvent + switch typedEvent { + case .large(let largeChange): + revId = largeChange.revId + parentId = largeChange.parentId + case .newTalkPageTopic(let newTalkPageTopic): + revId = newTalkPageTopic.revId + parentId = newTalkPageTopic.parentId + case .vandalismRevert(let vandalismRevert): + revId = vandalismRevert.revId + parentId = vandalismRevert.parentId + default: + assertionFailure("Shouldn't happen") + revId = 0 + parentId = 0 + } + } + + public static func == (lhs: ArticleAsLivingDocViewModel.Event.Large, rhs: ArticleAsLivingDocViewModel.Event.Large) -> Bool { + return lhs.revId == rhs.revId && lhs.wereThanksSent == rhs.wereThanksSent + } + + } + + } +} + +// MARK: Large Event Type Helper methods + +public extension ArticleAsLivingDocViewModel.Event.Large { + + func articleInsertHtmlSnippet(isFirst: Bool = false, isLast: Bool = false, indexPath: IndexPath) -> String? { + guard let timestampForDisplay = self.fullyRelativeTimestampForDisplay(), + let userInfo = userInfoHtmlSnippet() else { + return nil + } + + let liElementIdName = isFirst ? "significant-changes-first-list" : isLast ? "significant-changes-last-list" : "significant-changes-list" + + let lastUserInfoIdAdditions = isLast ? " id='significant-changes-userInfo-last'" : "" + + return "
  • \(timestampForDisplay)

    \(eventDescription ?? NSAttributedString(string: ""))

    \(userInfo)

  • " + } + + private var htmlSignificantEventsLinkEndingTag: String { + return "" + } + + // if trait collection or theme is different from the last time attributed strings were generated, + // reset to nil to trigger generation again the next time it's requested + func resetAttributedStringsIfNeededWithTraitCollection(_ traitCollection: UITraitCollection, theme: Theme) { + if let lastTraitCollection = lastTraitCollection, + let lastTheme = lastTheme, + lastTraitCollection != traitCollection || lastTheme != theme { + eventDescription = nil + sideScrollingCollectionViewHeight = nil + changeDetails = nil + userInfo = nil + Self.heightForThreeLineSnippet = nil + Self.heightForReferenceTitle = nil + } + + lastTraitCollection = traitCollection + lastTheme = theme + } + + struct IndividualDescription { + let priority: Int // used for sorting + let text: String + } + + private func individualDescriptionsForTypedChanges(_ typedChanges: [SignificantEvents.TypedChange]) -> [IndividualDescription] { + + var descriptions: [IndividualDescription] = [] + var numReferences = 0 + for typedChange in typedChanges { + switch typedChange { + case .addedText(let addedText): + let countNumber = NSNumber(value: addedText.characterCount) + let characterCount = "\(NumberFormatter.localizedThousandsStringFromNumber(countNumber).localizedLowercase) " + String.localizedStringWithFormat(CommonStrings.charactersTextDescription, addedText.characterCount) + let description = String.localizedStringWithFormat(CommonStrings.addedTextDescription, characterCount) + descriptions.append(IndividualDescription(priority: 1, text: description)) + case .deletedText(let deletedText): + let countNumber = NSNumber(value: deletedText.characterCount) + let characterCount = "\(NumberFormatter.localizedThousandsStringFromNumber(countNumber).localizedLowercase) " + String.localizedStringWithFormat(CommonStrings.charactersTextDescription, deletedText.characterCount) + let description = String.localizedStringWithFormat(CommonStrings.deletedTextDescription, characterCount) + descriptions.append(IndividualDescription(priority: 2, text: description)) + case .newTemplate(let newTemplate): + var numArticleDescriptions = 0 + for template in newTemplate.typedTemplates { + switch template { + case .articleDescription: + numArticleDescriptions += 1 + case .bookCitation, + .journalCitation, + .newsCitation, + .websiteCitation: + numReferences += 1 + } + } + + if numArticleDescriptions > 0 { + let description = CommonStrings.articleDescriptionUpdatedDescription + descriptions.append(IndividualDescription(priority: 3, text: description)) + } + } + } + + if descriptions.count == 0 { + switch numReferences { + case 0: + break + case 1: + let description = CommonStrings.singleReferenceAddedDescription + descriptions.append(IndividualDescription(priority: 0, text: description)) + default: + let description = CommonStrings.multipleReferencesAddedDescription + descriptions.append(IndividualDescription(priority: 0, text: description)) + } + } else { + if numReferences > 0 { + let description = String.localizedStringWithFormat(CommonStrings.numericalMultipleReferencesAddedDescription, numReferences) + descriptions.append(IndividualDescription(priority: 0, text: description)) + } + } + + return descriptions + } + + private func sectionsSet() -> Set { + let set: Set + switch typedEvent { + case .newTalkPageTopic: + set = Set() + case .vandalismRevert(let vandalismRevert): + set = Set(vandalismRevert.sections) + case .large(let largeChange): + var sections: [String] = [] + for typedChange in largeChange.typedChanges { + switch typedChange { + case .addedText(let addedTextChange): + sections.append(contentsOf: addedTextChange.sections) + case .deletedText(let deletedTextChange): + sections.append(contentsOf: deletedTextChange.sections) + case .newTemplate(let newTemplate): + sections.append(contentsOf: newTemplate.sections) + } + } + + set = Set(sections) + case .small: + assertionFailure("This shouldn't happen") + set = Set() + } + + // strip == signs from all section titles + let finalSet = set.map { Self.sectionTitleWithWikitextAndHtmlStripped(originalTitle: $0) } + + return Set(finalSet) + } + + // remove one or more equal signs and zero or more spaces on either side of the title text + // also removing html for display and potential javascript injection issues - https://phabricator.wikimedia.org/T268201 + private static func sectionTitleWithWikitextAndHtmlStripped(originalTitle: String) -> String { + var loopTitle = originalTitle.removingHTML + + let regex = "^=+\\s*|\\s*=+$" + var maybeMatch = loopTitle.range(of: regex, options: .regularExpression) + while let match = maybeMatch { + loopTitle.removeSubrange(match) + maybeMatch = loopTitle.range(of: regex, options: .regularExpression) + } + + return loopTitle + } + + private func localizedStringFromSections(sections: [String]) -> String? { + var localizedString: String + switch sections.count { + case 0: + return nil + case 1: + let firstSection = sections[0] + localizedString = String.localizedStringWithFormat(CommonStrings.oneSectionDescription, firstSection) + case 2: + let firstSection = sections[0] + let secondSection = sections[1] + localizedString = String.localizedStringWithFormat(CommonStrings.twoSectionsDescription, firstSection, secondSection) + default: + localizedString = String.localizedStringWithFormat(CommonStrings.manySectionsDescription, sections.count) + } + + return " " + localizedString + } + + private func localizedSectionHtmlSnippet(sectionsSet: Set) -> String? { + + let sections = Array(sectionsSet) + guard let localizedString = localizedStringFromSections(sections: sections) else { + return nil + } + + let mutableLocalizedString = NSMutableString(string: localizedString) + + var ranges: [NSRange] = [] + for section in sections { + let rangeOfSection = (localizedString as NSString).range(of: section) + let rangeValid = rangeOfSection.location != NSNotFound && rangeOfSection.location + rangeOfSection.length <= localizedString.count + if rangeValid { + ranges.append(rangeOfSection) + } + } + + var offset = 0 + for range in ranges { + let italicStart = "" + let italicEnd = "" + mutableLocalizedString.insert(italicStart, at: range.location + offset) + offset += italicStart.count + mutableLocalizedString.insert(italicEnd, at: range.location + range.length + offset) + offset += italicEnd.count + } + + if let returnString = mutableLocalizedString.copy() as? NSString { + return returnString as String + } else { + assertionFailure("This shouldn't happen") + return nil + } + } + + private func localizedSectionAttributedString(sectionsSet: Set, traitCollection: UITraitCollection, theme: Theme) -> NSAttributedString? { + let sections = Array(sectionsSet) + guard let localizedString = localizedStringFromSections(sections: sections) else { + return nil + } + + let font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + let italicFont = UIFont.wmf_font(.italicBody, compatibleWithTraitCollection: traitCollection) + let attributes = [NSAttributedString.Key.font: font, + NSAttributedString.Key.foregroundColor: theme.colors.primaryText] + + var ranges: [NSRange] = [] + for section in sections { + let rangeOfSection = (localizedString as NSString).range(of: section) + let rangeValid = rangeOfSection.location != NSNotFound && rangeOfSection.location + rangeOfSection.length <= localizedString.count + if rangeValid { + ranges.append(rangeOfSection) + } + } + + let mutableAttributedString = NSMutableAttributedString(string: localizedString, attributes: attributes) + + for range in ranges { + mutableAttributedString.addAttribute(NSAttributedString.Key.font, value: italicFont, range: range) + } + + guard let attributedString = mutableAttributedString.copy() as? NSAttributedString else { + assertionFailure("This shouldn't happen") + return NSAttributedString(string: localizedString, attributes: attributes) + } + + return attributedString + } + + static let sideScrollingCellPadding = UIEdgeInsets(top: 17, left: 15, bottom: 17, right: 15) + static let sideScrollingCellWidth: CGFloat = 250 + static var availableSideScrollingCellWidth: CGFloat = { + return sideScrollingCellWidth - sideScrollingCellPadding.left - sideScrollingCellPadding.right + }() + + private static let changeDetailDescriptionTextStyle = DynamicTextStyle.subheadline + private static let changeDetailDescriptionTextStyleItalic = DynamicTextStyle.italicSubheadline + private static let changeDetailDescriptionFontWeight = UIFont.Weight.regular + + static let changeDetailReferenceTitleStyle = DynamicTextStyle.semiboldSubheadline + static let changeDetailReferenceTitleDescriptionSpacing: CGFloat = 13 + static let additionalPointsForShadow: CGFloat = 16 + + @discardableResult func calculateSideScrollingCollectionViewHeightForTraitCollection(_ traitCollection: UITraitCollection, theme: Theme) -> CGFloat { + + if let sideScrollingCollectionViewHeight = sideScrollingCollectionViewHeight { + return sideScrollingCollectionViewHeight + } + + let changeDetails = changeDetailsForTraitCollection(traitCollection, theme: theme) + + let tallestSnippetContentHeight: CGFloat = calculateTallestSnippetContentHeightInChangeDetails(changeDetails: changeDetails) + let tallestReferenceChangeDetailHeight: CGFloat = calculateTallestReferenceContentHeightInChangeDetails(changeDetails: changeDetails, traitCollection: traitCollection, theme: theme) + + let maxContentHeight = maxContentHeightFromTallestSnippetContentHeight(tallestSnippetContentHeight: tallestSnippetContentHeight, tallestReferenceContentHeight: tallestReferenceChangeDetailHeight, traitCollection: traitCollection, theme: theme) + + let finalHeight = maxContentHeight == 0 ? 0 : maxContentHeight + Self.sideScrollingCellPadding.top + Self.sideScrollingCellPadding.bottom + Self.additionalPointsForShadow + self.sideScrollingCollectionViewHeight = finalHeight + return finalHeight + } + + func changeDetailsForTraitCollection(_ traitCollection: UITraitCollection, theme: Theme) -> [ChangeDetail] { + if let changeDetails = changeDetails { + return changeDetails + } + + var changeDetails: [ChangeDetail] = [] + + switch typedEvent { + case .newTalkPageTopic(let newTalkPageTopic): + let attributedString = newTalkPageTopic.snippet.byAttributingHTML(with: Self.changeDetailDescriptionTextStyle, boldWeight: Self.changeDetailDescriptionFontWeight, matching: traitCollection, color: theme.colors.primaryText, linkColor: theme.colors.link, handlingLists: true, handlingSuperSubscripts: true) + let changeDetail = ChangeDetail.snippet(Snippet(description: attributedString)) + changeDetails.append(changeDetail) + case .large(let largeChange): + for typedChange in largeChange.typedChanges { + switch typedChange { + case .addedText(let addedText): + // TODO: Add highlighting here. For snippetType 1, add a highlighting attribute across the whole string. Otherwise, seek out highlight-add span ranges and add those attributes + guard let snippet = addedText.snippet else { + continue + } + + let attributedString = snippet.byAttributingHTML(with: Self.changeDetailDescriptionTextStyle, boldWeight: Self.changeDetailDescriptionFontWeight, matching: traitCollection, color: theme.colors.primaryText, handlingLinks: true, linkColor: theme.colors.link, handlingLists: true, handlingSuperSubscripts: true) + let changeDetail = ChangeDetail.snippet(Snippet(description: attributedString)) + changeDetails.append(changeDetail) + case .deletedText: + continue + case .newTemplate(let newTemplate): + for template in newTemplate.typedTemplates { + + switch template { + case .articleDescription(let articleDescription): + let font = UIFont.wmf_font(Self.changeDetailDescriptionTextStyle, compatibleWithTraitCollection: traitCollection) + let attributes = [NSAttributedString.Key.font: font, + NSAttributedString.Key.foregroundColor: + theme.colors.primaryText] + let attributedString = NSAttributedString(string: articleDescription.text, attributes: attributes) + let changeDetail = ChangeDetail.snippet(Snippet(description: attributedString)) + changeDetails.append(changeDetail) + // tonitodo: these code blocks are all very similar. make a generic method instead? + case .bookCitation(let bookCitation): + let typeText = referenceTypeForTemplate(template, traitCollection: traitCollection, theme: theme) + let accessYear = accessDateYearForTemplate(template, traitCollection: traitCollection, theme: theme) + let bookCitationDescription = descriptionForBookCitation(bookCitation, traitCollection: traitCollection, theme: theme) + if let reference = Reference(type: typeText, description: bookCitationDescription, accessDateYearDisplay: accessYear) { + let changeDetail = ChangeDetail.reference(reference) + changeDetails.append(changeDetail) + } + case .journalCitation(let journalCitation): + let typeText = referenceTypeForTemplate(template, traitCollection: traitCollection, theme: theme) + let accessYear = accessDateYearForTemplate(template, traitCollection: traitCollection, theme: theme) + let citationDescription = descriptionForJournalCitation(journalCitation, traitCollection: traitCollection, theme: theme) + if let reference = Reference(type: typeText, description: citationDescription, accessDateYearDisplay: accessYear) { + let changeDetail = ChangeDetail.reference(reference) + changeDetails.append(changeDetail) + } + case .newsCitation(let newsCitation): + let typeText = referenceTypeForTemplate(template, traitCollection: traitCollection, theme: theme) + let accessYear = accessDateYearForTemplate(template, traitCollection: traitCollection, theme: theme) + let citationDescription = descriptionForNewsCitation(newsCitation, traitCollection: traitCollection, theme: theme) + if let reference = Reference(type: typeText, description: citationDescription, accessDateYearDisplay: accessYear) { + let changeDetail = ChangeDetail.reference(reference) + changeDetails.append(changeDetail) + } + case .websiteCitation(let websiteCitation): + let typeText = referenceTypeForTemplate(template, traitCollection: traitCollection, theme: theme) + let accessYear = accessDateYearForTemplate(template, traitCollection: traitCollection, theme: theme) + let citationDescription = descriptionForWebsiteCitation(websiteCitation, traitCollection: traitCollection, theme: theme) + if let reference = Reference(type: typeText, description: citationDescription, accessDateYearDisplay: accessYear) { + let changeDetail = ChangeDetail.reference(reference) + changeDetails.append(changeDetail) + } + } + } + } + } + case .vandalismRevert: + return [] + case .small: + assertionFailure("This should not happen") + return [] + } + + self.changeDetails = changeDetails + return changeDetails + } + + // Note: heightForThreeLineSnippet and heightForReferenceTitle methods are placeholder calculations when determining a side scrolling cell's content height. + // When there are no reference cells, we are capping off article content snippet cells at 3 lines. If there are reference cells, snippet cells are allowed to show lines to the full height of the tallest reference cell. + // Reference cells titles are only ever 1 line, so we are using placeholder text to calculate that rather than going up against actual view model title values, since the height will be the same regardless of the size of the title value + // heightForThreeLine and heightForReferenceTitle only ever needs to be calculated once per traitCollection's preferredContentSize, so we are optimizing in the similar way that ArticleAsLivingDocViewModel's various NSAttributedStrings are optimized, i.e. calculate once, then reset when the traitCollection changes via resetAttributedStringsIfNeededWithTraitCollection. + private static var heightForThreeLineSnippet: CGFloat? + private static func heightForThreeLineSnippetForTraitCollection(_ traitCollection: UITraitCollection, theme: Theme) -> CGFloat { + + if let heightForThreeLineSnippet = heightForThreeLineSnippet { + return heightForThreeLineSnippet + } + + let snippetFont = UIFont.wmf_font(Self.changeDetailDescriptionTextStyle, compatibleWithTraitCollection: traitCollection) + let snippetAttributes = [NSAttributedString.Key.font: snippetFont] + let threeLineSnippetText = """ + 1 + 2 + 3 + """ + let threeLineSnippetAttString = NSAttributedString(string: threeLineSnippetText, attributes: snippetAttributes) + + let finalHeight = ceil(threeLineSnippetAttString.boundingRect(with: CGSize(width: Self.availableSideScrollingCellWidth, height: CGFloat.infinity), options: [.usesLineFragmentOrigin], context: nil).height) + heightForThreeLineSnippet = finalHeight + return finalHeight + } + + private static var heightForReferenceTitle: CGFloat? + private static func heightForReferenceTitleForTraitCollection(_ traitCollection: UITraitCollection, theme: Theme) -> CGFloat { + + if let heightForReferenceTitle = heightForReferenceTitle { + return heightForReferenceTitle + } + + let referenceTitleFont = UIFont.wmf_font(Self.changeDetailReferenceTitleStyle, compatibleWithTraitCollection: traitCollection) + let referenceTitleAttributes = [NSAttributedString.Key.font: referenceTitleFont] + let oneLineTitleText = "1" + let oneLineTitleAttString = NSAttributedString(string: oneLineTitleText, attributes: referenceTitleAttributes) + let finalHeight = ceil(oneLineTitleAttString.boundingRect(with: CGSize(width: Self.availableSideScrollingCellWidth, height: CGFloat.infinity), options: [.usesLineFragmentOrigin], context: nil).height) + heightForReferenceTitle = finalHeight + return finalHeight + } + + private func calculateTallestSnippetContentHeightInChangeDetails(changeDetails: [ChangeDetail]) -> CGFloat { + var tallestSnippetChangeDetailHeight: CGFloat = 0 + + changeDetails.forEach { (changeDetail) in + switch changeDetail { + case .snippet(let snippet): + let snippetHeight = ceil(snippet.description.boundingRect(with: CGSize(width: Self.availableSideScrollingCellWidth, height: CGFloat.infinity), options: [.usesLineFragmentOrigin], context: nil).height) + + if tallestSnippetChangeDetailHeight < snippetHeight { + tallestSnippetChangeDetailHeight = snippetHeight + } + + case .reference: + break + } + } + + return tallestSnippetChangeDetailHeight + } + + private func calculateTallestReferenceContentHeightInChangeDetails(changeDetails: [ChangeDetail], traitCollection: UITraitCollection, theme: Theme) -> CGFloat { + var tallestReferenceChangeDetailHeight: CGFloat = 0 + + changeDetails.forEach { (changeDetail) in + switch changeDetail { + case .snippet: + break + case .reference(let reference): + let titleHeight = Self.heightForReferenceTitleForTraitCollection(traitCollection, theme: theme) + let descriptionHeight = ceil(reference.description.boundingRect(with: CGSize(width: Self.availableSideScrollingCellWidth, height: CGFloat.infinity), options: [.usesLineFragmentOrigin], context: nil).height) + let totalHeight = titleHeight + Self.changeDetailReferenceTitleDescriptionSpacing + descriptionHeight + + if tallestReferenceChangeDetailHeight < totalHeight { + tallestReferenceChangeDetailHeight = totalHeight + } + } + } + + return tallestReferenceChangeDetailHeight + } + + private func maxContentHeightFromTallestSnippetContentHeight(tallestSnippetContentHeight: CGFloat, tallestReferenceContentHeight: CGFloat, traitCollection: UITraitCollection, theme: Theme) -> CGFloat { + + guard tallestSnippetContentHeight > 0 else { + return tallestReferenceContentHeight + } + + let threeLineSnippetHeight = Self.heightForThreeLineSnippetForTraitCollection(traitCollection, theme: theme) + if tallestReferenceContentHeight == 0 { + return min(tallestSnippetContentHeight, threeLineSnippetHeight) + } else { + let finalSnippetHeight = min(tallestSnippetContentHeight, threeLineSnippetHeight) + return max(tallestReferenceContentHeight, finalSnippetHeight) + } + } + + private func referenceTypeForTemplate(_ template: SignificantEvents.Template, traitCollection: UITraitCollection, theme: Theme) -> String { + + var typeString: String + switch template { + case .articleDescription: + assertionFailure("This should not happen") + return "" + case .bookCitation: + typeString = CommonStrings.newBookReferenceTitle + case .journalCitation: + typeString = CommonStrings.newJournalReferenceTitle + case .newsCitation: + typeString = CommonStrings.newNewsReferenceTitle + case .websiteCitation: + typeString = CommonStrings.newWebsiteReferenceTitle + + } + + return typeString + } + + private func accessDateYearForTemplate(_ template: SignificantEvents.Template, traitCollection: UITraitCollection, theme: Theme) -> String? { + + let accessDateString: String? + switch template { + case .newsCitation(let newsCitation): + accessDateString = newsCitation.accessDateString + case .websiteCitation(let websiteCitation): + accessDateString = websiteCitation.accessDateString + default: + return nil + } + + if let accessDateString = accessDateString { + let dateFormatter = DateFormatter.wmf_monthNameDayOfMonthNumberYear() + if let date = dateFormatter?.date(from: accessDateString) { + let yearDateFormatter = DateFormatter.wmf_year() + let year = yearDateFormatter?.string(from: date) + return year + } + } + + return nil + + } + + private func descriptionForJournalCitation(_ journalCitation: SignificantEvents.Citation.Journal, traitCollection: UITraitCollection, theme: Theme) -> NSAttributedString? { + + let font = UIFont.wmf_font(Self.changeDetailDescriptionTextStyle, compatibleWithTraitCollection: traitCollection) + let boldFont = UIFont.wmf_font(Self.changeDetailDescriptionTextStyleItalic, compatibleWithTraitCollection: traitCollection) + let attributes = [NSAttributedString.Key.font: font, + NSAttributedString.Key.foregroundColor: + theme.colors.primaryText] + let boldAttributes = [NSAttributedString.Key.font: boldFont, + NSAttributedString.Key.foregroundColor: + theme.colors.primaryText] + + let titleString = "\"\(journalCitation.title)\" " + let mutableAttributedString = mutableString(from: titleString, linkedTo: journalCitation.urlString, with: attributes, linkColor: theme.colors.link) + let titleAttributedString = mutableAttributedString.copy() as? NSAttributedString + + var descriptionStart = "" + if let firstName = journalCitation.firstName { + if let lastName = journalCitation.lastName { + descriptionStart += "\(lastName), \(firstName)" + } + } else { + if let lastName = journalCitation.lastName { + descriptionStart += "\(lastName)" + } + } + + if let sourceDate = journalCitation.sourceDateString { + descriptionStart += " (\(sourceDate)). " + } else { + descriptionStart += ". " + } + + let descriptionStartAttributedString = NSAttributedString(string: descriptionStart, attributes: attributes) + + var volumeString = "" + if let volumeNumber = journalCitation.volumeNumber { + volumeString = String.localizedStringWithFormat(CommonStrings.newJournalReferenceVolume, volumeNumber) + } + let volumeAttributedString = NSAttributedString(string: "\(volumeString) ", attributes: boldAttributes) + + var descriptionEnd = "" + if let database = journalCitation.database { + let viaDatabaseString = String.localizedStringWithFormat(CommonStrings.newJournalReferenceDatabase, database) + if let pages = journalCitation.pages { + descriptionEnd += "\(pages) - \(viaDatabaseString)." + } else { + descriptionEnd += "\(viaDatabaseString)." + } + } else { + if let pages = journalCitation.pages { + descriptionEnd += "\(pages)." + } + } + + let descriptionEndAttributedString = NSAttributedString(string: descriptionEnd, attributes: attributes) + + let finalMutableAttributedString = NSMutableAttributedString(string: "") + finalMutableAttributedString.append(descriptionStartAttributedString) + if let titleAttributedString = titleAttributedString { + finalMutableAttributedString.append(titleAttributedString) + } + finalMutableAttributedString.append(volumeAttributedString) + finalMutableAttributedString.append(descriptionEndAttributedString) + + return finalMutableAttributedString.copy() as? NSAttributedString + + } + + private func descriptionForWebsiteCitation(_ websiteCitation: SignificantEvents.Citation.Website, traitCollection: UITraitCollection, theme: Theme) -> NSAttributedString? { + let font = UIFont.wmf_font(Self.changeDetailDescriptionTextStyle, compatibleWithTraitCollection: traitCollection) + let italicFont = UIFont.wmf_font(Self.changeDetailDescriptionTextStyleItalic, compatibleWithTraitCollection: traitCollection) + let attributes = [NSAttributedString.Key.font: font, + NSAttributedString.Key.foregroundColor: + theme.colors.primaryText] + let italicAttributes = [NSAttributedString.Key.font: italicFont, + NSAttributedString.Key.foregroundColor: + theme.colors.primaryText] + + let titleString = "\"\(websiteCitation.title)\" " + let mutableAttributedString = mutableString(from: titleString, linkedTo: websiteCitation.urlString, with: attributes, linkColor: theme.colors.link) + let titleAttributedString = mutableAttributedString.copy() as? NSAttributedString + + var publisherText = "" + if let publisher = websiteCitation.publisher { + publisherText = "\(publisher). " + } + + let publisherAttributedString = NSAttributedString(string: publisherText, attributes: italicAttributes) + + var accessDateString = "" + if let accessDate = websiteCitation.accessDateString { + accessDateString = "\(accessDate). " + } + + let accessDateAttributedString = NSAttributedString(string: accessDateString, attributes: attributes) + + let finalMutableAttributedString = NSMutableAttributedString(string: "") + if let titleAttributedString = titleAttributedString { + finalMutableAttributedString.append(titleAttributedString) + } + finalMutableAttributedString.append(publisherAttributedString) + finalMutableAttributedString.append(accessDateAttributedString) + + if let archiveDateString = websiteCitation.archiveDateString, + let archiveUrlString = websiteCitation.archiveDotOrgUrlString, + URL(string: archiveUrlString) != nil { + let archiveLinkText = CommonStrings.newWebsiteReferenceArchiveUrlText + let archiveLinkMutableAttributedString = mutableString(from: archiveLinkText, linkedTo: archiveUrlString, with: attributes, linkColor: theme.colors.link) + + if let archiveLinkAttributedString = archiveLinkMutableAttributedString.copy() as? NSAttributedString { + let lastText = String.localizedStringWithFormat(CommonStrings.newWebsiteReferenceArchiveDateText, archiveDateString) + let lastAttributedString = NSAttributedString(string: " \(lastText)", attributes: attributes) + + finalMutableAttributedString.append(archiveLinkAttributedString) + finalMutableAttributedString.append(lastAttributedString) + + } + + } + + return finalMutableAttributedString.copy() as? NSAttributedString + } + + private func descriptionForNewsCitation(_ newsCitation: SignificantEvents.Citation.News, traitCollection: UITraitCollection, theme: Theme) -> NSAttributedString? { + + let font = UIFont.wmf_font(Self.changeDetailDescriptionTextStyle, compatibleWithTraitCollection: traitCollection) + let italicFont = UIFont.wmf_font(Self.changeDetailDescriptionTextStyleItalic, compatibleWithTraitCollection: traitCollection) + let attributes = [NSAttributedString.Key.font: font, + NSAttributedString.Key.foregroundColor: + theme.colors.primaryText] + let italicAttributes = [NSAttributedString.Key.font: italicFont, + NSAttributedString.Key.foregroundColor: + theme.colors.primaryText] + + let titleString = "\"\(newsCitation.title)\" " + let mutableAttributedString = mutableString(from: titleString, linkedTo: newsCitation.urlString, with: attributes, linkColor: theme.colors.link) + let titleAttributedString = mutableAttributedString.copy() as? NSAttributedString + + var descriptionStart = "" + if let firstName = newsCitation.firstName { + if let lastName = newsCitation.lastName { + descriptionStart += "\(lastName), \(firstName) " + } + } else { + if let lastName = newsCitation.lastName { + descriptionStart += "\(lastName) " + } + } + + if let sourceDate = newsCitation.sourceDateString { + descriptionStart += "(\(sourceDate)). " + } else { + descriptionStart += ". " + } + + let descriptionStartAttributedString = NSAttributedString(string: descriptionStart, attributes: attributes) + + var publicationText = "" + if let publication = newsCitation.publication { + publicationText = "\(publication). " + } + + let publicationAttributedString = NSAttributedString(string: publicationText, attributes: italicAttributes) + + var retrievedString = "" + if let accessDate = newsCitation.accessDateString { + retrievedString = String.localizedStringWithFormat(CommonStrings.newNewsReferenceRetrievedDate, accessDate) + } + + let retrievedDateAttributedString = NSAttributedString(string: "\(retrievedString) ", attributes: attributes) + + let finalMutableAttributedString = NSMutableAttributedString(string: "") + finalMutableAttributedString.append(descriptionStartAttributedString) + if let titleAttributedString = titleAttributedString { + finalMutableAttributedString.append(titleAttributedString) + } + finalMutableAttributedString.append(publicationAttributedString) + finalMutableAttributedString.append(retrievedDateAttributedString) + + return finalMutableAttributedString.copy() as? NSAttributedString + + } + + private func descriptionForBookCitation(_ bookCitation: SignificantEvents.Citation.Book, traitCollection: UITraitCollection, theme: Theme) -> NSAttributedString? { + + let font = UIFont.wmf_font(Self.changeDetailDescriptionTextStyle, compatibleWithTraitCollection: traitCollection) + let italicFont = UIFont.wmf_font(Self.changeDetailDescriptionTextStyleItalic, compatibleWithTraitCollection: traitCollection) + let attributes = [NSAttributedString.Key.font: font, + NSAttributedString.Key.foregroundColor: + theme.colors.primaryText] + let italicAttributes = [NSAttributedString.Key.font: italicFont, + NSAttributedString.Key.foregroundColor: + theme.colors.primaryText] + + let titleAttributedString = NSAttributedString(string: "\(bookCitation.title) ", attributes: italicAttributes) + + var descriptionStart = "" + if let firstName = bookCitation.firstName { + if let lastName = bookCitation.lastName { + descriptionStart += "\(lastName), \(firstName)" + } + } else { + if let lastName = bookCitation.lastName { + descriptionStart += "\(lastName)" + } + } + + if let yearOfPub = bookCitation.yearPublished { + descriptionStart += " (\(yearOfPub)). " + } else { + descriptionStart += ". " + } + + let descriptionStartAttributedString = NSAttributedString(string: descriptionStart, attributes: attributes) + + var descriptionMiddle = "" + if let locationPublished = bookCitation.locationPublished { + if let publisher = bookCitation.publisher { + descriptionMiddle += "\(locationPublished): \(publisher). " + } else { + descriptionMiddle += "\(locationPublished). " + } + } else { + if let publisher = bookCitation.publisher { + descriptionMiddle += "\(publisher). " + } + } + + if let pagesCited = bookCitation.pagesCited { + descriptionMiddle += "pp. \(pagesCited) " + } + + let descriptionMiddleAttributedString = NSAttributedString(string: descriptionMiddle, attributes: attributes) + + var isbnAttributedString: NSAttributedString? + if let isbn = bookCitation.isbn { + let isbnPrefix = "ISBN: " + let mutableAttributedString = NSMutableAttributedString(string: "\(isbnPrefix + isbn)", attributes: attributes) + let isbnTitle = "Special:BookSources" + let isbnURL = Configuration.current.articleURLForHost(Configuration.Domain.englishWikipedia, languageVariantCode: nil, appending: [isbnTitle, isbn]) + let range = NSRange(location: 0, length: isbnPrefix.count + isbn.count) + if let isbnURL = isbnURL { + mutableAttributedString.addAttributes([NSAttributedString.Key.link : isbnURL, + NSAttributedString.Key.foregroundColor: theme.colors.link], range: range) + } else { + mutableAttributedString.addAttributes(attributes, range: range) + } + + isbnAttributedString = mutableAttributedString.copy() as? NSAttributedString + } + + let finalMutableAttributedString = NSMutableAttributedString(string: "") + finalMutableAttributedString.append(descriptionStartAttributedString) + finalMutableAttributedString.append(titleAttributedString) + finalMutableAttributedString.append(descriptionMiddleAttributedString) + if let isbnAttributedString = isbnAttributedString { + finalMutableAttributedString.append(isbnAttributedString) + } + + return finalMutableAttributedString.copy() as? NSAttributedString + + } + + private func mutableString(from text: String, linkedTo urlString: String?, with textAttributes: [NSAttributedString.Key:Any], linkColor: UIColor) -> NSMutableAttributedString { + let mutableAttributedString: NSMutableAttributedString + if let urlString = urlString, let url = URL(string: urlString), let externalLinkIcon = UIImage(named: "mini-external") { + mutableAttributedString = NSMutableAttributedString(string: text.trimmingCharacters(in: .whitespaces), attributes: textAttributes) + mutableAttributedString.append(NSAttributedString(string: " ")) + let externalLinkString = NSAttributedString(attachment: NSTextAttachment(image: externalLinkIcon)) + mutableAttributedString.append(externalLinkString) + mutableAttributedString.append(NSAttributedString(string: " ")) + let range = NSRange(location: 0, length: mutableAttributedString.length) + mutableAttributedString.addAttributes([NSAttributedString.Key.link : url, + NSAttributedString.Key.foregroundColor: linkColor], range: range) + } else { + mutableAttributedString = NSMutableAttributedString(string: text, attributes: textAttributes) + let range = NSRange(location: 0, length: text.count) + mutableAttributedString.addAttributes(textAttributes, range: range) + } + return mutableAttributedString + } + + private func getTimestampString() -> String? { + switch typedEvent { + case .newTalkPageTopic(let newTalkPageTopic): + return newTalkPageTopic.timestampString + case .large(let largeChange): + return largeChange.timestampString + case .vandalismRevert(let vandalismRevert): + return vandalismRevert.timestampString + case .small: + return nil + } + } + + // Only used in the html portion of the feature + func fullyRelativeTimestampForDisplay() -> String? { + + guard let timestampString = getTimestampString() else { + return nil + } + + return ArticleAsLivingDocViewModel.displayTimestamp(timestampString: timestampString, fullyRelative: true) + } + + func timestampForDisplay() -> String? { + if let displayTimestamp = displayTimestamp { + return displayTimestamp + } else if let timestampString = getTimestampString() { + self.displayTimestamp = ArticleAsLivingDocViewModel.eventDisplayTimestamp(timestampString: timestampString) + } + + return displayTimestamp + } + + private func userNameAndEditCount() -> (userName: String, editCount: UInt?)? { + let userName: String + let editCount: UInt? + switch typedEvent { + case .newTalkPageTopic(let newTalkPageTopic): + userName = newTalkPageTopic.user + editCount = newTalkPageTopic.userEditCount + case .large(let largeChange): + userName = largeChange.user + editCount = largeChange.userEditCount + case .vandalismRevert(let vandalismRevert): + userName = vandalismRevert.user + editCount = vandalismRevert.userEditCount + case .small: + return nil + } + + return (userName: userName, editCount: editCount) + } + + static var botIconName: String { + return "article-as-living-doc-svg-bot" + } + + static var anonymousIconName: String = "article-as-living-doc-svg-anon" + + private func userInfoHtmlSnippet() -> String? { + guard let userNameAndEditCount = self.userNameAndEditCount() else { + assertionFailure("Shouldn't reach this point") + return nil + } + let userName = userNameAndEditCount.userName + let editCount = userNameAndEditCount.editCount + + if let editCount = editCount, + userType != .anonymous { + let formattedEditCount = NumberFormatter.localizedThousandsStringFromNumber(NSNumber(value: editCount)).localizedLowercase + let userInfo = String.localizedStringWithFormat(CommonStrings.revisionUserInfo, userName, formattedEditCount) + + let rangeOfUserName = (userInfo as NSString).range(of: userName) + let rangeValid = rangeOfUserName.location != NSNotFound && rangeOfUserName.location + rangeOfUserName.length <= userInfo.count + let userNameHrefString = "#significant-events-username-\(userName)" + if rangeValid { + + let mutableUserInfo = NSMutableString(string: userInfo) + + let linkStartInsert: String + if userType == .bot { + linkStartInsert = "" + } else { + linkStartInsert = "" + } + let linkEndInsert = "" + mutableUserInfo.insert(linkStartInsert, at: rangeOfUserName.location) + mutableUserInfo.insert(linkEndInsert, at: rangeOfUserName.location + rangeOfUserName.length + linkStartInsert.count) + + if let userInfoResult = mutableUserInfo.copy() as? NSString { + return (userInfoResult as String) + } else { + assertionFailure("This shouldn't happen") + return nil + } + } + } else { + return "\(CommonStrings.revisionUserInfoAnonymous)" + + } + + return nil + } + + func userInfoForTraitCollection(_ traitCollection: UITraitCollection, theme: Theme) -> NSAttributedString? { + if let userInfo = userInfo { + return userInfo + } + + guard let userNameAndEditCount = self.userNameAndEditCount() else { + assertionFailure("Shouldn't reach this point") + return nil + } + + let userName = userNameAndEditCount.userName + let maybeEditCount = userNameAndEditCount.editCount + + guard let editCount = maybeEditCount, + userType != .anonymous else { + let anonymousUserInfo = CommonStrings.revisionUserInfoAnonymous + + let font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection) + let attributes = [NSAttributedString.Key.font: font, + NSAttributedString.Key.foregroundColor: theme.colors.secondaryText] + let mutableAttributedString = NSMutableAttributedString(string: anonymousUserInfo, attributes: attributes) + addIcon(to: mutableAttributedString, at: 0, for: userType) + // Need this next line to appropriately color the icon + mutableAttributedString.addAttributes(attributes, range: NSRange(location: 0, length: 2)) + guard let attributedString = mutableAttributedString.copy() as? NSAttributedString else { + return nil + } + + self.userInfo = attributedString + return attributedString + } + + let formattedEditCount = NumberFormatter.localizedThousandsStringFromNumber(NSNumber(value: editCount)).localizedLowercase + let userInfo = String.localizedStringWithFormat( CommonStrings.revisionUserInfo, userName, formattedEditCount) + + let font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection) + let attributes = [NSAttributedString.Key.font: font, + NSAttributedString.Key.foregroundColor: theme.colors.secondaryText] + let rangeOfUserName = (userInfo as NSString).range(of: userName) + let rangeValid = rangeOfUserName.location != NSNotFound && rangeOfUserName.location + rangeOfUserName.length <= userInfo.count + + guard let title = "User:\(userName)".percentEncodedPageTitleForPathComponents, + let userNameURL = Configuration.current.articleURLForHost(Configuration.Domain.englishWikipedia, languageVariantCode: nil, appending: [title]), + rangeValid else { + let attributedString = NSAttributedString(string: userInfo, attributes: attributes) + self.userInfo = attributedString + return attributedString + } + + let mutableAttributedString = NSMutableAttributedString(string: userInfo, attributes: attributes) + mutableAttributedString.addAttribute(NSAttributedString.Key.link, value: userNameURL as NSURL, range: rangeOfUserName) + mutableAttributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: theme.colors.link, range: rangeOfUserName) + + addIcon(to: mutableAttributedString, at: rangeOfUserName.location, for: userType) + + guard let attributedString = mutableAttributedString.copy() as? NSAttributedString else { + return nil + } + + self.userInfo = attributedString + return attributedString + } + + func addIcon(to mutableAttributedString: NSMutableAttributedString, at location: Int, for userType: UserType) { + if userType == .bot || userType == .anonymous { + let imageAttachment = NSTextAttachment() + imageAttachment.image = UIImage(named: (userType == .bot ? Self.botIconName : Self.anonymousIconName)) + let imageString = NSAttributedString(attachment: imageAttachment) + mutableAttributedString.insert(imageString, at: location) + mutableAttributedString.insert(NSAttributedString(string: " "), at: location + imageString.length) + } + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Significant Events Endpoint/SignificantEventsFetcher.swift b/Apps/Wikipedia/WMF Framework/Significant Events Endpoint/SignificantEventsFetcher.swift new file mode 100644 index 0000000..011bb6f --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Significant Events Endpoint/SignificantEventsFetcher.swift @@ -0,0 +1,119 @@ +import Foundation + +enum SignificantEventsFetcherError: Error { + case failureToGenerateURL + case missingSignificantEvents +} + +public class SignificantEventsFetcher: Fetcher { + + public func fetchSignificantEvents(rvStartId: UInt? = nil, title: String, siteURL: URL, completion: @escaping ((Result) -> Void)) { + + guard let url = significantEventsURL(rvStartId: rvStartId, title: title, siteURL: siteURL) else { + completion(.failure(SignificantEventsFetcherError.failureToGenerateURL)) + return + } + + let request = URLRequest(url: url) + + _ = session.jsonDecodableTask(with: request) { (significantEvents: SignificantEvents?, response, error) in + if let error = error { + completion(.failure(error)) + return + } + + if let statusCode = (response as? HTTPURLResponse)?.statusCode, + statusCode != 200 { + completion(.failure(RequestError.unexpectedResponse)) + return + } + + guard let significantEvents = significantEvents else { + completion(.failure(SignificantEventsFetcherError.missingSignificantEvents)) + return + } + + completion(.success(significantEvents)) + } + + } + + private func significantEventsURL(rvStartId: UInt? = nil, title: String, siteURL: URL) -> URL? { + let labsHost = "mobileapps-ios-experiments.wmflabs.org" + guard let siteHost = siteURL.host, + let percentEncodedTitle = title.percentEncodedPageTitleForPathComponents else { + return nil + } + + let pathComponents = [siteHost, "v1", "page", "significant-events", percentEncodedTitle] + var components = URLComponents() + components.host = labsHost + components.scheme = "https" + components.replacePercentEncodedPathWithPathComponents(pathComponents) + if let rvStartId = rvStartId { + let queryParameters = ["rvstartid": rvStartId] + components.replacePercentEncodedQueryWithQueryParameters(queryParameters) + } + + return components.url + } + + private struct EditMetrics: Decodable { + let items: [Item]? + + struct Item: Decodable { + let results: [Result]? + + struct Result: Decodable { + let edits: Int? + } + } + } + + public func fetchEditMetrics(for pageTitle: String, pageURL: URL, completion: @escaping (Result<[NSNumber], Error>) -> Void ) { + DispatchQueue.global(qos: .userInitiated).async { + guard + let title = pageTitle.percentEncodedPageTitleForPathComponents, + let project = pageURL.wmf_site?.host, + let daysAgo = Calendar.current.date(byAdding: .day, value: -30, to: Date()), + let from = DateFormatter.wmf_englishUTCNonDelimitedYearMonthDay()?.string(from: daysAgo), + let to = DateFormatter.wmf_englishUTCNonDelimitedYearMonthDay()?.string(from: Date()) + else { + completion(.failure(RequestError.invalidParameters)) + return + } + let pathComponents = ["edits", "per-page", project, title, "all-editor-types", "daily", from, to] + let components = self.configuration.metricsAPIURLComponents(appending: pathComponents) + guard let url = components.url else { + completion(.failure(RequestError.invalidParameters)) + return + } + self.session.jsonDecodableTask(with: url) { (editMetrics: EditMetrics?, response: URLResponse?, error: Error?) in + if let error = error { + completion(.failure(error)) + return + } + guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { + completion(.failure(RequestError.unexpectedResponse)) + return + } + var allEdits = [NSNumber]() + guard + let items = editMetrics?.items, + let firstItem = items.first, + let results = firstItem.results + else { + completion(.failure(RequestError.noNewData)) + return + } + for case let result in results { + guard let edits = result.edits else { + continue + } + allEdits.append(NSNumber(value: edits)) + } + completion(.success(allEdits)) + } + } + } +} diff --git a/Apps/Wikipedia/WMF Framework/Significant Events Endpoint/SignificantEventsModels.swift b/Apps/Wikipedia/WMF Framework/Significant Events Endpoint/SignificantEventsModels.swift new file mode 100644 index 0000000..6d47e4c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Significant Events Endpoint/SignificantEventsModels.swift @@ -0,0 +1,608 @@ +import Foundation +enum SignificantEventsDecodeError: Error { + case unableToParseIntoTypedEvents +} + +public struct SignificantEvents: Decodable { + public let nextRvStartId: UInt? + public let sha: String? + private let untypedEvents: [UntypedEvent] + public let typedEvents: [TypedEvent] + public let summary: Summary + + enum CodingKeys: String, CodingKey { + case nextRvStartId + case sha + case untypedEvents = "timeline" + case typedEvents + case summary + } + + public struct Summary: Decodable { + public let earliestTimestampString: String + public let numChanges: UInt + public let numUsers: UInt + + enum CodingKeys: String, CodingKey { + case earliestTimestampString = "earliestTimestamp" + case numChanges + case numUsers + } + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + nextRvStartId = try? container.decode(UInt.self, forKey: .nextRvStartId) + sha = try? container.decode(String.self, forKey: .sha) + summary = try container.decode(Summary.self, forKey: .summary) + let untypedEvents = try container.decode([UntypedEvent].self, forKey: .untypedEvents) + + var typedEvents: [TypedEvent] = [] + + for untypedEvent in untypedEvents { + switch untypedEvent.outputType { + case .small: + if let event = Event.Small(untypedEvent: untypedEvent) { + typedEvents.append(.small(event)) + } + case .large: + if let event = Event.Large(untypedEvent: untypedEvent) { + typedEvents.append(.large(event)) + } + case .vandalismRevert: + if let event = Event.VandalismRevert(untypedEvent: untypedEvent) { + typedEvents.append(.vandalismRevert(event)) + } + case .newTalkPageTopic: + if let event = Event.NewTalkPageTopic(untypedEvent: untypedEvent) { + typedEvents.append(.newTalkPageTopic(event)) + } + } + } + + // zero untyped events is a valid case if the user has paged to the end of the endpoint cache + // unTypedEvents > 0 and typedEvents == 0 is invalid, meaning all events failed to convert + guard typedEvents.count > 0 || untypedEvents.count == 0 else { + throw SignificantEventsDecodeError.unableToParseIntoTypedEvents + } + + self.typedEvents = typedEvents + self.untypedEvents = untypedEvents + } + + public enum SnippetType: Int, Decodable { + case addedLine = 1 + case addedAndDeletedInLine = 3 + case addedAndDeletedInMovedLine = 5 + } + + public enum EventOutputType: String, Decodable { + case large = "large-change" + case small = "small-change" + case newTalkPageTopic = "new-talk-page-topic" + case vandalismRevert = "vandalism-revert" + } + + public enum ChangeOutputType: String, Decodable { + case addedText = "added-text" + case deletedText = "deleted-text" + case newTemplate = "new-template" + } + + public enum TypedEvent { + case large(Event.Large) + case small(Event.Small) + case vandalismRevert(Event.VandalismRevert) + case newTalkPageTopic(Event.NewTalkPageTopic) + } + + public enum TypedChange { + case addedText(Change.AddedText) + case deletedText(Change.DeletedText) + case newTemplate(Change.NewTemplates) + } +} + +// MARK: Events + +public extension SignificantEvents { + + struct Event { + + public struct Large { + let outputType: EventOutputType + public let revId: UInt + public let parentId: UInt + public let timestampString: String + public let user: String + public let userId: UInt + public let userGroups: [String]? + public let userEditCount: UInt? + public let typedChanges: [TypedChange] + + init?(untypedEvent: UntypedEvent) { + guard let revId = untypedEvent.revId, + let parentId = untypedEvent.parentId, + let timestampString = untypedEvent.timestampString, + let user = untypedEvent.user, + let userId = untypedEvent.userId, + let untypedChanges = untypedEvent.untypedChanges else { + return nil + } + + self.outputType = untypedEvent.outputType + self.revId = revId + self.parentId = parentId + self.timestampString = timestampString + self.user = user + self.userId = userId + self.userGroups = untypedEvent.userGroups + self.userEditCount = untypedEvent.userEditCount + + var changes: [TypedChange] = [] + + for untypedChange in untypedChanges { + switch untypedChange.outputType { + case .addedText: + if let change = Change.AddedText(untypedChange: untypedChange) { + changes.append(.addedText(change)) + } + case .deletedText: + if let change = Change.DeletedText(untypedChange: untypedChange) { + changes.append(.deletedText(change)) + } + case .newTemplate: + if let change = Change.NewTemplates(untypedChange: untypedChange) { + changes.append(.newTemplate(change)) + } + } + } + + guard changes.count == untypedChanges.count else { + return nil + } + + self.typedChanges = changes + } + } + + public struct Small: Equatable { + let outputType: EventOutputType + public let revId: UInt + public let parentId: UInt + public let timestampString: String + + fileprivate init?(untypedEvent: UntypedEvent) { + guard let revId = untypedEvent.revId, + let parentId = untypedEvent.parentId, + let timestampString = untypedEvent.timestampString else { + return nil + } + + self.outputType = untypedEvent.outputType + self.revId = revId + self.parentId = parentId + self.timestampString = timestampString + } + + public static func == (lhs: SignificantEvents.Event.Small, rhs: SignificantEvents.Event.Small) -> Bool { + return lhs.revId == rhs.revId + } + } + + public struct VandalismRevert { + let outputType: EventOutputType + public let revId: UInt + public let parentId: UInt + public let timestampString: String + public let user: String + public let userId: UInt + public let sections: [String] + public let userGroups: [String]? + public let userEditCount: UInt? + + fileprivate init?(untypedEvent: UntypedEvent) { + guard let revId = untypedEvent.revId, + let parentId = untypedEvent.parentId, + let timestampString = untypedEvent.timestampString, + let user = untypedEvent.user, + let userId = untypedEvent.userId, + let sections = untypedEvent.sections else { + return nil + } + + self.outputType = untypedEvent.outputType + self.revId = revId + self.parentId = parentId + self.timestampString = timestampString + self.user = user + self.userId = userId + self.sections = sections + self.userGroups = untypedEvent.userGroups + self.userEditCount = untypedEvent.userEditCount + } + } + + public struct NewTalkPageTopic { + let outputType: EventOutputType + let revId: UInt + let parentId: UInt + public let timestampString: String + public let user: String + public let userId: UInt + public let section: String? + public let snippet: String + public let userGroups: [String]? + public let userEditCount: UInt? + + fileprivate init?(untypedEvent: UntypedEvent) { + guard let revId = untypedEvent.revId, + let parentId = untypedEvent.parentId, + let timestampString = untypedEvent.timestampString, + let user = untypedEvent.user, + let userId = untypedEvent.userId, + let snippet = untypedEvent.snippet else { + return nil + } + + self.outputType = untypedEvent.outputType + self.revId = revId + self.parentId = parentId + self.timestampString = timestampString + self.user = user + self.userId = userId + self.section = untypedEvent.section + self.snippet = snippet + self.userGroups = untypedEvent.userGroups + self.userEditCount = untypedEvent.userEditCount + } + } + } +} + +// MARK: Changes + +public extension SignificantEvents { + + struct Change { + + public struct AddedText { + let outputType: ChangeOutputType + public let sections: [String] + public let snippet: String? + public let snippetType: SnippetType + public let characterCount: UInt + + fileprivate init?(untypedChange: UntypedChange) { + guard let snippetType = untypedChange.snippetType, + let characterCount = untypedChange.characterCount else { + return nil + } + + self.outputType = untypedChange.outputType + self.sections = untypedChange.sections + self.snippet = untypedChange.snippet + self.snippetType = snippetType + self.characterCount = characterCount + } + } + + public struct DeletedText { + let outputType: ChangeOutputType + public let sections: [String] + public let characterCount: UInt + + fileprivate init?(untypedChange: UntypedChange) { + guard let characterCount = untypedChange.characterCount else { + return nil + } + + self.outputType = untypedChange.outputType + self.sections = untypedChange.sections + self.characterCount = characterCount + } + } + + public struct NewTemplates { + let outputType: ChangeOutputType + public let sections: [String] + private let untypedTemplates: [[String: String]] + public let typedTemplates: [Template] + + fileprivate init?(untypedChange: UntypedChange) { + guard let untypedTemplates = untypedChange.untypedTemplates else { + return nil + } + + var typedTemplates: [Template] = [] + self.outputType = untypedChange.outputType + self.sections = untypedChange.sections + self.untypedTemplates = untypedTemplates + + for untypedTemplate in untypedTemplates { + guard let name = untypedTemplate["name"] else { + continue + } + if name.localizedCaseInsensitiveContains("cite") { + if name.localizedCaseInsensitiveContains("book"), let bookCitation = Citation.Book(dict: untypedTemplate) { + typedTemplates.append(.bookCitation(bookCitation)) + } else if name.localizedCaseInsensitiveContains("journal"), let journalCitation = Citation.Journal(dict: untypedTemplate) { + typedTemplates.append(.journalCitation(journalCitation)) + } else if name.localizedCaseInsensitiveContains("web"), let webCitation = Citation.Website(dict: untypedTemplate) { + typedTemplates.append(.websiteCitation(webCitation)) + } else if name.localizedCaseInsensitiveContains("news"), let newsCitation = Citation.News(dict: untypedTemplate) { + typedTemplates.append(.newsCitation(newsCitation)) + } + } else if name.localizedCaseInsensitiveContains("short description"), let articleDescription = ArticleDescription(dict: untypedTemplate) { + typedTemplates.append(.articleDescription(articleDescription)) + } + } + + self.typedTemplates = typedTemplates + } + } + } +} + +// MARK: Templates + +public extension SignificantEvents { + + enum Template { + case bookCitation(Citation.Book) + case articleDescription(ArticleDescription) + case journalCitation(Citation.Journal) + case newsCitation(Citation.News) + case websiteCitation(Citation.Website) + } + + struct Citation { + + // https://en.wikipedia.org/wiki/Template:Cite_book/TemplateData + public struct Book { + public let title: String + public let lastName: String? + public let firstName: String? + public let yearPublished: String? + public let locationPublished: String? + public let publisher: String? + public let pagesCited: String? + public let isbn: String? + + init?(dict: [String: String]) { + guard let title = dict.nonEmptyValueForKey(key: "title") else { + return nil + } + + self.title = title + + let batch1 = dict.nonEmptyValueForKey(key: "last") ?? + dict.nonEmptyValueForKey(key: "last1") ?? + dict.nonEmptyValueForKey(key: "author") ?? + dict.nonEmptyValueForKey(key: "author1") ?? + dict.nonEmptyValueForKey(key: "author1-last") + let batch2 = dict.nonEmptyValueForKey(key: "author-last") ?? + dict.nonEmptyValueForKey(key: "surname1") ?? + dict.nonEmptyValueForKey(key: "author-last1") ?? + dict.nonEmptyValueForKey(key: "subject1") ?? + dict.nonEmptyValueForKey(key: "surname") + let batch3 = dict.nonEmptyValueForKey(key: "author-last") ?? + dict.nonEmptyValueForKey(key: "subject") + + self.lastName = batch1 ?? batch2 ?? batch3 + + self.firstName = dict.nonEmptyValueForKey(key: "first") ?? + dict.nonEmptyValueForKey(key: "given") ?? + dict.nonEmptyValueForKey(key: "author-first") ?? + dict.nonEmptyValueForKey(key: "first1") ?? + dict.nonEmptyValueForKey(key: "given1") ?? + dict.nonEmptyValueForKey(key: "author-first1") ?? + dict.nonEmptyValueForKey(key: "author1-first") + + self.yearPublished = dict.nonEmptyValueForKey(key: "year") + self.locationPublished = dict.nonEmptyValueForKey(key: "location") ?? + dict.nonEmptyValueForKey(key: "place") + + self.publisher = dict.nonEmptyValueForKey(key: "publisher") ?? + dict.nonEmptyValueForKey(key: "distributor") ?? + dict.nonEmptyValueForKey(key: "institution") ?? + dict.nonEmptyValueForKey(key: "newsgroup") + + self.pagesCited = dict.nonEmptyValueForKey(key: "pages") ?? + dict.nonEmptyValueForKey(key: "pp") + + self.isbn = dict.nonEmptyValueForKey(key: "isbn", caseInsensitive: true) ?? + dict.nonEmptyValueForKey(key: "isbn13", caseInsensitive: true) + } + } + + // https://en.wikipedia.org/wiki/Template:Cite_journal#TemplateData + public struct Journal { + public let lastName: String? + public let firstName: String? + public let sourceDateString: String? + public let title: String + public let journal: String + public let urlString: String? + public let volumeNumber: String? + public let pages: String? + public let database: String? + + init?(dict: [String: String]) { + guard let title = dict.nonEmptyValueForKey(key: "title"), + let journal = dict.nonEmptyValueForKey(key: "journal") else { + return nil + } + + self.title = title + self.journal = journal + + self.lastName = dict.nonEmptyValueForKey(key: "last") ?? + dict.nonEmptyValueForKey(key: "author") ?? + dict.nonEmptyValueForKey(key: "author1") ?? + dict.nonEmptyValueForKey(key: "authors") ?? + dict.nonEmptyValueForKey(key: "last1") + + self.firstName = dict.nonEmptyValueForKey(key: "first") ?? + dict.nonEmptyValueForKey(key: "first1") + + self.sourceDateString = dict.nonEmptyValueForKey(key: "date") + self.urlString = dict.nonEmptyValueForKey(key: "url", caseInsensitive: true) + self.volumeNumber = dict.nonEmptyValueForKey(key: "volume") + self.pages = dict.nonEmptyValueForKey(key: "pages") + self.database = dict.nonEmptyValueForKey(key: "via") + } + } + + // https://en.wikipedia.org/wiki/Template:Cite_news#TemplateData + public struct News { + public let lastName: String? + public let firstName: String? + public let sourceDateString: String? + public let title: String + public let urlString: String? + public let publication: String? + public let accessDateString: String? + + init?(dict: [String: String]) { + guard let title = dict.nonEmptyValueForKey(key: "title") else { + return nil + } + + self.title = title + self.lastName = dict.nonEmptyValueForKey(key: "last") ?? + dict.nonEmptyValueForKey(key: "last1") ?? + dict.nonEmptyValueForKey(key: "author") ?? + dict.nonEmptyValueForKey(key: "author1") ?? + dict.nonEmptyValueForKey(key: "authors") + + self.firstName = dict.nonEmptyValueForKey(key: "first") ?? + dict.nonEmptyValueForKey(key: "first1") + + self.sourceDateString = dict.nonEmptyValueForKey(key: "date") + self.publication = dict.nonEmptyValueForKey(key: "work") ?? + dict.nonEmptyValueForKey(key: "journal") ?? + dict.nonEmptyValueForKey(key: "magazine") ?? + dict.nonEmptyValueForKey(key: "periodical") ?? + dict.nonEmptyValueForKey(key: "newspaper") ?? + dict.nonEmptyValueForKey(key: "website") + + self.urlString = dict.nonEmptyValueForKey(key: "url", caseInsensitive: true) + self.accessDateString = dict.nonEmptyValueForKey(key: "access-date") ?? dict.nonEmptyValueForKey(key: "accessdate") + } + } + + // https://en.wikipedia.org/wiki/Template:Cite_web#TemplateData + public struct Website { + + public let urlString: String + public let title: String + public let publisher: String? + public let accessDateString: String? + public let archiveDateString: String? + public let archiveDotOrgUrlString: String? + + init?(dict: [String: String]) { + guard let title = dict.nonEmptyValueForKey(key: "title"), + let urlString = dict.nonEmptyValueForKey(key: "url", caseInsensitive: true) else { + return nil + } + + self.title = title + self.urlString = urlString + + self.publisher = dict.nonEmptyValueForKey(key: "publisher") ?? + dict.nonEmptyValueForKey(key: "website") ?? + dict.nonEmptyValueForKey(key: "work") + + self.accessDateString = dict.nonEmptyValueForKey(key: "access-date") ?? dict.nonEmptyValueForKey(key: "accessdate") + self.archiveDateString = dict.nonEmptyValueForKey(key: "archive-date") ?? dict.nonEmptyValueForKey(key: "archivedate") + self.archiveDotOrgUrlString = dict.nonEmptyValueForKey(key: "archive-url") ?? dict.nonEmptyValueForKey(key: "archiveurl") + } + } + } + + struct ArticleDescription { + public let text: String + + init?(dict: [String: String]) { + guard let text = dict.nonEmptyValueForKey(key: "1") else { + return nil + } + + self.text = text + } + } + + +} + +// MARK: Untyped + +public extension SignificantEvents { + struct UntypedEvent: Decodable { + let outputType: EventOutputType + let revId: UInt? + let parentId: UInt? + let timestampString: String? + let user: String? + let userId: UInt? + let userGroups: [String]? + let userEditCount: UInt? + let count: UInt? + let sections: [String]? + let section: String? + let snippet: String? + let untypedChanges: [UntypedChange]? + + enum CodingKeys: String, CodingKey { + case revId = "revid" + case parentId = "parentid" + case timestampString = "timestamp" + case outputType + case user + case userId = "userid" + case userGroups + case userEditCount + case count + case sections + case section + case snippet + case untypedChanges = "significantChanges" + } + } + + struct UntypedChange: Decodable { + let outputType: ChangeOutputType + let sections: [String] + let snippet: String? + let snippetType: SnippetType? + let characterCount: UInt? + let untypedTemplates: [[String: String]]? + + enum CodingKeys: String, CodingKey { + case outputType + case sections + case snippet + case snippetType + case characterCount + case untypedTemplates = "templates" + } + } +} + +private extension Dictionary where Key == String, Value == String { + func nonEmptyValueForKey(key: String, caseInsensitive: Bool = false) -> String? { + guard let key = caseInsensitive + ? keys.first(where: {$0.caseInsensitiveCompare(key) == .orderedSame}) + : key else { + return nil + } + + if let value = self[key], !value.isEmpty { + return value + } + + return nil + } +} diff --git a/Apps/Wikipedia/WMF Framework/String+Domains.swift b/Apps/Wikipedia/WMF Framework/String+Domains.swift new file mode 100644 index 0000000..65264bc --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/String+Domains.swift @@ -0,0 +1,11 @@ +import Foundation + +extension String { + var withDotPrefix: String { + return "." + self + } + + public func isDomainOrSubDomainOf(_ domain: String) -> Bool { + return self == domain || self.hasSuffix(domain.withDotPrefix) + } +} diff --git a/Apps/Wikipedia/WMF Framework/String+HTML.swift b/Apps/Wikipedia/WMF Framework/String+HTML.swift new file mode 100644 index 0000000..0dd5b7d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/String+HTML.swift @@ -0,0 +1,30 @@ +import Foundation + +extension String { + /// Converts HTML string to NSAttributedString by handling a limited subset of tags. Optionally bolds an additional string based on matching. + /// + /// This is used instead of alloc/init'ing the attributed string with @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} because that approach proved to be slower and could't be called from a background thread. More info: https://developer.apple.com/documentation/foundation/nsattributedstring/1524613-initwithdata + /// + /// - Parameter textStyle: DynamicTextStyle to use with the resulting string + /// - Parameter boldWeight: Font weight for bolded parts of the string + /// - Parameter traitCollection: trait collection for font selection + /// - Parameter color: Text color + /// - Parameter handlingLinks: Whether or not link tags should be parsed and turned into links in the resulting string + /// - Parameter linkColor: Link text color + /// - Parameter handlingLists: Whether or not list tags should be parsed and styled in the resulting string + /// - Parameter handlingSuperSubscripts: whether or not super and subscript tags should be parsed and styled in the resulting string + /// - Parameter tagMapping: Lowercase string tag name to another lowercase string tag name - converts tags, for example, @{@"a":@"b"} will turn tags to tags + /// - Parameter additionalTagAttributes: Additional text attributes for given tags - lowercase tag name to attribute key/value pairs + /// - Returns: the resulting NSMutableAttributedString with styles applied to match the limited set of HTML tags that were parsed + public func byAttributingHTML(with textStyle: DynamicTextStyle, boldWeight: UIFont.Weight = .semibold, matching traitCollection: UITraitCollection, color: UIColor? = nil, handlingLinks: Bool = true, linkColor: UIColor? = nil, handlingLists: Bool = false, handlingSuperSubscripts: Bool = false, tagMapping: [String: String]? = nil, additionalTagAttributes: [String: [NSAttributedString.Key: Any]]? = nil) -> NSMutableAttributedString { + let font = UIFont.wmf_font(textStyle, compatibleWithTraitCollection: traitCollection) + let boldFont = UIFont.wmf_font(textStyle.with(weight: boldWeight), compatibleWithTraitCollection: traitCollection) + let italicFont = UIFont.wmf_font(textStyle.with(traits: [.traitItalic]), compatibleWithTraitCollection: traitCollection) + let boldItalicFont = UIFont.wmf_font(textStyle.with(weight: boldWeight, traits: [.traitItalic]), compatibleWithTraitCollection: traitCollection) + return (self as NSString).wmf_attributedStringFromHTML(with: font, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, color: color, linkColor: linkColor, handlingLinks: handlingLinks, handlingLists: handlingLists, handlingSuperSubscripts: handlingSuperSubscripts, tagMapping: tagMapping, additionalTagAttributes: additionalTagAttributes) + } + + public var removingHTML: String { + return (self as NSString).wmf_stringByRemovingHTML() + } +} diff --git a/Apps/Wikipedia/WMF Framework/String+JavaScript.swift b/Apps/Wikipedia/WMF Framework/String+JavaScript.swift new file mode 100644 index 0000000..38dbc2f --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/String+JavaScript.swift @@ -0,0 +1,11 @@ +import Foundation + +public extension String { + /// Can use ES6 backticks ` now instead of apostrophes. + /// Doing so means we *only* have to escape backticks instead of apostrophes, quotes and line breaks. + /// (May consider switching other native-to-JS messaging to do same later.) + var sanitizedForJavaScriptTemplateLiterals: String { + return replacingOccurrences(of: "([\\\\{}\\`])", with: "\\\\$1", options: [.regularExpression]) + } +} + diff --git a/Apps/Wikipedia/WMF Framework/String+LinkParsing.swift b/Apps/Wikipedia/WMF Framework/String+LinkParsing.swift new file mode 100644 index 0000000..5dbd77e --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/String+LinkParsing.swift @@ -0,0 +1,136 @@ +/// Detect Wiki namespace in strings. For example, detect that "/wiki/Talk:Dog" is a talk page and "/wiki/Special:ApiSandbox" is a special page +extension String { + static let namespaceRegex = try! NSRegularExpression(pattern: "^(.+?)_*:_*(.*)$") + // Assumes the input is the remainder of a /wiki/ path + func namespaceOfWikiResourcePath(with languageCode: String) -> PageNamespace { + guard let namespaceString = String.namespaceRegex.firstReplacementString(in: self) else { + return .main + } + return WikipediaURLTranslations.commonNamespace(for: namespaceString, in: languageCode) ?? .main + } + + public func namespaceAndTitleOfWikiResourcePath(with languageCode: String) -> (namespace: PageNamespace, title: String) { + guard let result = String.namespaceRegex.firstMatch(in: self) else { + return (.main, self) + } + let namespaceString = String.namespaceRegex.replacementString(for: result, in: self, offset: 0, template: "$1") + guard let namespace = WikipediaURLTranslations.commonNamespace(for: namespaceString, in: languageCode) else { + return (.main, self) + } + let title = String.namespaceRegex.replacementString(for: result, in: self, offset: 0, template: "$2") + return (namespace, title) + } + + static let wikiResourceRegex = try! NSRegularExpression(pattern: "^/wiki/(.+)$", options: .caseInsensitive) + var wikiResourcePath: String? { + return String.wikiResourceRegex.firstReplacementString(in: self) + } + + static let wResourceRegex = try! NSRegularExpression(pattern: "^/w/(.+)$", options: .caseInsensitive) + public var wResourcePath: String? { + return String.wResourceRegex.firstReplacementString(in: self) + } + + public var fullRange: NSRange { + return NSRange(startIndex.. String? { + if let namespaceString = String.namespaceRegex.firstReplacementString(in: self) { + let namespaceStringWithColon = "\(namespaceString):" + if namespaceOfWikiResourcePath(with: languageCode) == .talk { + return replacingOccurrences(of: namespaceStringWithColon, with: "") + } + } + return nil + } + +} + +/// Page title transformation +public extension String { + var percentEncodedPageTitleForPathComponents: String? { + return denormalizedPageTitle?.addingPercentEncoding(withAllowedCharacters: .encodeURIComponentAllowed) + } + + var normalizedPageTitle: String? { + return replacingOccurrences(of: "_", with: " ").precomposedStringWithCanonicalMapping + } + + var denormalizedPageTitle: String? { + return replacingOccurrences(of: " ", with: "_").precomposedStringWithCanonicalMapping + } + + var asTalkPageFragment: String? { + let denormalizedName = replacingOccurrences(of: " ", with: "_") + let unlinkedName = denormalizedName.replacingOccurrences(of: "[[", with: "").replacingOccurrences(of: "]]", with: "") + return unlinkedName.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.wmf_encodeURIComponentAllowed()) + } + + // assumes string is already normalized + var googleFormPercentEncodedPageTitle: String? { + return googleFormPageTitle?.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) + } + + var googleFormPageTitle: String? { + return replacingOccurrences(of: " ", with: "+").precomposedStringWithCanonicalMapping + } + + var unescapedNormalizedPageTitle: String? { + return removingPercentEncoding?.normalizedPageTitle + } + + var isReferenceFragment: Bool { + return contains("ref_") + } + + var isCitationFragment: Bool { + return contains("cite_note") + } + + var isEndNoteFragment: Bool { + return contains("endnote_") + } +} + +@objc extension NSString { + /// Deprecated - use namespace methods + @objc var wmf_isWikiResource: Bool { + return (self as String).wikiResourcePath != nil + } + + /// Deprecated - use swift methods + @objc var wmf_pathWithoutWikiPrefix: String? { + return (self as String).wikiResourcePath + } + + /// Deprecated - use swift methods + @objc var wmf_denormalizedPageTitle: String? { + return (self as String).denormalizedPageTitle + } + + /// Deprecated - use swift methods + @objc var wmf_normalizedPageTitle: String? { + return (self as String).normalizedPageTitle + } + + /// Deprecated - use swift methods + @objc var wmf_unescapedNormalizedPageTitle: String? { + return (self as String).unescapedNormalizedPageTitle + } + + /// Deprecated - use swift methods + @objc var wmf_isReferenceFragment: Bool { + return (self as String).isReferenceFragment + } + + /// Deprecated - use swift methods + @objc var wmf_isCitationFragment: Bool { + return (self as String).isCitationFragment + } + + /// Deprecated - use swift methods + @objc var wmf_isEndNoteFragment: Bool { + return (self as String).isEndNoteFragment + } +} diff --git a/Apps/Wikipedia/WMF Framework/SummaryExtensions.swift b/Apps/Wikipedia/WMF Framework/SummaryExtensions.swift new file mode 100644 index 0000000..63c9646 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/SummaryExtensions.swift @@ -0,0 +1,162 @@ +import CocoaLumberjackSwift + +extension WMFArticle { + func merge(_ article: WMFArticle) { + guard article.objectID != objectID else { + return + } + // merge important keys not set by the summary + let keysToMerge = [#keyPath(WMFArticle.savedDate), #keyPath(WMFArticle.placesSortOrder), #keyPath(WMFArticle.pageViews)] + for key in keysToMerge { + guard let valueToMerge = article.value(forKey: key) else { + continue + } + // keep the later date when both have date values + if let dateValueToMerge = valueToMerge as? Date, let dateValue = value(forKey: key) as? Date, dateValue > dateValueToMerge { + continue + } + // prefer existing values + if value(forKey: key) != nil { + continue + } + setValue(valueToMerge, forKey: key) + } + + if let articleReadingLists = article.readingLists { + addReadingLists(articleReadingLists) + } + + if let articlePreviewReadingLists = article.previewReadingLists { + addPreviewReadingLists(articlePreviewReadingLists) + } + + if article.isExcludedFromFeed { + isExcludedFromFeed = true + } + + let mergeViewedProperties: Bool + if let viewedDateToMerge = article.viewedDate { + if let existingViewedDate = viewedDate, existingViewedDate > viewedDateToMerge { + mergeViewedProperties = false + } else { + mergeViewedProperties = true + } + } else { + mergeViewedProperties = false + } + + if mergeViewedProperties { + viewedDate = article.viewedDate + viewedFragment = article.viewedFragment + viewedScrollPosition = article.viewedScrollPosition + wasSignificantlyViewed = article.wasSignificantlyViewed + } + } + + @objc public func update(withSummary summary: ArticleSummary) { + if let original = summary.original { + imageURLString = original.source + imageWidth = NSNumber(value: original.width) + imageHeight = NSNumber(value: original.height) + } else { + imageURLString = nil + imageWidth = NSNumber(value: 0) + imageHeight = NSNumber(value: 0) + } + + if let thumbnail = summary.thumbnail { + thumbnailURLString = thumbnail.source + thumbnailURL = thumbnail.url + } + + wikidataDescription = summary.articleDescription + wikidataID = summary.wikidataID + displayTitleHTML = summary.displayTitle ?? summary.title ?? "" + snippet = summary.extract?.wmf_summaryFromText() + + if let summaryCoordinate = summary.coordinates { + coordinate = CLLocationCoordinate2D(latitude: summaryCoordinate.lat, longitude: summaryCoordinate.lon) + } else { + coordinate = nil + } + if let id = summary.id { + pageID = NSNumber(value: id) + } else { + pageID = nil + } + if let timestamp = summary.timestamp { + lastModifiedDate = DateFormatter.wmf_iso8601()?.date(from: timestamp) + } else { + lastModifiedDate = nil + } + } +} + +extension NSManagedObjectContext { + @objc public func wmf_createOrUpdateArticleSummmaries(withSummaryResponses summaryResponses: [WMFInMemoryURLKey: ArticleSummary]) throws -> [WMFInMemoryURLKey: WMFArticle] { + guard !summaryResponses.isEmpty else { + return [:] + } + var keys: [WMFInMemoryURLKey] = [] + var reverseRedirectedKeys: [WMFInMemoryURLKey: WMFInMemoryURLKey] = [:] + keys.reserveCapacity(summaryResponses.count) + for (key, summary) in summaryResponses { + guard + let summaryKey = summary.key, + key != summaryKey // find the mismatched keys + else { + keys.append(key) + continue + } + reverseRedirectedKeys[summaryKey] = key + keys.append(summaryKey) + do { + let articlesWithKey = try fetchArticles(with: key.url) + let articlesWithSummaryKey = try fetchArticles(with: summaryKey.url) + guard let canonicalArticle = articlesWithSummaryKey.first ?? articlesWithKey.first else { + continue + } + for article in articlesWithKey { + canonicalArticle.merge(article) + delete(article) + } + for article in articlesWithSummaryKey { + canonicalArticle.merge(article) + delete(article) + } + canonicalArticle.key = summaryKey.databaseKey + canonicalArticle.variant = summaryKey.languageVariantCode + } catch let error { + DDLogError("Error fetching articles for merge: \(error)") + } + } + var keysToCreate = Set(keys) + var articles: [WMFInMemoryURLKey: WMFArticle] = [:] + articles.reserveCapacity(keys.count) + let fetchedArticles = try self.fetchArticlesWithInMemoryURLKeys(keys) + for articleToUpdate in fetchedArticles { + guard let articleKey = articleToUpdate.inMemoryKey else { + continue + } + let requestedKey = reverseRedirectedKeys[articleKey] ?? articleKey + guard let result = summaryResponses[requestedKey] else { + articles[requestedKey] = articleToUpdate + continue + } + articleToUpdate.update(withSummary: result) + articles[requestedKey] = articleToUpdate + keysToCreate.remove(articleKey) + } + for key in keysToCreate { + let requestedKey = reverseRedirectedKeys[key] ?? key + guard let result = summaryResponses[requestedKey], // responses are by requested key + let article = self.createArticle(with: key.url) else { // article should have redirected key + continue + } + article.update(withSummary: result) + articles[requestedKey] = article + } + try self.save() + return articles + } +} diff --git a/Apps/Wikipedia/WMF Framework/SurveyAnnouncementsController.swift b/Apps/Wikipedia/WMF Framework/SurveyAnnouncementsController.swift new file mode 100644 index 0000000..88e39c5 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/SurveyAnnouncementsController.swift @@ -0,0 +1,119 @@ +import Foundation + +@objc(WMFSurveyAnnouncementsController) +public final class SurveyAnnouncementsController: NSObject { + + @objc public static let shared = SurveyAnnouncementsController() + + private let queue = DispatchQueue(label: "SurveyAnnouncementsQueue") + + // ex: 'en.wikipedia.org' + typealias AnnouncementsHost = String + private var announcementsByHost: [AnnouncementsHost: [WMFAnnouncement]] = [:] + + public struct SurveyAnnouncementResult { + + public let campaignIdentifier: String + public let announcement: WMFAnnouncement + public let actionURLString: String + public let displayDelay: TimeInterval + } + + public private(set) var failureDeterminingABTestBucket = false + + @objc public func setAnnouncements(_ announcements: [WMFAnnouncement], forSiteURL siteURL: URL, dataStore: MWKDataStore) { + + guard let components = URLComponents(url: siteURL, resolvingAgainstBaseURL: false), + let host = components.host else { + return + } + + let surveyAnnouncements = announcements.filter { $0.announcementType == .survey } + + queue.sync { + announcementsByHost[host] = surveyAnnouncements + } + + // assign and persist ab test bucket & percentage + // this works for now since we only have one experiment for this release but will likely need to change as we expand + if let articleAsLivingDocAnnouncement = surveyAnnouncements.first(where: { ($0.identifier?.hasPrefix("IOSAAALDSURVEY")) ?? false }), + let percentage = articleAsLivingDocAnnouncement.percentReceivingExperiment { + + do { + if dataStore.abTestsController.percentageForExperiment(.articleAsLivingDoc) == nil { + try dataStore.abTestsController.setPercentage(percentage, forExperiment: .articleAsLivingDoc) + } + + try dataStore.abTestsController.determineBucketForExperiment(.articleAsLivingDoc, withPercentage: percentage) + failureDeterminingABTestBucket = false + } catch { + failureDeterminingABTestBucket = true + } + } + } + + private func getAnnouncementsForSiteURL(_ siteURL: URL) -> [WMFAnnouncement]? { + guard let components = URLComponents(url: siteURL, resolvingAgainstBaseURL: false), + let host = components.host else { + return nil + } + + var announcements: [WMFAnnouncement]? = [] + queue.sync { + announcements = announcementsByHost[host] + } + + return announcements + } + + // Use for determining whether to show user a survey prompt or not. + // Considers domain, campaign start/end dates, and whether articleURL is within allowlist of article titles in campaign + public func activeSurveyAnnouncementResultForArticleURL(_ articleURL: URL) -> SurveyAnnouncementResult? { + + guard let articleTitle = articleURL.wmf_title?.denormalizedPageTitle, let siteURL = articleURL.wmf_site else { + return nil + } + + guard let announcements = getAnnouncementsForSiteURL(siteURL) else { + return nil + } + + for announcement in announcements { + + guard let startTime = announcement.startTime, + let endTime = announcement.endTime, + let domain = announcement.domain, + let articleTitles = announcement.articleTitles, + let displayDelay = announcement.displayDelay, + let components = URLComponents(url: siteURL, resolvingAgainstBaseURL: false), + let host = components.host, + let identifier = announcement.identifier, + let normalizedArticleTitle = articleTitle.normalizedPageTitle, + let actionURLString = announcement.actionURLString else { + continue + } + + let now = Date() + + if now > startTime && now < endTime && host == domain, articleTitles.contains(normalizedArticleTitle) { + return SurveyAnnouncementResult(campaignIdentifier: identifier, announcement: announcement, actionURLString: actionURLString, displayDelay: displayDelay.doubleValue) + } + } + + return nil + } + + public func userHasSeenSurveyPrompt(forCampaignIdentifier identifier: String) -> Bool { + // Note any value indicates survey was seen. + // true = they tapped through to the Google survey, false = they dismissed the survey prompt. + guard UserDefaults.standard.object(forKey: identifier) == nil else { + return true + } + + return false + } + + public func markSurveyAnnouncementAnswer(_ answer: Bool, campaignIdentifier: String) { + UserDefaults.standard.setValue(answer, forKey: campaignIdentifier) + } +} diff --git a/Apps/Wikipedia/WMF Framework/Theme.swift b/Apps/Wikipedia/WMF Framework/Theme.swift new file mode 100644 index 0000000..17ed6a2 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Theme.swift @@ -0,0 +1,1008 @@ +import Foundation +import SystemConfiguration +import Components + +public extension UIColor { + @objc(initWithHexInteger:alpha:) + convenience init(_ hex: Int, alpha: CGFloat) { + let r = CGFloat((hex & 0xFF0000) >> 16) / 255.0 + let g = CGFloat((hex & 0xFF00) >> 8) / 255.0 + let b = CGFloat(hex & 0xFF) / 255.0 + self.init(red: r, green: g, blue: b, alpha: alpha) + } + + @objc(initWithHexInteger:) + convenience init(_ hex: Int) { + self.init(hex, alpha: 1) + } + + @objc class func wmf_colorWithHex(_ hex: Int) -> UIColor { + return UIColor(hex) + } + + // `initWithHexString:alpha:` should almost never be used. `initWithHexInteger:alpha:` is preferred. + @objc(initWithHexString:alpha:) + convenience init(_ hexString: String, alpha: CGFloat = 1.0) { + let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + guard hex.count == 6, + let int = Scanner(string: hex).scanInt32(representation: .hexadecimal), + int != UINT32_MAX else { + assertionFailure("Unexpected issue scanning hex string: \(hexString)") + self.init(white: 0, alpha: alpha) + return + } + self.init(Int(int), alpha: alpha) + } + + // Make colors accessible to @objc + @objc static var wmf_blue_700: UIColor { + return .blue700 + } + + @objc static var wmf_blue_300: UIColor { + return .blue300 + } + + @objc static var wmf_blue_600: UIColor { + return .blue600 + } + + @objc static var wmf_yellow_600: UIColor { + return .yellow600 + } + @objc static var wmf_red_600: UIColor { + return .red600 + } + + @objc static var wmf_gray_400: UIColor { + return .gray400 + } + + @objc static var wmf_green_600: UIColor { + return .green600 + } + + @objc static var wmf_purple: UIColor { + return .purple600 + } + + @objc static var wmf_orange: UIColor { + return .orange600 + } + + @objc func wmf_hexStringIncludingAlpha(_ includeAlpha: Bool) -> String { + var r: CGFloat = 0 + var g: CGFloat = 0 + var b: CGFloat = 0 + var a: CGFloat = 0 + + getRed(&r, green: &g, blue: &b, alpha: &a) + + var hexString = String(format: "%02X%02X%02X", Int(255.0 * r), Int(255.0 * g), Int(255.0 * b)) + if includeAlpha { + hexString = hexString.appendingFormat("%02X", Int(255.0 * a)) + } + return hexString + } + + @objc var wmf_hexString: String { + return wmf_hexStringIncludingAlpha(false) + } +} + +@objc(WMFColors) +public class Colors: NSObject { + fileprivate static let light = Colors( + identifier: .light) + + fileprivate static let sepia = Colors(identifier: .sepia) + + fileprivate static let dark = Colors(identifier: .dark) + + fileprivate static let black = Colors(identifier: .black) + + fileprivate static let widgetLight = Colors(identifier: .widgetLight) + + fileprivate static let widgetDark = Colors(identifier: .widgetDark) + + public let identifier: Identifier + + @objc public var baseBackground: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray200 + case .sepia: + return .beige400 + case .dark, .black, .widgetDark: + return .gray800 + } + } + + @objc public var midBackground: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray100 + case .sepia: + return .beige300 + case .dark, .black, .widgetDark: + return .gray700 + } + } + + @objc public var subCellBackground: UIColor { + switch identifier { + case .light: + return .white + case .sepia: + return .beige300 + case .dark, .black: + return .gray700 + case .widgetLight, .widgetDark: + return .clear + } + } + + @objc public var paperBackground: UIColor { + switch identifier { + case .light, .widgetLight: + return .white + case .sepia: + return .beige100 + case .dark: + return .gray675 + case .black, .widgetDark: + return .black + } + } + + @objc public var popoverBackground: UIColor { + switch identifier { + case .light, .sepia: + return .white + case .dark: + return .gray800 + case .black: + return .gray700 + case .widgetLight, .widgetDark: + return .clear + } + } + + @objc public var chromeBackground: UIColor { + switch identifier { + case .light: + return .white + case .sepia: + return .beige100 + case .dark: + return .gray700 + case .black: + return .gray700 + case .widgetLight, .widgetDark: + return .clear + } + } + + @objc public var chromeShadow: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray400 + case .sepia: + return .taupe200 + case .dark, .widgetDark: + return .gray650 + case .black: + return .gray675 + } + } + + @objc public var overlayBackground: UIColor { + switch identifier { + case .light, .widgetLight: + return .black.withAlphaComponent(0.5) + case .sepia: + return .taupe600.withAlphaComponent(0.6) + default: + return .black.withAlphaComponent(0.75) + } + } + + @objc public var batchSelectionBackground: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .blue100 + case .dark, .black, .widgetDark: + return .blue700 + + } + } + + @objc public var referenceHighlightBackground: UIColor { + switch identifier { + case .light, .sepia, .dark, .widgetLight: + return .clear + case .black, .widgetDark: + return .white.withAlphaComponent(0.2) + } + } + + @objc public var hintBackground: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .blue100 + case .dark, .widgetDark: + return .gray800 + case .black: + return .gray650 + } + } + + @objc public var hintWarningText: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .gray700 + case .dark, .black, .widgetDark: + return .yellow600 + } + } + + @objc public var hintWarningBackground: UIColor { + switch identifier { + case .light, .sepia: + return .orange600 + case .dark, .black: + return .gray700 + default: + return .clear + } + } + + @objc public var animationBackground: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .gray150 + case .dark, .black, .widgetDark: + return .gray700 + } + } + + @objc public var overlayText: UIColor { + return .gray600 + } + + @objc public var primaryText: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .gray700 + case .dark, .black, .widgetDark: + return .gray100 + } + } + + @objc public var secondaryText: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray500 + case .sepia: + return .taupe600 + default: + return .gray300 + } + } + + @objc public var tertiaryText: UIColor { + switch identifier { + case .light: + return .gray500 + case .sepia: + return .taupe600 + default: + return .gray300 + + } + } + + @objc public var disabledText: UIColor { + switch identifier { + case .light: + return .gray500 + case .sepia: + return .taupe600 + default: + return .gray300 + } + } + + @objc public var disabledLink: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray600 + case .sepia: + return .gray500 + case .dark, .black, .widgetDark: + return .gray400 + } + } + + @objc public var chromeText: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .gray700 + case .dark, .black, .widgetDark: + return .gray100 + } + } + + @objc public var link: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .blue600 + case .dark, .black, .widgetDark: + return .blue300 + } + } + + @objc public var accent: UIColor { + switch identifier { + default: + return .green600 + } + } + + @objc public var secondaryAction: UIColor { + return .blue700 + } + + @objc public var destructive: UIColor { + switch identifier { + case .sepia: + return .red700 + default: + return .red600 + } + } + @objc public var warning: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .orange600 + case .dark, .black, .widgetDark: + return .yellow600 + } + } + @objc public var error: UIColor { + switch identifier { + case .sepia: + return .red700 + default: + return .red600 + } + } + @objc public var unselected: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray400 + case .sepia: + return .taupe600 + case .dark, .black, .widgetDark: + return .gray300 + } + } + + @objc public var border: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray400 + case .sepia: + return .taupe200 + case .dark, .widgetDark: + return .gray650 + case .black: + return .gray675 + } + } + + @objc public var shadow: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray200 + case .sepia: + return .taupe200 + case .dark, .widgetDark: + return .gray800 + case .black: + return .gray700 + } + } + + public var cardBackground: UIColor { + switch identifier { + case .light: + return .white + case .sepia: + return .beige300 + case .dark, .black: + return .gray700 + case .widgetLight, .widgetDark: + return .clear + } + } + + public var selectedCardBackground: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray100 + case .sepia: + return .beige400 + case .dark, .widgetDark: + return .gray700 + case .black: + return .gray675 + } + } + + @objc public var cardBorder: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray100 + case .sepia: + return .taupe200 + case .dark, .widgetDark: + return .gray650 + case .black: + return .gray675 + } + } + + @objc public var cardShadow: UIColor { + switch identifier { + case .light: + return .gray700 + default: + return .clear + } + } + + @objc public var cardButtonBackground: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray100 + case .sepia: + return .beige300 + case .dark, .black, .widgetDark: + return .gray650 + } + } + + @objc public var cardButtonSelectedBackground: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray100 + case .sepia: + return .taupe200 + case .dark, .widgetDark: + return .gray650 + case .black: + return .gray675 + } + } + + @objc public var icon: UIColor? { + switch identifier { + case .sepia: + return .taupe600 + case .dark, .black: + return .gray300 + default: + return nil + } + } + + @objc public var iconBackground: UIColor? { + switch identifier { + case .sepia: + return .beige400 + case .dark, .black: + return .gray675 + default: + return nil + } + } + + @objc public var searchFieldBackground: UIColor { + return .darkSearchFieldBackground + } + + @objc public var keyboardBarSearchFieldBackground: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .gray200 + case .dark, .black, .widgetDark: + return .gray650 + } + } + + @objc public var rankGradientStart: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .blue600 + case .dark, .black, .widgetDark: + return .blue300 + } + } + + @objc public var rankGradientEnd: UIColor { + return .green600 + } + + @objc public var rankGradient: Gradient { + return Gradient(startColor: rankGradientStart, endColor: rankGradientEnd) + } + + @objc public var blurEffectStyle: UIBlurEffect.Style { + switch identifier { + case .light, .sepia, .widgetLight: + return .extraLight + case .dark, .black, .widgetDark: + return .dark + } + } + + @objc public var blurEffectBackground: UIColor { + switch identifier { + case .black, .dark, .widgetDark: + return .gray300.withAlphaComponent(0.55) + default: + return .clear + } + } + + @objc public var tagText: UIColor { + switch identifier { + case .light: + return .blue600 + default: + return .white + } + } + + @objc public var tagBackground: UIColor { + switch identifier { + case .light: + return .blue600.withAlphaComponent(0.1) + default: + return .blue300 + + } + } + + @objc public var tagSelectedBackground: UIColor { + switch identifier { + case .light: + return .blue600.withAlphaComponent(0.25) + default: + return .blue600 + + } + } + + @objc public var distanceBorder: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray400 + case .sepia: + return .taupe600 + case .dark, .black, .widgetDark: + return .gray300 + } + } + + @objc public var descriptionBackground: UIColor { + switch identifier { + case .light: + return .yellow600 + case .sepia, .widgetLight: + return .orange600 + case .dark, .black, .widgetDark: + return .blue300 + } + } + + @objc public var descriptionWarning: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .orange600 + case .dark, .black, .widgetDark: + return .yellow600 + } + } + + @objc public var pageIndicator: UIColor { + return .blue100 + } + + @objc public var pageIndicatorCurrent: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .blue600 + case .dark, .black, .widgetDark: + return .blue300 + } + } + + @objc public var unreadIndicator: UIColor { + return .green600 + } + + @objc public var refreshControlTint: UIColor { + return secondaryText + } + + @objc public var inputAccessoryBackground: UIColor { + switch identifier { + case .light: + return .white + case .sepia: + return .beige300 + case .dark, .black: + return .gray700 + case .widgetLight, .widgetDark: + return .clear + } + } + + @objc public var inputAccessoryButtonTint: UIColor { + switch identifier { + case .light, .sepia, .widgetLight: + return .gray600 + case .dark, .black, .widgetDark: + return .gray100 + } + } + + @objc public var inputAccessoryButtonSelectedTint: UIColor { + return primaryText + } + + @objc public var inputAccessoryButtonSelectedBackgroundColor: UIColor { + return baseBackground + } + + public var diffTextAdd: UIColor { + switch identifier { + case .light: + return .gray700 + default: + return .green600 + } + } + + public var diffTextDelete: UIColor { + switch identifier { + case .light: + return .gray700 + case .sepia: + return.red700 + case .dark, .black: + return .red600 + default: + return .clear + } + } + + public var diffHighlightAdd: UIColor? { + switch identifier { + case .light: + return .green100 + default: + return nil + } + } + + public var diffHighlightDelete: UIColor? { + switch identifier { + case .light: + return .red100 + default: + return nil + } + } + + public var diffStrikethroughColor: UIColor { + switch identifier { + case .light: + return .gray700 + case .sepia: + return .red700 + case .dark, .black: + return .red600 + default: + return .clear + } + } + + public var diffContextItemBackground: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray100 + case .sepia: + return .beige300 + case .dark, .black, .widgetDark: + return .gray700 + } + } + + public var diffContextItemBorder: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray400 + case .sepia: + return .taupe200 + case .dark, .widgetDark: + return .gray650 + case .black: + return .gray675 + } + } + + public var diffMoveParagraphBackground: UIColor { + switch identifier { + case .light, .widgetLight: + return .gray100 + case .sepia: + return .beige300 + case .dark, .black, .widgetDark: + return .gray700 + } + } + + public var diffCompareAccent: UIColor { + return .orange600.withAlphaComponent(0.3) + } + + public var diffCompareChangeHeading: UIColor { + switch identifier { + case .light: + return .white + case .sepia: + return .beige100 + case .black, .dark: + return .black + default: + return .clear + } + } + + public var talkPageCoffeRollBackground: UIColor { + switch identifier { + case .light: + return .beige100 + case .sepia: + return .beige400 + case .dark, .black: + return .gray800 + default: + return .clear + } + } + + init(identifier: Identifier) { + self.identifier = identifier + } + + public enum Identifier { + case light + case sepia + case dark + case black + case widgetLight + case widgetDark + } + +} + +@objc(WMFTheme) +public class Theme: NSObject { + + @objc public static let standard = Theme.light + + @objc public let colors: Colors + + @objc public let isDark: Bool + + @objc public let hasInputAccessoryShadow: Bool + + @objc public var preferredStatusBarStyle: UIStatusBarStyle { + return isDark ? .lightContent : .default + } + + @objc public var scrollIndicatorStyle: UIScrollView.IndicatorStyle { + return isDark ? .white : .black + } + + @objc public var blurEffectStyle: UIBlurEffect.Style { + return isDark ? .dark : .light + } + + @objc public var keyboardAppearance: UIKeyboardAppearance { + return isDark ? .dark : .light + } + + @objc public lazy var navigationBarBackgroundImage: UIImage = { + return UIImage.wmf_image(from: colors.paperBackground) + }() + + @objc public lazy var sheetNavigationBarBackgroundImage: UIImage = { + return UIImage.wmf_image(from: colors.chromeBackground) + }() + + @objc public lazy var editorNavigationBarBackgroundImage: UIImage = { + return UIImage.wmf_image(from: colors.inputAccessoryBackground) + }() + + @objc public var navigationBarShadowImage: UIImage { + return clearImage + } + + @objc public lazy var clearImage: UIImage = { + return #imageLiteral(resourceName: "transparent-pixel") + }() + + static let tabBarItemBadgeParagraphStyle: NSParagraphStyle = { + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.firstLineHeadIndent = 0.4 + return paragraphStyle + }() + + static let tabBarItemFont: UIFont = { + return UIFont.systemFont(ofSize: 12) + }() + + public lazy var tabBarItemBadgeTextAttributes: [NSAttributedString.Key: Any] = { + return [NSAttributedString.Key.foregroundColor: colors.chromeBackground, NSAttributedString.Key.paragraphStyle: Theme.tabBarItemBadgeParagraphStyle] + }() + + public lazy var tabBarTitleTextAttributes: [NSAttributedString.Key: Any] = { + return [.foregroundColor: colors.secondaryText, .font: Theme.tabBarItemFont] + }() + + public lazy var tabBarSelectedTitleTextAttributes: [NSAttributedString.Key: Any] = { + return [.foregroundColor: colors.link, .font: Theme.tabBarItemFont] + }() + + public static let exploreCardCornerRadius: CGFloat = 10 + + static func roundedRectImage(with color: UIColor, cornerRadius: CGFloat, width: CGFloat? = nil, height: CGFloat? = nil) -> UIImage? { + let minDimension = 2 * cornerRadius + 1 + let rect = CGRect(x: 0, y: 0, width: width ?? minDimension, height: height ?? minDimension) + let scale = UIScreen.main.scale + UIGraphicsBeginImageContextWithOptions(rect.size, false, scale) + guard let context = UIGraphicsGetCurrentContext() else { + return nil + } + context.setFillColor(color.cgColor) + let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius) + path.fill() + let capInsets = UIEdgeInsets(top: cornerRadius, left: cornerRadius, bottom: cornerRadius, right: cornerRadius) + let image = UIGraphicsGetImageFromCurrentImageContext()?.resizableImage(withCapInsets: capInsets) + UIGraphicsEndImageContext() + return image + } + + @objc public lazy var searchFieldBackgroundImage: UIImage? = { + return Theme.roundedRectImage(with: colors.searchFieldBackground, cornerRadius: 10, height: 36) + }() + + @objc public lazy var navigationBarTitleTextAttributes: [NSAttributedString.Key: Any] = { + return [NSAttributedString.Key.foregroundColor: colors.chromeText] + }() + + public static let dimmedImageOpacity: CGFloat = 0.65 + @objc public let imageOpacity: CGFloat + @objc public let cardBorderWidthInPixels: Int + @objc public let cardShadowOpacity: Float + + @objc public let name: String + @objc public let displayName: String + public let analyticsName: String + public let webName: String + + @objc public let multiSelectIndicatorImage: UIImage? + fileprivate static let lightMultiSelectIndicator = UIImage(named: "selected", in: Bundle.main, compatibleWith:nil) + fileprivate static let darkMultiSelectIndicator = UIImage(named: "selected-dark", in: Bundle.main, compatibleWith:nil) + + private static let defaultCardBorderWidthInPixels: Int = 1 + private static let lightCardBorderWidthInPixels: Int = { + return DeviceInfo.shared.isOlderDevice ? 4 : defaultCardBorderWidthInPixels + }() + + private static let defaultCardShadowOpacity: Float = { + return DeviceInfo.shared.isOlderDevice ? 0 : 0.13 + }() + + @objc public static let defaultThemeName = "standard" + @objc public static let defaultAnalyticsThemeName = "default" + + private static let darkThemePrefix = "dark" + private static let blackThemePrefix = "black" + + @objc public static func isDefaultThemeName(_ name: String?) -> Bool { + guard let name = name else { + return true + } + return name == defaultThemeName + } + + @objc public static func isDarkThemeName(_ name: String?) -> Bool { + guard let name = name else { + return false + } + return name.hasPrefix(darkThemePrefix) || name.hasPrefix(blackThemePrefix) + } + + @objc public static let light = Theme(colors: .light, imageOpacity: 1, cardBorderWidthInPixels: Theme.lightCardBorderWidthInPixels, cardShadowOpacity: defaultCardShadowOpacity, multiSelectIndicatorImage: Theme.lightMultiSelectIndicator, isDark: false, hasInputAccessoryShadow: true, name: "light", displayName: WMFLocalizedString("theme-light-display-name", value: "Light", comment: "Light theme name presented to the user"), analyticsName: "default", webName: "light") + + @objc public static let sepia = Theme(colors: .sepia, imageOpacity: 1, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: Theme.lightMultiSelectIndicator, isDark: false, hasInputAccessoryShadow: false, name: "sepia", displayName: WMFLocalizedString("theme-sepia-display-name", value: "Sepia", comment: "Sepia theme name presented to the user"), analyticsName: "sepia", webName: "sepia") + + @objc public static let dark = Theme(colors: .dark, imageOpacity: 1, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: Theme.darkMultiSelectIndicator, isDark: true, hasInputAccessoryShadow: false, name: darkThemePrefix, displayName: WMFLocalizedString("theme-dark-display-name", value: "Dark", comment: "Dark theme name presented to the user"), analyticsName: "dark", webName: "dark") + + @objc public static let darkDimmed = Theme(colors: .dark, imageOpacity: Theme.dimmedImageOpacity, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: Theme.darkMultiSelectIndicator, isDark: true, hasInputAccessoryShadow: false, name: "\(darkThemePrefix)-dimmed", displayName: Theme.dark.displayName, analyticsName: "dark", webName: "dark") + + @objc public static let black = Theme(colors: .black, imageOpacity: 1, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: Theme.darkMultiSelectIndicator, isDark: true, hasInputAccessoryShadow: false, name: blackThemePrefix, displayName: WMFLocalizedString("theme-black-display-name", value: "Black", comment: "Black theme name presented to the user"), analyticsName: "black", webName: "black") + + @objc public static let blackDimmed = Theme(colors: .black, imageOpacity: Theme.dimmedImageOpacity, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: Theme.darkMultiSelectIndicator, isDark: true, hasInputAccessoryShadow: false, name: "\(blackThemePrefix)-dimmed", displayName: Theme.black.displayName, analyticsName: "black", webName: "black") + + @objc public static let widgetLight = Theme(colors: .widgetLight, imageOpacity: 1, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: nil, isDark: false, hasInputAccessoryShadow: false, name: "widget-light", displayName: "", analyticsName: "", webName: "light") + + @objc public static let widgetDark = Theme(colors: .widgetDark, imageOpacity: 1, cardBorderWidthInPixels: Theme.defaultCardBorderWidthInPixels, cardShadowOpacity: 0, multiSelectIndicatorImage: nil, isDark: false, hasInputAccessoryShadow: false, name: "widget-dark", displayName: "", analyticsName: "", webName: "black") + + public class func widgetThemeCompatible(with traitCollection: UITraitCollection) -> Theme { + return traitCollection.userInterfaceStyle == .dark ? Theme.widgetDark : Theme.widgetLight + } + + init(colors: Colors, imageOpacity: CGFloat, cardBorderWidthInPixels: Int, cardShadowOpacity: Float, multiSelectIndicatorImage: UIImage?, isDark: Bool, hasInputAccessoryShadow: Bool, name: String, displayName: String, analyticsName: String, webName: String) { + self.colors = colors + self.imageOpacity = imageOpacity + self.name = name + self.displayName = displayName + self.multiSelectIndicatorImage = multiSelectIndicatorImage + self.isDark = isDark + self.hasInputAccessoryShadow = hasInputAccessoryShadow + self.cardBorderWidthInPixels = cardBorderWidthInPixels + self.cardShadowOpacity = cardShadowOpacity + self.analyticsName = analyticsName + self.webName = webName + } + + fileprivate static let themesByName = [Theme.light.name: Theme.light, Theme.dark.name: Theme.dark, Theme.sepia.name: Theme.sepia, Theme.darkDimmed.name: Theme.darkDimmed, Theme.black.name: Theme.black, Theme.blackDimmed.name: Theme.blackDimmed] + + @objc(withName:) + public class func withName(_ name: String?) -> Theme? { + guard let name = name else { + return nil + } + return themesByName[name] + } + + @objc public func withDimmingEnabled(_ isDimmingEnabled: Bool) -> Theme { + guard let baseName = name.components(separatedBy: "-").first else { + return self + } + let adjustedName = isDimmingEnabled ? "\(baseName)-dimmed" : baseName + return Theme.withName(adjustedName) ?? self + } +} + +@objc(WMFThemeable) +public protocol Themeable: AnyObject { + @objc(applyTheme:) + func apply(theme: Theme) // this might be better as a var theme: Theme { get set } - common VC superclasses could check for viewIfLoaded and call an update method in the setter. This would elminate the need for the viewIfLoaded logic in every applyTheme: +} + +// Use for SwiftUI environment objects +public final class ObservableTheme: ObservableObject { + @Published public var theme: Theme + + public init(theme: Theme) { + self.theme = theme + } +} diff --git a/Apps/Wikipedia/WMF Framework/ThemeableTextField.swift b/Apps/Wikipedia/WMF Framework/ThemeableTextField.swift new file mode 100644 index 0000000..4c65676 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/ThemeableTextField.swift @@ -0,0 +1,92 @@ +import UIKit + +@objc(WMFThemeableTextField) +open class ThemeableTextField: UITextField, Themeable { + var theme = Theme.light + @objc public var isUnderlined = true + private var clearButton: UIButton? + public var clearAccessibilityLabel: String? { + get { + return clearButton?.accessibilityLabel + } set { + clearButton?.accessibilityLabel = newValue + } + } + + func setup() { + let image = #imageLiteral(resourceName: "clear-mini") + clearButton = UIButton(frame: CGRect(origin: .zero, size: image.size)) + clearButton?.setImage(image, for: .normal) + clearButton?.addTarget(self, action: #selector(clear), for: .touchUpInside) + rightView = clearButton + rightViewMode = .whileEditing + textAlignment = .natural + } + + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + fileprivate var _placeholder: String? + override open var placeholder: String? { + didSet { + _placeholder = placeholder + guard let newPlaceholder = placeholder else { + return + } + attributedPlaceholder = NSAttributedString(string: newPlaceholder, attributes: [NSAttributedString.Key.foregroundColor: theme.colors.secondaryText]) + } + } + + fileprivate func _clear() { + text = nil + sendActions(for: .editingChanged) + } + + @objc(clear) + fileprivate func clear() { + guard let shouldClear = delegate?.textFieldShouldClear?(self) else { + _clear() + return + } + + guard shouldClear else { + return + } + + _clear() + } + + @objc(applyTheme:) + public func apply(theme: Theme) { + self.theme = theme + rightView?.tintColor = theme.colors.tertiaryText + backgroundColor = theme.colors.paperBackground + textColor = theme.colors.primaryText + placeholder = _placeholder + keyboardAppearance = theme.keyboardAppearance + borderStyle = .none + if isUnderlined { + layer.masksToBounds = false + layer.shadowColor = theme.colors.tertiaryText.cgColor + layer.shadowOffset = CGSize(width: 0.0, height: 1.0) + layer.shadowOpacity = 1.0 + layer.shadowRadius = 0.0 + } else { + layer.masksToBounds = true + layer.shadowColor = nil + layer.shadowOffset = CGSize.zero + layer.shadowOpacity = 0.0 + layer.shadowRadius = 0.0 + } + + } +} + + diff --git a/Apps/Wikipedia/WMF Framework/Third Party/EXTScope.h b/Apps/Wikipedia/WMF Framework/Third Party/EXTScope.h new file mode 100644 index 0000000..c7a51ef --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/EXTScope.h @@ -0,0 +1,116 @@ +// +// EXTScope.h +// extobjc +// +// Created by Justin Spahr-Summers on 2011-05-04. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import + +/** + * \@onExit defines some code to be executed when the current scope exits. The + * code must be enclosed in braces and terminated with a semicolon, and will be + * executed regardless of how the scope is exited, including from exceptions, + * \c goto, \c return, \c break, and \c continue. + * + * Provided code will go into a block to be executed later. Keep this in mind as + * it pertains to memory management, restrictions on assignment, etc. Because + * the code is used within a block, \c return is a legal (though perhaps + * confusing) way to exit the cleanup block early. + * + * Multiple \@onExit statements in the same scope are executed in reverse + * lexical order. This helps when pairing resource acquisition with \@onExit + * statements, as it guarantees teardown in the opposite order of acquisition. + * + * @note This statement cannot be used within scopes defined without braces + * (like a one line \c if). In practice, this is not an issue, since \@onExit is + * a useless construct in such a case anyways. + */ +#define onExit \ +ext_keywordify \ +__strong ext_cleanupBlock_t metamacro_concat(ext_exitBlock_, __LINE__) __attribute__((cleanup(ext_executeCleanupBlock), unused)) = ^ + +/** + * Creates \c __weak shadow variables for each of the variables provided as + * arguments, which can later be made strong again with #strongify. + * + * This is typically used to weakly reference variables in a block, but then + * ensure that the variables stay alive during the actual execution of the block + * (if they were live upon entry). + * + * See #strongify for an example of usage. + */ +#define weakify(...) \ +ext_keywordify \ +metamacro_foreach_cxt(ext_weakify_,, __weak, __VA_ARGS__) + +/** + * Like #weakify, but uses \c __unsafe_unretained instead, for targets or + * classes that do not support weak references. + */ +#define unsafeify(...) \ +ext_keywordify \ +metamacro_foreach_cxt(ext_weakify_,, __unsafe_unretained, __VA_ARGS__) + +/** + * Strongly references each of the variables provided as arguments, which must + * have previously been passed to #weakify. + * + * The strong references created will shadow the original variable names, such + * that the original names can be used without issue (and a significantly + * reduced risk of retain cycles) in the current scope. + * + * @code + + id foo = [[NSObject alloc] init]; + id bar = [[NSObject alloc] init]; + + @weakify(foo, bar); + + // this block will not keep 'foo' or 'bar' alive + BOOL (^matchesFooOrBar)(id) = ^ BOOL (id obj){ + // but now, upon entry, 'foo' and 'bar' will stay alive until the block has + // finished executing + @strongify(foo, bar); + + return [foo isEqual:obj] || [bar isEqual:obj]; + }; + + * @endcode + */ +#define strongify(...) \ +ext_keywordify \ +_Pragma("clang diagnostic push") \ +_Pragma("clang diagnostic ignored \"-Wshadow\"") \ +metamacro_foreach(ext_strongify_,, __VA_ARGS__) \ +_Pragma("clang diagnostic pop") + +/*** implementation details follow ***/ +typedef void (^ext_cleanupBlock_t)(void); + +void ext_executeCleanupBlock (__strong ext_cleanupBlock_t *block); + +#define ext_weakify_(INDEX, CONTEXT, VAR) \ +CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR); + +#define ext_strongify_(INDEX, VAR) \ +__strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_); + +// Details about the choice of backing keyword: +// +// The use of @try/@catch/@finally can cause the compiler to suppress +// return-type warnings. +// The use of @autoreleasepool {} is not optimized away by the compiler, +// resulting in superfluous creation of autorelease pools. +// +// Since neither option is perfect, and with no other alternatives, the +// compromise is to use @autorelease in DEBUG builds to maintain compiler +// analysis, and to use @try/@catch otherwise to avoid insertion of unnecessary +// autorelease pools. +#if DEBUG +#define ext_keywordify autoreleasepool {} +#else +#define ext_keywordify try {} @catch (...) {} +#endif diff --git a/Apps/Wikipedia/WMF Framework/Third Party/EXTScope.m b/Apps/Wikipedia/WMF Framework/Third Party/EXTScope.m new file mode 100644 index 0000000..5e68a9a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/EXTScope.m @@ -0,0 +1,15 @@ +// +// EXTScope.m +// extobjc +// +// Created by Justin Spahr-Summers on 2011-05-04. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import "EXTScope.h" + +void ext_executeCleanupBlock (__strong ext_cleanupBlock_t *block) { + (*block)(); +} + diff --git a/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImage.h b/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImage.h new file mode 100644 index 0000000..f056c3b --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImage.h @@ -0,0 +1,84 @@ +// +// FLAnimatedImage.h +// Flipboard +// +// Created by Raphael Schaad on 7/8/13. +// Copyright (c) 2013-2015 Flipboard. All rights reserved. +// + + +#import + +// Allow user classes conveniently just importing one header. +#import "FLAnimatedImageView.h" + + +#ifndef NS_DESIGNATED_INITIALIZER + #if __has_attribute(objc_designated_initializer) + #define NS_DESIGNATED_INITIALIZER __attribute((objc_designated_initializer)) + #else + #define NS_DESIGNATED_INITIALIZER + #endif +#endif + +extern const NSTimeInterval kFLAnimatedImageDelayTimeIntervalMinimum; + +// +// An `FLAnimatedImage`'s job is to deliver frames in a highly performant way and works in conjunction with `FLAnimatedImageView`. +// It subclasses `NSObject` and not `UIImage` because it's only an "image" in the sense that a sea lion is a lion. +// It tries to intelligently choose the frame cache size depending on the image and memory situation with the goal to lower CPU usage for smaller ones, lower memory usage for larger ones and always deliver frames for high performant play-back. +// Note: `posterImage`, `size`, `loopCount`, `delayTimes` and `frameCount` don't change after successful initialization. +// +@interface FLAnimatedImage : NSObject + +@property (nonatomic, strong, readonly) UIImage *posterImage; // Guaranteed to be loaded; usually equivalent to `-imageLazilyCachedAtIndex:0` +@property (nonatomic, assign, readonly) CGSize size; // The `.posterImage`'s `.size` + +@property (nonatomic, assign, readonly) NSUInteger loopCount; // 0 means repeating the animation indefinitely +@property (nonatomic, strong, readonly) NSDictionary *delayTimesForIndexes; // Of type `NSTimeInterval` boxed in `NSNumber`s +@property (nonatomic, assign, readonly) NSUInteger frameCount; // Number of valid frames; equal to `[.delayTimes count]` + +@property (nonatomic, assign, readonly) NSUInteger frameCacheSizeCurrent; // Current size of intelligently chosen buffer window; can range in the interval [1..frameCount] +@property (nonatomic, assign) NSUInteger frameCacheSizeMax; // Allow to cap the cache size; 0 means no specific limit (default) + +// Intended to be called from main thread synchronously; will return immediately. +// If the result isn't cached, will return `nil`; the caller should then pause playback, not increment frame counter and keep polling. +// After an initial loading time, depending on `frameCacheSize`, frames should be available immediately from the cache. +- (UIImage *)imageLazilyCachedAtIndex:(NSUInteger)index; + +// Pass either a `UIImage` or an `FLAnimatedImage` and get back its size ++ (CGSize)sizeForImage:(id)image; + +// On success, the initializers return an `FLAnimatedImage` with all fields initialized, on failure they return `nil` and an error will be logged. +- (instancetype)initWithAnimatedGIFData:(NSData *)data; +// Pass 0 for optimalFrameCacheSize to get the default, predrawing is enabled by default. +- (instancetype)initWithAnimatedGIFData:(NSData *)data optimalFrameCacheSize:(NSUInteger)optimalFrameCacheSize predrawingEnabled:(BOOL)isPredrawingEnabled NS_DESIGNATED_INITIALIZER; ++ (instancetype)animatedImageWithGIFData:(NSData *)data; + +@property (nonatomic, strong, readonly) NSData *data; // The data the receiver was initialized with; read-only + +@end + +typedef NS_ENUM(NSUInteger, FLLogLevel) { + FLLogLevelNone = 0, + FLLogLevelError, + FLLogLevelWarn, + FLLogLevelInfo, + FLLogLevelDebug, + FLLogLevelVerbose +}; + +@interface FLAnimatedImage (Logging) + ++ (void)setLogBlock:(void (^)(NSString *logString, FLLogLevel logLevel))logBlock logLevel:(FLLogLevel)logLevel; ++ (void)logStringFromBlock:(NSString *(^)(void))stringBlock withLevel:(FLLogLevel)level; + +@end + +#define FLLog(logLevel, format, ...) [FLAnimatedImage logStringFromBlock:^NSString *{ return [NSString stringWithFormat:(format), ## __VA_ARGS__]; } withLevel:(logLevel)] + +@interface FLWeakProxy : NSProxy + ++ (instancetype)weakProxyForObject:(id)targetObject; + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImage.m b/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImage.m new file mode 100755 index 0000000..402bb6f --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImage.m @@ -0,0 +1,820 @@ +// +// FLAnimatedImage.m +// Flipboard +// +// Created by Raphael Schaad on 7/8/13. +// Copyright (c) 2013-2015 Flipboard. All rights reserved. +// + + +#import "FLAnimatedImage.h" +#import +#import + + +// From vm_param.h, define for iOS 8.0 or higher to build on device. +#ifndef BYTE_SIZE + #define BYTE_SIZE 8 // byte size in bits +#endif + +#define MEGABYTE (1024 * 1024) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Weverything" + +// This is how the fastest browsers do it as per 2012: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser-compatibility +const NSTimeInterval kFLAnimatedImageDelayTimeIntervalMinimum = 0.02; + +// An animated image's data size (dimensions * frameCount) category; its value is the max allowed memory (in MB). +// E.g.: A 100x200px GIF with 30 frames is ~2.3MB in our pixel format and would fall into the `FLAnimatedImageDataSizeCategoryAll` category. +typedef NS_ENUM(NSUInteger, FLAnimatedImageDataSizeCategory) { + FLAnimatedImageDataSizeCategoryAll = 10, // All frames permanently in memory (be nice to the CPU) + FLAnimatedImageDataSizeCategoryDefault = 75, // A frame cache of default size in memory (usually real-time performance and keeping low memory profile) + FLAnimatedImageDataSizeCategoryOnDemand = 250, // Only keep one frame at the time in memory (easier on memory, slowest performance) + FLAnimatedImageDataSizeCategoryUnsupported // Even for one frame too large, computer says no. +}; + +typedef NS_ENUM(NSUInteger, FLAnimatedImageFrameCacheSize) { + FLAnimatedImageFrameCacheSizeNoLimit = 0, // 0 means no specific limit + FLAnimatedImageFrameCacheSizeLowMemory = 1, // The minimum frame cache size; this will produce frames on-demand. + FLAnimatedImageFrameCacheSizeGrowAfterMemoryWarning = 2, // If we can produce the frames faster than we consume, one frame ahead will already result in a stutter-free playback. + FLAnimatedImageFrameCacheSizeDefault = 5 // Build up a comfy buffer window to cope with CPU hiccups etc. +}; + + +#if defined(DEBUG) && DEBUG +@protocol FLAnimatedImageDebugDelegate +@optional +- (void)debug_animatedImage:(FLAnimatedImage *)animatedImage didUpdateCachedFrames:(NSIndexSet *)indexesOfFramesInCache; +- (void)debug_animatedImage:(FLAnimatedImage *)animatedImage didRequestCachedFrame:(NSUInteger)index; +- (CGFloat)debug_animatedImagePredrawingSlowdownFactor:(FLAnimatedImage *)animatedImage; +@end +#endif + + +@interface FLAnimatedImage () + +@property (nonatomic, assign, readonly) NSUInteger frameCacheSizeOptimal; // The optimal number of frames to cache based on image size & number of frames; never changes +@property (nonatomic, assign, readonly, getter=isPredrawingEnabled) BOOL predrawingEnabled; // Enables predrawing of images to improve performance. +@property (nonatomic, assign) NSUInteger frameCacheSizeMaxInternal; // Allow to cap the cache size e.g. when memory warnings occur; 0 means no specific limit (default) +@property (nonatomic, assign) NSUInteger requestedFrameIndex; // Most recently requested frame index +@property (nonatomic, assign, readonly) NSUInteger posterImageFrameIndex; // Index of non-purgable poster image; never changes +@property (nonatomic, strong, readonly) NSMutableDictionary *cachedFramesForIndexes; +@property (nonatomic, strong, readonly) NSMutableIndexSet *cachedFrameIndexes; // Indexes of cached frames +@property (nonatomic, strong, readonly) NSMutableIndexSet *requestedFrameIndexes; // Indexes of frames that are currently produced in the background +@property (nonatomic, strong, readonly) NSIndexSet *allFramesIndexSet; // Default index set with the full range of indexes; never changes +@property (nonatomic, assign) NSUInteger memoryWarningCount; +@property (nonatomic, strong, readonly) dispatch_queue_t serialQueue; +@property (nonatomic, strong, readonly) __attribute__((NSObject)) CGImageSourceRef imageSource; + +// The weak proxy is used to break retain cycles with delayed actions from memory warnings. +// We are lying about the actual type here to gain static type checking and eliminate casts. +// The actual type of the object is `FLWeakProxy`. +@property (nonatomic, strong, readonly) FLAnimatedImage *weakProxy; + +#if defined(DEBUG) && DEBUG +@property (nonatomic, weak) id debug_delegate; +#endif + +@end + + +// For custom dispatching of memory warnings to avoid deallocation races since NSNotificationCenter doesn't retain objects it is notifying. +static NSHashTable *allAnimatedImagesWeak; + +@implementation FLAnimatedImage + +#pragma mark - Accessors +#pragma mark Public + +// This is the definite value the frame cache needs to size itself to. +- (NSUInteger)frameCacheSizeCurrent +{ + NSUInteger frameCacheSizeCurrent = self.frameCacheSizeOptimal; + + // If set, respect the caps. + if (self.frameCacheSizeMax > FLAnimatedImageFrameCacheSizeNoLimit) { + frameCacheSizeCurrent = MIN(frameCacheSizeCurrent, self.frameCacheSizeMax); + } + + if (self.frameCacheSizeMaxInternal > FLAnimatedImageFrameCacheSizeNoLimit) { + frameCacheSizeCurrent = MIN(frameCacheSizeCurrent, self.frameCacheSizeMaxInternal); + } + + return frameCacheSizeCurrent; +} + + +- (void)setFrameCacheSizeMax:(NSUInteger)frameCacheSizeMax +{ + if (_frameCacheSizeMax != frameCacheSizeMax) { + + // Remember whether the new cap will cause the current cache size to shrink; then we'll make sure to purge from the cache if needed. + BOOL willFrameCacheSizeShrink = (frameCacheSizeMax < self.frameCacheSizeCurrent); + + // Update the value + _frameCacheSizeMax = frameCacheSizeMax; + + if (willFrameCacheSizeShrink) { + [self purgeFrameCacheIfNeeded]; + } + } +} + + +#pragma mark Private + +- (void)setFrameCacheSizeMaxInternal:(NSUInteger)frameCacheSizeMaxInternal +{ + if (_frameCacheSizeMaxInternal != frameCacheSizeMaxInternal) { + + // Remember whether the new cap will cause the current cache size to shrink; then we'll make sure to purge from the cache if needed. + BOOL willFrameCacheSizeShrink = (frameCacheSizeMaxInternal < self.frameCacheSizeCurrent); + + // Update the value + _frameCacheSizeMaxInternal = frameCacheSizeMaxInternal; + + if (willFrameCacheSizeShrink) { + [self purgeFrameCacheIfNeeded]; + } + } +} + + +#pragma mark - Life Cycle + ++ (void)initialize +{ + if (self == [FLAnimatedImage class]) { + // UIKit memory warning notification handler shared by all of the instances + allAnimatedImagesWeak = [NSHashTable weakObjectsHashTable]; + + [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:nil usingBlock:^(NSNotification *note) { + // UIKit notifications are posted on the main thread. didReceiveMemoryWarning: is expecting the main run loop, and we don't lock on allAnimatedImagesWeak + NSAssert([NSThread isMainThread], @"Received memory warning on non-main thread"); + // Get a strong reference to all of the images. If an instance is returned in this array, it is still live and has not entered dealloc. + // Note that FLAnimatedImages can be created on any thread, so the hash table must be locked. + NSArray *images = nil; + @synchronized(allAnimatedImagesWeak) { + images = [[allAnimatedImagesWeak allObjects] copy]; + } + // Now issue notifications to all of the images while holding a strong reference to them + [images makeObjectsPerformSelector:@selector(didReceiveMemoryWarning:) withObject:note]; + }]; + } +} + + +- (instancetype)init +{ + FLAnimatedImage *animatedImage = [self initWithAnimatedGIFData:nil]; + if (!animatedImage) { + FLLog(FLLogLevelError, @"Use `-initWithAnimatedGIFData:` and supply the animated GIF data as an argument to initialize an object of type `FLAnimatedImage`."); + } + return animatedImage; +} + + +- (instancetype)initWithAnimatedGIFData:(NSData *)data +{ + return [self initWithAnimatedGIFData:data optimalFrameCacheSize:0 predrawingEnabled:YES]; +} + +- (instancetype)initWithAnimatedGIFData:(NSData *)data optimalFrameCacheSize:(NSUInteger)optimalFrameCacheSize predrawingEnabled:(BOOL)isPredrawingEnabled +{ + // Early return if no data supplied! + BOOL hasData = ([data length] > 0); + if (!hasData) { + FLLog(FLLogLevelError, @"No animated GIF data supplied."); + return nil; + } + + self = [super init]; + if (self) { + // Do one-time initializations of `readonly` properties directly to ivar to prevent implicit actions and avoid need for private `readwrite` property overrides. + + // Keep a strong reference to `data` and expose it read-only publicly. + // However, we will use the `_imageSource` as handler to the image data throughout our life cycle. + _data = data; + _predrawingEnabled = isPredrawingEnabled; + + // Initialize internal data structures + _cachedFramesForIndexes = [[NSMutableDictionary alloc] init]; + _cachedFrameIndexes = [[NSMutableIndexSet alloc] init]; + _requestedFrameIndexes = [[NSMutableIndexSet alloc] init]; + + // Note: We could leverage `CGImageSourceCreateWithURL` too to add a second initializer `-initWithAnimatedGIFContentsOfURL:`. + _imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, + (__bridge CFDictionaryRef)@{(NSString *)kCGImageSourceShouldCache: @NO}); + // Early return on failure! + if (!_imageSource) { + FLLog(FLLogLevelError, @"Failed to `CGImageSourceCreateWithData` for animated GIF data %@", data); + return nil; + } + + // Early return if not GIF! + CFStringRef imageSourceContainerType = CGImageSourceGetType(_imageSource); + BOOL isGIFData = UTTypeConformsTo(imageSourceContainerType, kUTTypeGIF); + if (!isGIFData) { + FLLog(FLLogLevelError, @"Supplied data is of type %@ and doesn't seem to be GIF data %@", imageSourceContainerType, data); + return nil; + } + + // Get `LoopCount` + // Note: 0 means repeating the animation indefinitely. + // Image properties example: + // { + // FileSize = 314446; + // "{GIF}" = { + // HasGlobalColorMap = 1; + // LoopCount = 0; + // }; + // } + NSDictionary *imageProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(_imageSource, NULL); + _loopCount = [[[imageProperties objectForKey:(id)kCGImagePropertyGIFDictionary] objectForKey:(id)kCGImagePropertyGIFLoopCount] unsignedIntegerValue]; + + // Iterate through frame images + size_t imageCount = CGImageSourceGetCount(_imageSource); + NSUInteger skippedFrameCount = 0; + NSMutableDictionary *delayTimesForIndexesMutable = [NSMutableDictionary dictionaryWithCapacity:imageCount]; + for (size_t i = 0; i < imageCount; i++) { + @autoreleasepool { + CGImageRef frameImageRef = CGImageSourceCreateImageAtIndex(_imageSource, i, NULL); + if (frameImageRef) { + UIImage *frameImage = [UIImage imageWithCGImage:frameImageRef]; + // Check for valid `frameImage` before parsing its properties as frames can be corrupted (and `frameImage` even `nil` when `frameImageRef` was valid). + if (frameImage) { + // Set poster image + if (!self.posterImage) { + _posterImage = frameImage; + // Set its size to proxy our size. + _size = _posterImage.size; + // Remember index of poster image so we never purge it; also add it to the cache. + _posterImageFrameIndex = i; + [self.cachedFramesForIndexes setObject:self.posterImage forKey:@(self.posterImageFrameIndex)]; + [self.cachedFrameIndexes addIndex:self.posterImageFrameIndex]; + } + + // Get `DelayTime` + // Note: It's not in (1/100) of a second like still falsely described in the documentation as per iOS 8 (rdar://19507384) but in seconds stored as `kCFNumberFloat32Type`. + // Frame properties example: + // { + // ColorModel = RGB; + // Depth = 8; + // PixelHeight = 960; + // PixelWidth = 640; + // "{GIF}" = { + // DelayTime = "0.4"; + // UnclampedDelayTime = "0.4"; + // }; + // } + + NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(_imageSource, i, NULL); + NSDictionary *framePropertiesGIF = [frameProperties objectForKey:(id)kCGImagePropertyGIFDictionary]; + + // Try to use the unclamped delay time; fall back to the normal delay time. + NSNumber *delayTime = [framePropertiesGIF objectForKey:(id)kCGImagePropertyGIFUnclampedDelayTime]; + if (!delayTime) { + delayTime = [framePropertiesGIF objectForKey:(id)kCGImagePropertyGIFDelayTime]; + } + // If we don't get a delay time from the properties, fall back to `kDelayTimeIntervalDefault` or carry over the preceding frame's value. + const NSTimeInterval kDelayTimeIntervalDefault = 0.1; + if (!delayTime) { + if (i == 0) { + FLLog(FLLogLevelInfo, @"Falling back to default delay time for first frame %@ because none found in GIF properties %@", frameImage, frameProperties); + delayTime = @(kDelayTimeIntervalDefault); + } else { + FLLog(FLLogLevelInfo, @"Falling back to preceding delay time for frame %zu %@ because none found in GIF properties %@", i, frameImage, frameProperties); + delayTime = delayTimesForIndexesMutable[@(i - 1)]; + } + } + // Support frame delays as low as `kFLAnimatedImageDelayTimeIntervalMinimum`, with anything below being rounded up to `kDelayTimeIntervalDefault` for legacy compatibility. + // To support the minimum even when rounding errors occur, use an epsilon when comparing. We downcast to float because that's what we get for delayTime from ImageIO. + if ([delayTime floatValue] < ((float)kFLAnimatedImageDelayTimeIntervalMinimum - FLT_EPSILON)) { + FLLog(FLLogLevelInfo, @"Rounding frame %zu's `delayTime` from %f up to default %f (minimum supported: %f).", i, [delayTime floatValue], kDelayTimeIntervalDefault, kFLAnimatedImageDelayTimeIntervalMinimum); + delayTime = @(kDelayTimeIntervalDefault); + } + delayTimesForIndexesMutable[@(i)] = delayTime; + } else { + skippedFrameCount++; + FLLog(FLLogLevelInfo, @"Dropping frame %zu because valid `CGImageRef` %@ did result in `nil`-`UIImage`.", i, frameImageRef); + } + CFRelease(frameImageRef); + } else { + skippedFrameCount++; + FLLog(FLLogLevelInfo, @"Dropping frame %zu because failed to `CGImageSourceCreateImageAtIndex` with image source %@", i, _imageSource); + } + } + } + _delayTimesForIndexes = [delayTimesForIndexesMutable copy]; + _frameCount = imageCount; + + if (self.frameCount == 0) { + FLLog(FLLogLevelInfo, @"Failed to create any valid frames for GIF with properties %@", imageProperties); + return nil; + } else if (self.frameCount == 1) { + // Warn when we only have a single frame but return a valid GIF. + FLLog(FLLogLevelInfo, @"Created valid GIF but with only a single frame. Image properties: %@", imageProperties); + } else { + // We have multiple frames, rock on! + } + + // If no value is provided, select a default based on the GIF. + if (optimalFrameCacheSize == 0) { + // Calculate the optimal frame cache size: try choosing a larger buffer window depending on the predicted image size. + // It's only dependent on the image size & number of frames and never changes. + CGFloat animatedImageDataSize = CGImageGetBytesPerRow(self.posterImage.CGImage) * self.size.height * (self.frameCount - skippedFrameCount) / MEGABYTE; + if (animatedImageDataSize <= FLAnimatedImageDataSizeCategoryAll) { + _frameCacheSizeOptimal = self.frameCount; + } else if (animatedImageDataSize <= FLAnimatedImageDataSizeCategoryDefault) { + // This value doesn't depend on device memory much because if we're not keeping all frames in memory we will always be decoding 1 frame up ahead per 1 frame that gets played and at this point we might as well just keep a small buffer just large enough to keep from running out of frames. + _frameCacheSizeOptimal = FLAnimatedImageFrameCacheSizeDefault; + } else { + // The predicted size exceeds the limits to build up a cache and we go into low memory mode from the beginning. + _frameCacheSizeOptimal = FLAnimatedImageFrameCacheSizeLowMemory; + } + } else { + // Use the provided value. + _frameCacheSizeOptimal = optimalFrameCacheSize; + } + // In any case, cap the optimal cache size at the frame count. + _frameCacheSizeOptimal = MIN(_frameCacheSizeOptimal, self.frameCount); + + // Convenience/minor performance optimization; keep an index set handy with the full range to return in `-frameIndexesToCache`. + _allFramesIndexSet = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, self.frameCount)]; + + // See the property declarations for descriptions. + _weakProxy = (id)[FLWeakProxy weakProxyForObject:self]; + + // Register this instance in the weak table for memory notifications. The NSHashTable will clean up after itself when we're gone. + // Note that FLAnimatedImages can be created on any thread, so the hash table must be locked. + @synchronized(allAnimatedImagesWeak) { + [allAnimatedImagesWeak addObject:self]; + } + } + return self; +} + + ++ (instancetype)animatedImageWithGIFData:(NSData *)data +{ + FLAnimatedImage *animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:data]; + return animatedImage; +} + + +- (void)dealloc +{ + if (_weakProxy) { + [NSObject cancelPreviousPerformRequestsWithTarget:_weakProxy]; + } + + if (_imageSource) { + CFRelease(_imageSource); + } +} + + +#pragma mark - Public Methods + +// See header for more details. +// Note: both consumer and producer are throttled: consumer by frame timings and producer by the available memory (max buffer window size). +- (UIImage *)imageLazilyCachedAtIndex:(NSUInteger)index +{ + // Early return if the requested index is beyond bounds. + // Note: We're comparing an index with a count and need to bail on greater than or equal to. + if (index >= self.frameCount) { + FLLog(FLLogLevelWarn, @"Skipping requested frame %lu beyond bounds (total frame count: %lu) for animated image: %@", (unsigned long)index, (unsigned long)self.frameCount, self); + return nil; + } + + // Remember requested frame index, this influences what we should cache next. + self.requestedFrameIndex = index; +#if defined(DEBUG) && DEBUG + if ([self.debug_delegate respondsToSelector:@selector(debug_animatedImage:didRequestCachedFrame:)]) { + [self.debug_delegate debug_animatedImage:self didRequestCachedFrame:index]; + } +#endif + + // Quick check to avoid doing any work if we already have all possible frames cached, a common case. + if ([self.cachedFrameIndexes count] < self.frameCount) { + // If we have frames that should be cached but aren't and aren't requested yet, request them. + // Exclude existing cached frames, frames already requested, and specially cached poster image. + NSMutableIndexSet *frameIndexesToAddToCacheMutable = [self frameIndexesToCache]; + [frameIndexesToAddToCacheMutable removeIndexes:self.cachedFrameIndexes]; + [frameIndexesToAddToCacheMutable removeIndexes:self.requestedFrameIndexes]; + [frameIndexesToAddToCacheMutable removeIndex:self.posterImageFrameIndex]; + NSIndexSet *frameIndexesToAddToCache = [frameIndexesToAddToCacheMutable copy]; + + // Asynchronously add frames to our cache. + if ([frameIndexesToAddToCache count] > 0) { + [self addFrameIndexesToCache:frameIndexesToAddToCache]; + } + } + + // Get the specified image. + UIImage *image = self.cachedFramesForIndexes[@(index)]; + + // Purge if needed based on the current playhead position. + [self purgeFrameCacheIfNeeded]; + + return image; +} + + +// Only called once from `-imageLazilyCachedAtIndex` but factored into its own method for logical grouping. +- (void)addFrameIndexesToCache:(NSIndexSet *)frameIndexesToAddToCache +{ + // Order matters. First, iterate over the indexes starting from the requested frame index. + // Then, if there are any indexes before the requested frame index, do those. + NSRange firstRange = NSMakeRange(self.requestedFrameIndex, self.frameCount - self.requestedFrameIndex); + NSRange secondRange = NSMakeRange(0, self.requestedFrameIndex); + if (firstRange.length + secondRange.length != self.frameCount) { + FLLog(FLLogLevelWarn, @"Two-part frame cache range doesn't equal full range."); + } + + // Add to the requested list before we actually kick them off, so they don't get into the queue twice. + [self.requestedFrameIndexes addIndexes:frameIndexesToAddToCache]; + + // Lazily create dedicated isolation queue. + if (!self.serialQueue) { + _serialQueue = dispatch_queue_create("com.flipboard.framecachingqueue", DISPATCH_QUEUE_SERIAL); + } + + // Start streaming requested frames in the background into the cache. + // Avoid capturing self in the block as there's no reason to keep doing work if the animated image went away. + FLAnimatedImage * __weak weakSelf = self; + dispatch_async(self.serialQueue, ^{ + // Produce and cache next needed frame. + void (^frameRangeBlock)(NSRange, BOOL *) = ^(NSRange range, BOOL *stop) { + // Iterate through contiguous indexes; can be faster than `enumerateIndexesInRange:options:usingBlock:`. + for (NSUInteger i = range.location; i < NSMaxRange(range); i++) { +#if defined(DEBUG) && DEBUG + CFTimeInterval predrawBeginTime = CACurrentMediaTime(); +#endif + UIImage *image = [weakSelf imageAtIndex:i]; +#if defined(DEBUG) && DEBUG + CFTimeInterval predrawDuration = CACurrentMediaTime() - predrawBeginTime; + CFTimeInterval slowdownDuration = 0.0; + if ([self.debug_delegate respondsToSelector:@selector(debug_animatedImagePredrawingSlowdownFactor:)]) { + CGFloat predrawingSlowdownFactor = [self.debug_delegate debug_animatedImagePredrawingSlowdownFactor:self]; + slowdownDuration = predrawDuration * predrawingSlowdownFactor - predrawDuration; + [NSThread sleepForTimeInterval:slowdownDuration]; + } + FLLog(FLLogLevelVerbose, @"Predrew frame %lu in %f ms for animated image: %@", (unsigned long)i, (predrawDuration + slowdownDuration) * 1000, self); +#endif + // The results get returned one by one as soon as they're ready (and not in batch). + // The benefits of having the first frames as quick as possible outweigh building up a buffer to cope with potential hiccups when the CPU suddenly gets busy. + if (image && weakSelf) { + dispatch_async(dispatch_get_main_queue(), ^{ + weakSelf.cachedFramesForIndexes[@(i)] = image; + [weakSelf.cachedFrameIndexes addIndex:i]; + [weakSelf.requestedFrameIndexes removeIndex:i]; +#if defined(DEBUG) && DEBUG + if ([weakSelf.debug_delegate respondsToSelector:@selector(debug_animatedImage:didUpdateCachedFrames:)]) { + [weakSelf.debug_delegate debug_animatedImage:weakSelf didUpdateCachedFrames:weakSelf.cachedFrameIndexes]; + } +#endif + }); + } + } + }; + + [frameIndexesToAddToCache enumerateRangesInRange:firstRange options:0 usingBlock:frameRangeBlock]; + [frameIndexesToAddToCache enumerateRangesInRange:secondRange options:0 usingBlock:frameRangeBlock]; + }); +} + + ++ (CGSize)sizeForImage:(id)image +{ + CGSize imageSize = CGSizeZero; + + // Early return for nil + if (!image) { + return imageSize; + } + + if ([image isKindOfClass:[UIImage class]]) { + UIImage *uiImage = (UIImage *)image; + imageSize = uiImage.size; + } else if ([image isKindOfClass:[FLAnimatedImage class]]) { + FLAnimatedImage *animatedImage = (FLAnimatedImage *)image; + imageSize = animatedImage.size; + } else { + // Bear trap to capture bad images; we have seen crashers cropping up on iOS 7. + FLLog(FLLogLevelError, @"`image` isn't of expected types `UIImage` or `FLAnimatedImage`: %@", image); + } + + return imageSize; +} + + +#pragma mark - Private Methods +#pragma mark Frame Loading + +- (UIImage *)imageAtIndex:(NSUInteger)index +{ + // It's very important to use the cached `_imageSource` since the random access to a frame with `CGImageSourceCreateImageAtIndex` turns from an O(1) into an O(n) operation when re-initializing the image source every time. + CGImageRef imageRef = CGImageSourceCreateImageAtIndex(_imageSource, index, NULL); + + // Early return for nil + if (!imageRef) { + return nil; + } + + UIImage *image = [UIImage imageWithCGImage:imageRef]; + CFRelease(imageRef); + + // Loading in the image object is only half the work, the displaying image view would still have to synchronosly wait and decode the image, so we go ahead and do that here on the background thread. + if (self.isPredrawingEnabled) { + image = [[self class] predrawnImageFromImage:image]; + } + + return image; +} + + +#pragma mark Frame Caching + +- (NSMutableIndexSet *)frameIndexesToCache +{ + NSMutableIndexSet *indexesToCache = nil; + // Quick check to avoid building the index set if the number of frames to cache equals the total frame count. + if (self.frameCacheSizeCurrent == self.frameCount) { + indexesToCache = [self.allFramesIndexSet mutableCopy]; + } else { + indexesToCache = [[NSMutableIndexSet alloc] init]; + + // Add indexes to the set in two separate blocks- the first starting from the requested frame index, up to the limit or the end. + // The second, if needed, the remaining number of frames beginning at index zero. + NSUInteger firstLength = MIN(self.frameCacheSizeCurrent, self.frameCount - self.requestedFrameIndex); + NSRange firstRange = NSMakeRange(self.requestedFrameIndex, firstLength); + [indexesToCache addIndexesInRange:firstRange]; + NSUInteger secondLength = self.frameCacheSizeCurrent - firstLength; + if (secondLength > 0) { + NSRange secondRange = NSMakeRange(0, secondLength); + [indexesToCache addIndexesInRange:secondRange]; + } + // Double check our math, before we add the poster image index which may increase it by one. + if ([indexesToCache count] != self.frameCacheSizeCurrent) { + FLLog(FLLogLevelWarn, @"Number of frames to cache doesn't equal expected cache size."); + } + + [indexesToCache addIndex:self.posterImageFrameIndex]; + } + + return indexesToCache; +} + + +- (void)purgeFrameCacheIfNeeded +{ + // Purge frames that are currently cached but don't need to be. + // But not if we're still under the number of frames to cache. + // This way, if all frames are allowed to be cached (the common case), we can skip all the `NSIndexSet` math below. + if ([self.cachedFrameIndexes count] > self.frameCacheSizeCurrent) { + NSMutableIndexSet *indexesToPurge = [self.cachedFrameIndexes mutableCopy]; + [indexesToPurge removeIndexes:[self frameIndexesToCache]]; + [indexesToPurge enumerateRangesUsingBlock:^(NSRange range, BOOL *stop) { + // Iterate through contiguous indexes; can be faster than `enumerateIndexesInRange:options:usingBlock:`. + for (NSUInteger i = range.location; i < NSMaxRange(range); i++) { + [self.cachedFrameIndexes removeIndex:i]; + [self.cachedFramesForIndexes removeObjectForKey:@(i)]; + // Note: Don't `CGImageSourceRemoveCacheAtIndex` on the image source for frames that we don't want cached any longer to maintain O(1) time access. +#if defined(DEBUG) && DEBUG + if ([self.debug_delegate respondsToSelector:@selector(debug_animatedImage:didUpdateCachedFrames:)]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.debug_delegate debug_animatedImage:self didUpdateCachedFrames:self.cachedFrameIndexes]; + }); + } +#endif + } + }]; + } +} + + +- (void)growFrameCacheSizeAfterMemoryWarning:(NSNumber *)frameCacheSize +{ + self.frameCacheSizeMaxInternal = [frameCacheSize unsignedIntegerValue]; + FLLog(FLLogLevelDebug, @"Grew frame cache size max to %lu after memory warning for animated image: %@", (unsigned long)self.frameCacheSizeMaxInternal, self); + + // Schedule resetting the frame cache size max completely after a while. + const NSTimeInterval kResetDelay = 3.0; + [self.weakProxy performSelector:@selector(resetFrameCacheSizeMaxInternal) withObject:nil afterDelay:kResetDelay]; +} + + +- (void)resetFrameCacheSizeMaxInternal +{ + self.frameCacheSizeMaxInternal = FLAnimatedImageFrameCacheSizeNoLimit; + FLLog(FLLogLevelDebug, @"Reset frame cache size max (current frame cache size: %lu) for animated image: %@", (unsigned long)self.frameCacheSizeCurrent, self); +} + + +#pragma mark System Memory Warnings Notification Handler + +- (void)didReceiveMemoryWarning:(NSNotification *)notification +{ + self.memoryWarningCount++; + + // If we were about to grow larger, but got rapped on our knuckles by the system again, cancel. + [NSObject cancelPreviousPerformRequestsWithTarget:self.weakProxy selector:@selector(growFrameCacheSizeAfterMemoryWarning:) object:@(FLAnimatedImageFrameCacheSizeGrowAfterMemoryWarning)]; + [NSObject cancelPreviousPerformRequestsWithTarget:self.weakProxy selector:@selector(resetFrameCacheSizeMaxInternal) object:nil]; + + // Go down to the minimum and by that implicitly immediately purge from the cache if needed to not get jettisoned by the system and start producing frames on-demand. + FLLog(FLLogLevelDebug, @"Attempt setting frame cache size max to %lu (previous was %lu) after memory warning #%lu for animated image: %@", (unsigned long)FLAnimatedImageFrameCacheSizeLowMemory, (unsigned long)self.frameCacheSizeMaxInternal, (unsigned long)self.memoryWarningCount, self); + self.frameCacheSizeMaxInternal = FLAnimatedImageFrameCacheSizeLowMemory; + + // Schedule growing larger again after a while, but cap our attempts to prevent a periodic sawtooth wave (ramps upward and then sharply drops) of memory usage. + // + // [mem]^ (2) (5) (6) 1) Loading frames for the first time + // (*)| , , , 2) Mem warning #1; purge cache + // | /| (4)/| /| 3) Grow cache size a bit after a while, if no mem warning occurs + // | / | _/ | _/ | 4) Try to grow cache size back to optimum after a while, if no mem warning occurs + // |(1)/ |_/ |/ |__(7) 5) Mem warning #2; purge cache + // |__/ (3) 6) After repetition of (3) and (4), mem warning #3; purge cache + // +----------------------> 7) After 3 mem warnings, stay at minimum cache size + // [t] + // *) The mem high water mark before we get warned might change for every cycle. + // + const NSUInteger kGrowAttemptsMax = 2; + const NSTimeInterval kGrowDelay = 2.0; + if ((self.memoryWarningCount - 1) <= kGrowAttemptsMax) { + [self.weakProxy performSelector:@selector(growFrameCacheSizeAfterMemoryWarning:) withObject:@(FLAnimatedImageFrameCacheSizeGrowAfterMemoryWarning) afterDelay:kGrowDelay]; + } + + // Note: It's not possible to get the level of a memory warning with a public API: http://stackoverflow.com/questions/2915247/iphone-os-memory-warnings-what-do-the-different-levels-mean/2915477#2915477 +} + + +#pragma mark Image Decoding + +// Decodes the image's data and draws it off-screen fully in memory; it's thread-safe and hence can be called on a background thread. +// On success, the returned object is a new `UIImage` instance with the same content as the one passed in. +// On failure, the returned object is the unchanged passed in one; the data will not be predrawn in memory though and an error will be logged. +// First inspired by & good Karma to: https://gist.github.com/steipete/1144242 ++ (UIImage *)predrawnImageFromImage:(UIImage *)imageToPredraw +{ + // Always use a device RGB color space for simplicity and predictability what will be going on. + CGColorSpaceRef colorSpaceDeviceRGBRef = CGColorSpaceCreateDeviceRGB(); + // Early return on failure! + if (!colorSpaceDeviceRGBRef) { + FLLog(FLLogLevelError, @"Failed to `CGColorSpaceCreateDeviceRGB` for image %@", imageToPredraw); + return imageToPredraw; + } + + // Even when the image doesn't have transparency, we have to add the extra channel because Quartz doesn't support other pixel formats than 32 bpp/8 bpc for RGB: + // kCGImageAlphaNoneSkipFirst, kCGImageAlphaNoneSkipLast, kCGImageAlphaPremultipliedFirst, kCGImageAlphaPremultipliedLast + // (source: docs "Quartz 2D Programming Guide > Graphics Contexts > Table 2-1 Pixel formats supported for bitmap graphics contexts") + size_t numberOfComponents = CGColorSpaceGetNumberOfComponents(colorSpaceDeviceRGBRef) + 1; // 4: RGB + A + + // "In iOS 4.0 and later, and OS X v10.6 and later, you can pass NULL if you want Quartz to allocate memory for the bitmap." (source: docs) + void *data = NULL; + size_t width = imageToPredraw.size.width; + size_t height = imageToPredraw.size.height; + size_t bitsPerComponent = CHAR_BIT; + + size_t bitsPerPixel = (bitsPerComponent * numberOfComponents); + size_t bytesPerPixel = (bitsPerPixel / BYTE_SIZE); + size_t bytesPerRow = (bytesPerPixel * width); + + CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault; + + CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageToPredraw.CGImage); + // If the alpha info doesn't match to one of the supported formats (see above), pick a reasonable supported one. + // "For bitmaps created in iOS 3.2 and later, the drawing environment uses the premultiplied ARGB format to store the bitmap data." (source: docs) + if (alphaInfo == kCGImageAlphaNone || alphaInfo == kCGImageAlphaOnly) { + alphaInfo = kCGImageAlphaNoneSkipFirst; + } else if (alphaInfo == kCGImageAlphaFirst) { + alphaInfo = kCGImageAlphaPremultipliedFirst; + } else if (alphaInfo == kCGImageAlphaLast) { + alphaInfo = kCGImageAlphaPremultipliedLast; + } + // "The constants for specifying the alpha channel information are declared with the `CGImageAlphaInfo` type but can be passed to this parameter safely." (source: docs) + bitmapInfo |= alphaInfo; + + // Create our own graphics context to draw to; `UIGraphicsGetCurrentContext`/`UIGraphicsBeginImageContextWithOptions` doesn't create a new context but returns the current one which isn't thread-safe (e.g. main thread could use it at the same time). + // Note: It's not worth caching the bitmap context for multiple frames ("unique key" would be `width`, `height` and `hasAlpha`), it's ~50% slower. Time spent in libRIP's `CGSBlendBGRA8888toARGB8888` suddenly shoots up -- not sure why. + CGContextRef bitmapContextRef = CGBitmapContextCreate(data, width, height, bitsPerComponent, bytesPerRow, colorSpaceDeviceRGBRef, bitmapInfo); + CGColorSpaceRelease(colorSpaceDeviceRGBRef); + // Early return on failure! + if (!bitmapContextRef) { + FLLog(FLLogLevelError, @"Failed to `CGBitmapContextCreate` with color space %@ and parameters (width: %zu height: %zu bitsPerComponent: %zu bytesPerRow: %zu) for image %@", colorSpaceDeviceRGBRef, width, height, bitsPerComponent, bytesPerRow, imageToPredraw); + return imageToPredraw; + } + + // Draw image in bitmap context and create image by preserving receiver's properties. + CGContextDrawImage(bitmapContextRef, CGRectMake(0.0, 0.0, imageToPredraw.size.width, imageToPredraw.size.height), imageToPredraw.CGImage); + CGImageRef predrawnImageRef = CGBitmapContextCreateImage(bitmapContextRef); + UIImage *predrawnImage = [UIImage imageWithCGImage:predrawnImageRef scale:imageToPredraw.scale orientation:imageToPredraw.imageOrientation]; + CGImageRelease(predrawnImageRef); + CGContextRelease(bitmapContextRef); + + // Early return on failure! + if (!predrawnImage) { + FLLog(FLLogLevelError, @"Failed to `imageWithCGImage:scale:orientation:` with image ref %@ created with color space %@ and bitmap context %@ and properties and properties (scale: %f orientation: %ld) for image %@", predrawnImageRef, colorSpaceDeviceRGBRef, bitmapContextRef, imageToPredraw.scale, (long)imageToPredraw.imageOrientation, imageToPredraw); + return imageToPredraw; + } + + return predrawnImage; +} + + +#pragma mark - Description + +- (NSString *)description +{ + NSString *description = [super description]; + + description = [description stringByAppendingFormat:@" size=%@", NSStringFromCGSize(self.size)]; + description = [description stringByAppendingFormat:@" frameCount=%lu", (unsigned long)self.frameCount]; + + return description; +} + + +@end + +#pragma mark - Logging + +@implementation FLAnimatedImage (Logging) + +static void (^_logBlock)(NSString *logString, FLLogLevel logLevel) = nil; +static FLLogLevel _logLevel; + ++ (void)setLogBlock:(void (^)(NSString *logString, FLLogLevel logLevel))logBlock logLevel:(FLLogLevel)logLevel +{ + _logBlock = logBlock; + _logLevel = logLevel; +} + ++ (void)logStringFromBlock:(NSString *(^)(void))stringBlock withLevel:(FLLogLevel)level +{ + if (level <= _logLevel && _logBlock && stringBlock) { + _logBlock(stringBlock(), level); + } +} + +@end + + +#pragma mark - FLWeakProxy + +@interface FLWeakProxy () + +@property (nonatomic, weak) id target; + +@end + + +@implementation FLWeakProxy + +#pragma mark Life Cycle + +// This is the designated creation method of an `FLWeakProxy` and +// as a subclass of `NSProxy` it doesn't respond to or need `-init`. ++ (instancetype)weakProxyForObject:(id)targetObject +{ + FLWeakProxy *weakProxy = [FLWeakProxy alloc]; + weakProxy.target = targetObject; + return weakProxy; +} + + +#pragma mark Forwarding Messages + +- (id)forwardingTargetForSelector:(SEL)selector +{ + // Keep it lightweight: access the ivar directly + return _target; +} + + +#pragma mark - NSWeakProxy Method Overrides +#pragma mark Handling Unimplemented Methods + +- (void)forwardInvocation:(NSInvocation *)invocation +{ + // Fallback for when target is nil. Don't do anything, just return 0/NULL/nil. + // The method signature we've received to get here is just a dummy to keep `doesNotRecognizeSelector:` from firing. + // We can't really handle struct return types here because we don't know the length. + void *nullPointer = NULL; + [invocation setReturnValue:&nullPointer]; +} + + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector +{ + // We only get here if `forwardingTargetForSelector:` returns nil. + // In that case, our weak target has been reclaimed. Return a dummy method signature to keep `doesNotRecognizeSelector:` from firing. + // We'll emulate the Obj-c messaging nil behavior by setting the return value to nil in `forwardInvocation:`, but we'll assume that the return value is `sizeof(void *)`. + // Other libraries handle this situation by making use of a global method signature cache, but that seems heavier than necessary and has issues as well. + // See https://www.mikeash.com/pyblog/friday-qa-2010-02-26-futures.html and https://github.com/steipete/PSTDelegateProxy/issues/1 for examples of using a method signature cache. + return [NSObject instanceMethodSignatureForSelector:@selector(init)]; +} + + +@end +#pragma clang diagnostic pop diff --git a/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImageView.h b/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImageView.h new file mode 100644 index 0000000..c0d527a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImageView.h @@ -0,0 +1,36 @@ +// +// FLAnimatedImageView.h +// Flipboard +// +// Created by Raphael Schaad on 7/8/13. +// Copyright (c) 2013-2015 Flipboard. All rights reserved. +// + + +#import + +@class FLAnimatedImage; +@protocol FLAnimatedImageViewDebugDelegate; + + +// +// An `FLAnimatedImageView` can take an `FLAnimatedImage` and plays it automatically when in view hierarchy and stops when removed. +// The animation can also be controlled with the `UIImageView` methods `-start/stop/isAnimating`. +// It is a fully compatible `UIImageView` subclass and can be used as a drop-in component to work with existing code paths expecting to display a `UIImage`. +// Under the hood it uses a `CADisplayLink` for playback, which can be inspected with `currentFrame` & `currentFrameIndex`. +// +@interface FLAnimatedImageView : UIImageView + +// Setting `[UIImageView.image]` to a non-`nil` value clears out existing `animatedImage`. +// And vice versa, setting `animatedImage` will initially populate the `[UIImageView.image]` to its `posterImage` and then start animating and hold `currentFrame`. +@property (nonatomic, strong) FLAnimatedImage *animatedImage; +@property (nonatomic, copy) void(^loopCompletionBlock)(NSUInteger loopCountRemaining); + +@property (nonatomic, strong, readonly) UIImage *currentFrame; +@property (nonatomic, assign, readonly) NSUInteger currentFrameIndex; + +// The animation runloop mode. Enables playback during scrolling by allowing timer events (i.e. animation) with NSRunLoopCommonModes. +// To keep scrolling smooth on single-core devices such as iPhone 3GS/4 and iPod Touch 4th gen, the default run loop mode is NSDefaultRunLoopMode. Otherwise, the default is NSDefaultRunLoopMode. +@property (nonatomic, copy) NSString *runLoopMode; + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImageView.m b/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImageView.m new file mode 100755 index 0000000..fe8b23c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/FLAnimatedImage/FLAnimatedImage/FLAnimatedImageView.m @@ -0,0 +1,441 @@ +// +// FLAnimatedImageView.h +// Flipboard +// +// Created by Raphael Schaad on 7/8/13. +// Copyright (c) 2013-2015 Flipboard. All rights reserved. +// + + +#import "FLAnimatedImageView.h" +#import "FLAnimatedImage.h" +#import + + +#if defined(DEBUG) && DEBUG +@protocol FLAnimatedImageViewDebugDelegate +@optional +- (void)debug_animatedImageView:(FLAnimatedImageView *)animatedImageView waitingForFrame:(NSUInteger)index duration:(NSTimeInterval)duration; +@end +#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Weverything" +@interface FLAnimatedImageView () + +// Override of public `readonly` properties as private `readwrite` +@property (nonatomic, strong, readwrite) UIImage *currentFrame; +@property (nonatomic, assign, readwrite) NSUInteger currentFrameIndex; + +@property (nonatomic, assign) NSUInteger loopCountdown; +@property (nonatomic, assign) NSTimeInterval accumulator; +@property (nonatomic, strong) CADisplayLink *displayLink; + +@property (nonatomic, assign) BOOL shouldAnimate; // Before checking this value, call `-updateShouldAnimate` whenever the animated image or visibility (window, superview, hidden, alpha) has changed. +@property (nonatomic, assign) BOOL needsDisplayWhenImageBecomesAvailable; + +#if defined(DEBUG) && DEBUG +@property (nonatomic, weak) id debug_delegate; +#endif + +@end + + +@implementation FLAnimatedImageView +@synthesize runLoopMode = _runLoopMode; + +#pragma mark - Initializers + +// -initWithImage: isn't documented as a designated initializer of UIImageView, but it actually seems to be. +// Using -initWithImage: doesn't call any of the other designated initializers. +- (instancetype)initWithImage:(UIImage *)image +{ + self = [super initWithImage:image]; + if (self) { + [self commonInit]; + } + return self; +} + +// -initWithImage:highlightedImage: also isn't documented as a designated initializer of UIImageView, but it doesn't call any other designated initializers. +- (instancetype)initWithImage:(UIImage *)image highlightedImage:(UIImage *)highlightedImage +{ + self = [super initWithImage:image highlightedImage:highlightedImage]; + if (self) { + [self commonInit]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self commonInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonInit]; + } + return self; +} + +- (void)commonInit +{ + self.runLoopMode = [[self class] defaultRunLoopMode]; + self.accessibilityIgnoresInvertColors = YES; +} + + +#pragma mark - Accessors +#pragma mark Public + +- (void)setAnimatedImage:(FLAnimatedImage *)animatedImage +{ + if (![_animatedImage isEqual:animatedImage]) { + if (animatedImage) { + // Clear out the image. + super.image = nil; + // Ensure disabled highlighting; it's not supported (see `-setHighlighted:`). + super.highlighted = NO; + // UIImageView seems to bypass some accessors when calculating its intrinsic content size, so this ensures its intrinsic content size comes from the animated image. + [self invalidateIntrinsicContentSize]; + } else { + // Stop animating before the animated image gets cleared out. + [self stopAnimating]; + } + + _animatedImage = animatedImage; + + self.currentFrame = animatedImage.posterImage; + self.currentFrameIndex = 0; + if (animatedImage.loopCount > 0) { + self.loopCountdown = animatedImage.loopCount; + } else { + self.loopCountdown = NSUIntegerMax; + } + self.accumulator = 0.0; + + // Start animating after the new animated image has been set. + [self updateShouldAnimate]; + if (self.shouldAnimate) { + [self startAnimating]; + } + + [self.layer setNeedsDisplay]; + } +} + + +#pragma mark - Life Cycle + +- (void)dealloc +{ + // Removes the display link from all run loop modes. + [_displayLink invalidate]; +} + + +#pragma mark - UIView Method Overrides +#pragma mark Observing View-Related Changes + +- (void)didMoveToSuperview +{ + [super didMoveToSuperview]; + + [self updateShouldAnimate]; + if (self.shouldAnimate) { + [self startAnimating]; + } else { + [self stopAnimating]; + } +} + + +- (void)didMoveToWindow +{ + [super didMoveToWindow]; + + [self updateShouldAnimate]; + if (self.shouldAnimate) { + [self startAnimating]; + } else { + [self stopAnimating]; + } +} + +- (void)setAlpha:(CGFloat)alpha +{ + [super setAlpha:alpha]; + + [self updateShouldAnimate]; + if (self.shouldAnimate) { + [self startAnimating]; + } else { + [self stopAnimating]; + } +} + +- (void)setHidden:(BOOL)hidden +{ + [super setHidden:hidden]; + + [self updateShouldAnimate]; + if (self.shouldAnimate) { + [self startAnimating]; + } else { + [self stopAnimating]; + } +} + + +#pragma mark Auto Layout + +- (CGSize)intrinsicContentSize +{ + // Default to let UIImageView handle the sizing of its image, and anything else it might consider. + CGSize intrinsicContentSize = [super intrinsicContentSize]; + + // If we have have an animated image, use its image size. + // UIImageView's intrinsic content size seems to be the size of its image. The obvious approach, simply calling `-invalidateIntrinsicContentSize` when setting an animated image, results in UIImageView steadfastly returning `{UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric}` for its intrinsicContentSize. + // (Perhaps UIImageView bypasses its `-image` getter in its implementation of `-intrinsicContentSize`, as `-image` is not called after calling `-invalidateIntrinsicContentSize`.) + if (self.animatedImage) { + intrinsicContentSize = self.image.size; + } + + return intrinsicContentSize; +} + +#pragma mark Smart Invert Colors + +#pragma mark - UIImageView Method Overrides +#pragma mark Image Data + +- (UIImage *)image +{ + UIImage *image = nil; + if (self.animatedImage) { + // Initially set to the poster image. + image = self.currentFrame; + } else { + image = super.image; + } + return image; +} + + +- (void)setImage:(UIImage *)image +{ + if (image) { + // Clear out the animated image and implicitly pause animation playback. + self.animatedImage = nil; + } + + super.image = image; +} + + +#pragma mark Animating Images + +- (NSTimeInterval)frameDelayGreatestCommonDivisor +{ + // Presision is set to half of the `kFLAnimatedImageDelayTimeIntervalMinimum` in order to minimize frame dropping. + const NSTimeInterval kGreatestCommonDivisorPrecision = 2.0 / kFLAnimatedImageDelayTimeIntervalMinimum; + + NSArray *delays = self.animatedImage.delayTimesForIndexes.allValues; + + // Scales the frame delays by `kGreatestCommonDivisorPrecision` + // then converts it to an UInteger for in order to calculate the GCD. + NSUInteger scaledGCD = lrint([delays.firstObject floatValue] * kGreatestCommonDivisorPrecision); + for (NSNumber *value in delays) { + scaledGCD = gcd(lrint([value floatValue] * kGreatestCommonDivisorPrecision), scaledGCD); + } + + // Reverse to scale to get the value back into seconds. + return scaledGCD / kGreatestCommonDivisorPrecision; +} + + +static NSUInteger gcd(NSUInteger a, NSUInteger b) +{ + // http://en.wikipedia.org/wiki/Greatest_common_divisor + if (a < b) { + return gcd(b, a); + } else if (a == b) { + return b; + } + + while (true) { + NSUInteger remainder = a % b; + if (remainder == 0) { + return b; + } + a = b; + b = remainder; + } +} + + +- (void)startAnimating +{ + if (self.animatedImage) { + // Lazily create the display link. + if (!self.displayLink) { + // It is important to note the use of a weak proxy here to avoid a retain cycle. `-displayLinkWithTarget:selector:` + // will retain its target until it is invalidated. We use a weak proxy so that the image view will get deallocated + // independent of the display link's lifetime. Upon image view deallocation, we invalidate the display + // link which will lead to the deallocation of both the display link and the weak proxy. + FLWeakProxy *weakProxy = [FLWeakProxy weakProxyForObject:self]; + self.displayLink = [CADisplayLink displayLinkWithTarget:weakProxy selector:@selector(displayDidRefresh:)]; + + [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:self.runLoopMode]; + } + + // Note: The display link's `.frameInterval` value of 1 (default) means getting callbacks at the refresh rate of the display (~60Hz). + // Setting it to 2 divides the frame rate by 2 and hence calls back at every other display refresh. + const NSTimeInterval kDisplayRefreshRate = 60.0; // 60Hz + self.displayLink.frameInterval = MAX([self frameDelayGreatestCommonDivisor] * kDisplayRefreshRate, 1); + + self.displayLink.paused = NO; + } else { + [super startAnimating]; + } +} + +- (void)setRunLoopMode:(NSString *)runLoopMode +{ + if (![@[NSDefaultRunLoopMode, NSRunLoopCommonModes] containsObject:runLoopMode]) { + NSAssert(NO, @"Invalid run loop mode: %@", runLoopMode); + _runLoopMode = [[self class] defaultRunLoopMode]; + } else { + _runLoopMode = runLoopMode; + } +} + +- (void)stopAnimating +{ + if (self.animatedImage) { + self.displayLink.paused = YES; + } else { + [super stopAnimating]; + } +} + + +- (BOOL)isAnimating +{ + BOOL isAnimating = NO; + if (self.animatedImage) { + isAnimating = self.displayLink && !self.displayLink.isPaused; + } else { + isAnimating = [super isAnimating]; + } + return isAnimating; +} + + +#pragma mark Highlighted Image Unsupport + +- (void)setHighlighted:(BOOL)highlighted +{ + // Highlighted image is unsupported for animated images, but implementing it breaks the image view when embedded in a UICollectionViewCell. + if (!self.animatedImage) { + [super setHighlighted:highlighted]; + } +} + + +#pragma mark - Private Methods +#pragma mark Animation + +// Don't repeatedly check our window & superview in `-displayDidRefresh:` for performance reasons. +// Just update our cached value whenever the animated image or visibility (window, superview, hidden, alpha) is changed. +- (void)updateShouldAnimate +{ + BOOL isVisible = self.window && self.superview && ![self isHidden] && self.alpha > 0.0; + self.shouldAnimate = self.animatedImage && isVisible; +} + + +- (void)displayDidRefresh:(CADisplayLink *)displayLink +{ + // If for some reason a wild call makes it through when we shouldn't be animating, bail. + // Early return! + if (!self.shouldAnimate) { + FLLog(FLLogLevelWarn, @"Trying to animate image when we shouldn't: %@", self); + return; + } + + NSNumber *delayTimeNumber = [self.animatedImage.delayTimesForIndexes objectForKey:@(self.currentFrameIndex)]; + // If we don't have a frame delay (e.g. corrupt frame), don't update the view but skip the playhead to the next frame (in else-block). + if (delayTimeNumber) { + NSTimeInterval delayTime = [delayTimeNumber floatValue]; + // If we have a nil image (e.g. waiting for frame), don't update the view nor playhead. + UIImage *image = [self.animatedImage imageLazilyCachedAtIndex:self.currentFrameIndex]; + if (image) { + FLLog(FLLogLevelVerbose, @"Showing frame %lu for animated image: %@", (unsigned long)self.currentFrameIndex, self.animatedImage); + self.currentFrame = image; + if (self.needsDisplayWhenImageBecomesAvailable) { + [self.layer setNeedsDisplay]; + self.needsDisplayWhenImageBecomesAvailable = NO; + } + + self.accumulator += displayLink.duration * displayLink.frameInterval; + + // While-loop first inspired by & good Karma to: https://github.com/ondalabs/OLImageView/blob/master/OLImageView.m + while (self.accumulator >= delayTime) { + self.accumulator -= delayTime; + self.currentFrameIndex++; + if (self.currentFrameIndex >= self.animatedImage.frameCount) { + // If we've looped the number of times that this animated image describes, stop looping. + self.loopCountdown--; + if (self.loopCompletionBlock) { + self.loopCompletionBlock(self.loopCountdown); + } + + if (self.loopCountdown == 0) { + [self stopAnimating]; + return; + } + self.currentFrameIndex = 0; + } + // Calling `-setNeedsDisplay` will just paint the current frame, not the new frame that we may have moved to. + // Instead, set `needsDisplayWhenImageBecomesAvailable` to `YES` -- this will paint the new image once loaded. + self.needsDisplayWhenImageBecomesAvailable = YES; + } + } else { + FLLog(FLLogLevelDebug, @"Waiting for frame %lu for animated image: %@", (unsigned long)self.currentFrameIndex, self.animatedImage); +#if defined(DEBUG) && DEBUG + if ([self.debug_delegate respondsToSelector:@selector(debug_animatedImageView:waitingForFrame:duration:)]) { + [self.debug_delegate debug_animatedImageView:self waitingForFrame:self.currentFrameIndex duration:(NSTimeInterval)displayLink.duration * displayLink.frameInterval]; + } +#endif + } + } else { + self.currentFrameIndex++; + } +} + ++ (NSString *)defaultRunLoopMode +{ + // Key off `activeProcessorCount` (as opposed to `processorCount`) since the system could shut down cores in certain situations. + return [NSProcessInfo processInfo].activeProcessorCount > 1 ? NSRunLoopCommonModes : NSDefaultRunLoopMode; +} + + +#pragma mark - CALayerDelegate (Informal) +#pragma mark Providing the Layer's Content + +- (void)displayLayer:(CALayer *)layer +{ + layer.contents = (__bridge id)self.image.CGImage; +} + + +@end +#pragma clang diagnostic pop diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLJSONAdapter.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLJSONAdapter.m new file mode 100644 index 0000000..965d381 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLJSONAdapter.m @@ -0,0 +1,679 @@ +// +// MTLJSONAdapter.m +// Mantle +// +// Created by Justin Spahr-Summers on 2013-02-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +#import "NSDictionary+MTLJSONKeyPath.h" + +#import "MTLEXTRuntimeExtensions.h" +#import "MTLEXTScope.h" +#import "MTLJSONAdapter.h" +#import "MTLModel.h" +#import "MTLTransformerErrorHandling.h" +#import "MTLReflection.h" +#import "NSValueTransformer+MTLPredefinedTransformerAdditions.h" +#import "MTLValueTransformer.h" + +NSString * const MTLJSONAdapterErrorDomain = @"MTLJSONAdapterErrorDomain"; +const NSInteger MTLJSONAdapterErrorNoClassFound = 2; +const NSInteger MTLJSONAdapterErrorInvalidJSONDictionary = 3; +const NSInteger MTLJSONAdapterErrorInvalidJSONMapping = 4; + +// An exception was thrown and caught. +const NSInteger MTLJSONAdapterErrorExceptionThrown = 1; + +// Associated with the NSException that was caught. +NSString * const MTLJSONAdapterThrownExceptionErrorKey = @"MTLJSONAdapterThrownException"; + +@interface MTLJSONAdapter () + +// The MTLModel subclass being parsed, or the class of `model` if parsing has +// completed. +@property (nonatomic, strong, readonly) Class modelClass; + +// A cached copy of the return value of +JSONKeyPathsByPropertyKey. +@property (nonatomic, copy, readonly) NSDictionary *JSONKeyPathsByPropertyKey; + +// A cached copy of the return value of -valueTransformersForModelClass: +@property (nonatomic, copy, readonly) NSDictionary *valueTransformersByPropertyKey; + +// Used to cache the JSON adapters returned by -JSONAdapterForModelClass:error:. +@property (nonatomic, strong, readonly) NSMapTable *JSONAdaptersByModelClass; + +// If +classForParsingJSONDictionary: returns a model class different from the +// one this adapter was initialized with, use this method to obtain a cached +// instance of a suitable adapter instead. +// +// modelClass - The class from which to parse the JSON. This class must conform +// to . This argument must not be nil. +// error - If not NULL, this may be set to an error that occurs during +// initializing the adapter. +// +// Returns a JSON adapter for modelClass, creating one of necessary. If no +// adapter could be created, nil is returned. +- (MTLJSONAdapter *)JSONAdapterForModelClass:(Class)modelClass error:(NSError **)error; + +// Collect all value transformers needed for a given class. +// +// modelClass - The class from which to parse the JSON. This class must conform +// to . This argument must not be nil. +// +// Returns a dictionary with the properties of modelClass that need +// transformation as keys and the value transformers as values. ++ (NSDictionary *)valueTransformersForModelClass:(Class)modelClass; + +@end + +@implementation MTLJSONAdapter + +#pragma mark Convenience methods + ++ (id)modelOfClass:(Class)modelClass fromJSONDictionary:(NSDictionary *)JSONDictionary error:(NSError **)error { + MTLJSONAdapter *adapter = [[self alloc] initWithModelClass:modelClass]; + + return [adapter modelFromJSONDictionary:JSONDictionary error:error]; +} + ++ (NSArray *)modelsOfClass:(Class)modelClass fromJSONArray:(NSArray *)JSONArray error:(NSError **)error { + if (JSONArray == nil || ![JSONArray isKindOfClass:NSArray.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Missing JSON array", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"%@ could not be created because an invalid JSON array was provided: %@", @""), NSStringFromClass(modelClass), JSONArray.class], + }; + *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorInvalidJSONDictionary userInfo:userInfo]; + } + return nil; + } + + NSMutableArray *models = [NSMutableArray arrayWithCapacity:JSONArray.count]; + for (NSDictionary *JSONDictionary in JSONArray){ + MTLModel *model = [self modelOfClass:modelClass fromJSONDictionary:JSONDictionary error:error]; + + if (model == nil) return nil; + + [models addObject:model]; + } + + return models; +} + ++ (NSDictionary *)JSONDictionaryFromModel:(id)model error:(NSError **)error { + MTLJSONAdapter *adapter = [[self alloc] initWithModelClass:model.class]; + + return [adapter JSONDictionaryFromModel:model error:error]; +} + ++ (NSArray *)JSONArrayFromModels:(NSArray *)models error:(NSError **)error { + NSParameterAssert(models != nil); + NSParameterAssert([models isKindOfClass:NSArray.class]); + + NSMutableArray *JSONArray = [NSMutableArray arrayWithCapacity:models.count]; + for (MTLModel *model in models) { + NSDictionary *JSONDictionary = [self JSONDictionaryFromModel:model error:error]; + if (JSONDictionary == nil) return nil; + + [JSONArray addObject:JSONDictionary]; + } + + return JSONArray; +} + +#pragma mark Lifecycle + +- (id)init { + NSAssert(NO, @"%@ must be initialized with a model class", self.class); + return nil; +} + +- (id)initWithModelClass:(Class)modelClass { + NSParameterAssert(modelClass != nil); + NSParameterAssert([modelClass conformsToProtocol:@protocol(MTLJSONSerializing)]); + + self = [super init]; + if (self == nil) return nil; + + _modelClass = modelClass; + + _JSONKeyPathsByPropertyKey = [modelClass JSONKeyPathsByPropertyKey]; + + NSSet *propertyKeys = [self.modelClass propertyKeys]; + + for (NSString *mappedPropertyKey in _JSONKeyPathsByPropertyKey) { + if (![propertyKeys containsObject:mappedPropertyKey]) { + NSAssert(NO, @"%@ is not a property of %@.", mappedPropertyKey, modelClass); + return nil; + } + + id value = _JSONKeyPathsByPropertyKey[mappedPropertyKey]; + + if ([value isKindOfClass:NSArray.class]) { + for (NSString *keyPath in value) { + if ([keyPath isKindOfClass:NSString.class]) continue; + + NSAssert(NO, @"%@ must either map to a JSON key path or a JSON array of key paths, got: %@.", mappedPropertyKey, value); + return nil; + } + } else if (![value isKindOfClass:NSString.class]) { + NSAssert(NO, @"%@ must either map to a JSON key path or a JSON array of key paths, got: %@.",mappedPropertyKey, value); + return nil; + } + } + + _valueTransformersByPropertyKey = [self.class valueTransformersForModelClass:modelClass]; + + _JSONAdaptersByModelClass = [NSMapTable strongToStrongObjectsMapTable]; + + return self; +} + +#pragma mark Serialization + +- (NSDictionary *)JSONDictionaryFromModel:(id)model error:(NSError **)error { + NSParameterAssert(model != nil); + NSParameterAssert([model isKindOfClass:self.modelClass]); + + if (self.modelClass != model.class) { + MTLJSONAdapter *otherAdapter = [self JSONAdapterForModelClass:model.class error:error]; + + return [otherAdapter JSONDictionaryFromModel:model error:error]; + } + + NSSet *propertyKeysToSerialize = [self serializablePropertyKeys:[NSSet setWithArray:self.JSONKeyPathsByPropertyKey.allKeys] forModel:model]; + + NSDictionary *dictionaryValue = [model.dictionaryValue dictionaryWithValuesForKeys:propertyKeysToSerialize.allObjects]; + NSMutableDictionary *JSONDictionary = [[NSMutableDictionary alloc] initWithCapacity:dictionaryValue.count]; + + __block BOOL success = YES; + __block NSError *tmpError = nil; + + [dictionaryValue enumerateKeysAndObjectsUsingBlock:^(NSString *propertyKey, id value, BOOL *stop) { + id JSONKeyPaths = self.JSONKeyPathsByPropertyKey[propertyKey]; + + if (JSONKeyPaths == nil) return; + + NSValueTransformer *transformer = self.valueTransformersByPropertyKey[propertyKey]; + if ([transformer.class allowsReverseTransformation]) { + // Map NSNull -> nil for the transformer, and then back for the + // dictionaryValue we're going to insert into. + if ([value isEqual:NSNull.null]) value = nil; + + if ([transformer respondsToSelector:@selector(reverseTransformedValue:success:error:)]) { + id errorHandlingTransformer = (id)transformer; + + value = [errorHandlingTransformer reverseTransformedValue:value success:&success error:&tmpError]; + + if (!success) { + *stop = YES; + return; + } + } else { + value = [transformer reverseTransformedValue:value] ?: NSNull.null; + } + } + + void (^createComponents)(id, NSString *) = ^(id obj, NSString *keyPath) { + NSArray *keyPathComponents = [keyPath componentsSeparatedByString:@"."]; + + // Set up dictionaries at each step of the key path. + for (NSString *component in keyPathComponents) { + if ([obj valueForKey:component] == nil) { + // Insert an empty mutable dictionary at this spot so that we + // can set the whole key path afterward. + [obj setValue:[NSMutableDictionary dictionary] forKey:component]; + } + + obj = [obj valueForKey:component]; + } + }; + + if ([JSONKeyPaths isKindOfClass:NSString.class]) { + createComponents(JSONDictionary, JSONKeyPaths); + + [JSONDictionary setValue:value forKeyPath:JSONKeyPaths]; + } + + if ([JSONKeyPaths isKindOfClass:NSArray.class]) { + for (NSString *JSONKeyPath in JSONKeyPaths) { + createComponents(JSONDictionary, JSONKeyPath); + + [JSONDictionary setValue:value[JSONKeyPath] forKeyPath:JSONKeyPath]; + } + } + }]; + + if (success) { + return JSONDictionary; + } else { + if (error != NULL) *error = tmpError; + + return nil; + } +} + +- (id)modelFromJSONDictionary:(NSDictionary *)JSONDictionary error:(NSError **)error { + if ([self.modelClass respondsToSelector:@selector(classForParsingJSONDictionary:)]) { + Class class = [self.modelClass classForParsingJSONDictionary:JSONDictionary]; + if (class == nil) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not parse JSON", @""), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"No model class could be found to parse the JSON dictionary.", @"") + }; + + *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorNoClassFound userInfo:userInfo]; + } + + return nil; + } + + if (class != self.modelClass) { + NSAssert([class conformsToProtocol:@protocol(MTLJSONSerializing)], @"Class %@ returned from +classForParsingJSONDictionary: does not conform to ", class); + + MTLJSONAdapter *otherAdapter = [self JSONAdapterForModelClass:class error:error]; + + return [otherAdapter modelFromJSONDictionary:JSONDictionary error:error]; + } + } + + if (JSONDictionary == nil || ![JSONDictionary isKindOfClass:NSDictionary.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Missing JSON dictionary", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"%@ could not be created because an invalid JSON dictionary was provided: %@", @""), NSStringFromClass(self.modelClass), JSONDictionary.class], + }; + *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorInvalidJSONDictionary userInfo:userInfo]; + } + return nil; + } + + NSMutableDictionary *dictionaryValue = [[NSMutableDictionary alloc] initWithCapacity:JSONDictionary.count]; + + for (NSString *propertyKey in [self.modelClass propertyKeys]) { + id JSONKeyPaths = self.JSONKeyPathsByPropertyKey[propertyKey]; + + if (JSONKeyPaths == nil) continue; + + id value; + + if ([JSONKeyPaths isKindOfClass:NSArray.class]) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + + for (NSString *keyPath in JSONKeyPaths) { + BOOL success = NO; + id value = [JSONDictionary mtl_valueForJSONKeyPath:keyPath success:&success error:error]; + + if (!success) return nil; + + if (value != nil) dictionary[keyPath] = value; + } + + value = dictionary; + } else { + BOOL success = NO; + value = [JSONDictionary mtl_valueForJSONKeyPath:JSONKeyPaths success:&success error:error]; + + if (!success) return nil; + } + + if (value == nil) continue; + + @try { + NSValueTransformer *transformer = self.valueTransformersByPropertyKey[propertyKey]; + if (transformer != nil) { + // Map NSNull -> nil for the transformer, and then back for the + // dictionary we're going to insert into. + if ([value isEqual:NSNull.null]) value = nil; + + if ([transformer respondsToSelector:@selector(transformedValue:success:error:)]) { + id errorHandlingTransformer = (id)transformer; + + BOOL success = YES; + value = [errorHandlingTransformer transformedValue:value success:&success error:error]; + + if (!success) return nil; + } else { + value = [transformer transformedValue:value]; + } + + if (value == nil) value = NSNull.null; + } + + dictionaryValue[propertyKey] = value; + } @catch (NSException *ex) { + NSLog(@"*** Caught exception %@ parsing JSON key path \"%@\" from: %@", ex, JSONKeyPaths, JSONDictionary); + + // Fail fast in Debug builds. + #if DEBUG + @throw ex; + #else + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Caught exception parsing JSON key path \"%@\" for model class: %@", JSONKeyPaths, self.modelClass], + NSLocalizedRecoverySuggestionErrorKey: ex.description, + NSLocalizedFailureReasonErrorKey: ex.reason, + MTLJSONAdapterThrownExceptionErrorKey: ex + }; + + *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorExceptionThrown userInfo:userInfo]; + } + + return nil; + #endif + } + } + + id model = [self.modelClass modelWithDictionary:dictionaryValue error:error]; + + return [model validate:error] ? model : nil; +} + ++ (NSDictionary *)valueTransformersForModelClass:(Class)modelClass { + NSParameterAssert(modelClass != nil); + NSParameterAssert([modelClass conformsToProtocol:@protocol(MTLJSONSerializing)]); + + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + + for (NSString *key in [modelClass propertyKeys]) { + SEL selector = MTLSelectorWithKeyPattern(key, "JSONTransformer"); + if ([modelClass respondsToSelector:selector]) { + IMP imp = [modelClass methodForSelector:selector]; + NSValueTransformer * (*function)(id, SEL) = (__typeof__(function))imp; + NSValueTransformer *transformer = function(modelClass, selector); + + if (transformer != nil) result[key] = transformer; + + continue; + } + + if ([modelClass respondsToSelector:@selector(JSONTransformerForKey:)]) { + NSValueTransformer *transformer = [modelClass JSONTransformerForKey:key]; + + if (transformer != nil) { + result[key] = transformer; + continue; + } + } + + objc_property_t property = class_getProperty(modelClass, key.UTF8String); + + if (property == NULL) continue; + + mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property); + @onExit { + free(attributes); + }; + + NSValueTransformer *transformer = nil; + + if (*(attributes->type) == *(@encode(id))) { + Class propertyClass = attributes->objectClass; + + if (propertyClass != nil) { + transformer = [self transformerForModelPropertiesOfClass:propertyClass]; + } + + + // For user-defined MTLModel, try parse it with dictionaryTransformer. + if (nil == transformer && [propertyClass conformsToProtocol:@protocol(MTLJSONSerializing)]) { + transformer = [self dictionaryTransformerWithModelClass:propertyClass]; + } + + if (transformer == nil) transformer = [NSValueTransformer mtl_validatingTransformerForClass:propertyClass ?: NSObject.class]; + } else { + transformer = [self transformerForModelPropertiesOfObjCType:attributes->type] ?: [NSValueTransformer mtl_validatingTransformerForClass:NSValue.class]; + } + + if (transformer != nil) result[key] = transformer; + } + + return result; +} + +- (MTLJSONAdapter *)JSONAdapterForModelClass:(Class)modelClass error:(NSError **)error { + NSParameterAssert(modelClass != nil); + NSParameterAssert([modelClass conformsToProtocol:@protocol(MTLJSONSerializing)]); + + @synchronized(self) { + MTLJSONAdapter *result = [self.JSONAdaptersByModelClass objectForKey:modelClass]; + + if (result != nil) return result; + + result = [[self.class alloc] initWithModelClass:modelClass]; + + if (result != nil) { + [self.JSONAdaptersByModelClass setObject:result forKey:modelClass]; + } + + return result; + } +} + +- (NSSet *)serializablePropertyKeys:(NSSet *)propertyKeys forModel:(id)model { + return propertyKeys; +} + ++ (NSValueTransformer *)transformerForModelPropertiesOfClass:(Class)modelClass { + NSParameterAssert(modelClass != nil); + + SEL selector = MTLSelectorWithKeyPattern(NSStringFromClass(modelClass), "JSONTransformer"); + if (![self respondsToSelector:selector]) return nil; + + IMP imp = [self methodForSelector:selector]; + NSValueTransformer * (*function)(id, SEL) = (__typeof__(function))imp; + NSValueTransformer *result = function(self, selector); + + return result; +} + ++ (NSValueTransformer *)transformerForModelPropertiesOfObjCType:(const char *)objCType { + NSParameterAssert(objCType != NULL); + + if (strcmp(objCType, @encode(BOOL)) == 0) { + return [NSValueTransformer valueTransformerForName:MTLBooleanValueTransformerName]; + } + + return nil; +} + +@end + +@implementation MTLJSONAdapter (ValueTransformers) + ++ (NSValueTransformer *)dictionaryTransformerWithModelClass:(Class)modelClass { + NSParameterAssert([modelClass conformsToProtocol:@protocol(MTLModel)]); + NSParameterAssert([modelClass conformsToProtocol:@protocol(MTLJSONSerializing)]); + __block MTLJSONAdapter *adapter; + + return [MTLValueTransformer + transformerUsingForwardBlock:^ id (id JSONDictionary, BOOL *success, NSError **error) { + if (JSONDictionary == nil) return nil; + + if (![JSONDictionary isKindOfClass:NSDictionary.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert JSON dictionary to model object", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSDictionary, got: %@", @""), JSONDictionary], + MTLTransformerErrorHandlingInputValueErrorKey : JSONDictionary + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + if (!adapter) { + adapter = [[self alloc] initWithModelClass:modelClass]; + } + id model = [adapter modelFromJSONDictionary:JSONDictionary error:error]; + if (model == nil) { + *success = NO; + } + + return model; + } + reverseBlock:^ NSDictionary * (id model, BOOL *success, NSError **error) { + if (model == nil) return nil; + + if (![model conformsToProtocol:@protocol(MTLModel)] || ![model conformsToProtocol:@protocol(MTLJSONSerializing)]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert model object to JSON dictionary", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected a MTLModel object conforming to , got: %@.", @""), model], + MTLTransformerErrorHandlingInputValueErrorKey : model + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + if (!adapter) { + adapter = [[self alloc] initWithModelClass:modelClass]; + } + NSDictionary *result = [adapter JSONDictionaryFromModel:model error:error]; + if (result == nil) { + *success = NO; + } + + return result; + }]; +} + ++ (NSValueTransformer *)arrayTransformerWithModelClass:(Class)modelClass { + id dictionaryTransformer = [self dictionaryTransformerWithModelClass:modelClass]; + + return [MTLValueTransformer + transformerUsingForwardBlock:^ id (NSArray *dictionaries, BOOL *success, NSError **error) { + if (dictionaries == nil) return nil; + + if (![dictionaries isKindOfClass:NSArray.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert JSON array to model array", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSArray, got: %@.", @""), dictionaries], + MTLTransformerErrorHandlingInputValueErrorKey : dictionaries + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + NSMutableArray *models = [NSMutableArray arrayWithCapacity:dictionaries.count]; + for (id JSONDictionary in dictionaries) { + if (JSONDictionary == NSNull.null) { + [models addObject:NSNull.null]; + continue; + } + + if (![JSONDictionary isKindOfClass:NSDictionary.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert JSON array to model array", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSDictionary or an NSNull, got: %@.", @""), JSONDictionary], + MTLTransformerErrorHandlingInputValueErrorKey : JSONDictionary + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + id model = [dictionaryTransformer transformedValue:JSONDictionary success:success error:error]; + + if (*success == NO) return nil; + + if (model == nil) continue; + + [models addObject:model]; + } + + return models; + } + reverseBlock:^ id (NSArray *models, BOOL *success, NSError **error) { + if (models == nil) return nil; + + if (![models isKindOfClass:NSArray.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert model array to JSON array", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSArray, got: %@.", @""), models], + MTLTransformerErrorHandlingInputValueErrorKey : models + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + NSMutableArray *dictionaries = [NSMutableArray arrayWithCapacity:models.count]; + for (id model in models) { + if (model == NSNull.null) { + [dictionaries addObject:NSNull.null]; + continue; + } + + if (![model isKindOfClass:MTLModel.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert JSON array to model array", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected a MTLModel or an NSNull, got: %@.", @""), model], + MTLTransformerErrorHandlingInputValueErrorKey : model + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + NSDictionary *dict = [dictionaryTransformer reverseTransformedValue:model success:success error:error]; + + if (*success == NO) return nil; + + if (dict == nil) continue; + + [dictionaries addObject:dict]; + } + + return dictionaries; + }]; +} + ++ (NSValueTransformer *)NSURLJSONTransformer { + return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName]; +} + ++ (NSValueTransformer *)NSUUIDJSONTransformer { + return [NSValueTransformer valueTransformerForName:MTLUUIDValueTransformerName]; +} + +@end + +@implementation MTLJSONAdapter (Deprecated) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" + ++ (NSArray *)JSONArrayFromModels:(NSArray *)models { + return [self JSONArrayFromModels:models error:NULL]; +} + ++ (NSDictionary *)JSONDictionaryFromModel:(MTLModel *)model { + return [self JSONDictionaryFromModel:model error:NULL]; +} + +#pragma clang diagnostic pop + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLModel+NSCoding.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLModel+NSCoding.m new file mode 100644 index 0000000..51358db --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLModel+NSCoding.m @@ -0,0 +1,261 @@ +// +// MTLModel+NSCoding.m +// Mantle +// +// Created by Justin Spahr-Summers on 2013-02-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "MTLEXTRuntimeExtensions.h" +#import "MTLEXTScope.h" +#import "MTLModel+NSCoding.h" +#import "MTLReflection.h" + +// Used in archives to store the modelVersion of the archived instance. +static NSString * const MTLModelVersionKey = @"MTLModelVersion"; + +// Used to cache the reflection performed in +allowedSecureCodingClassesByPropertyKey. +static void *MTLModelCachedAllowedClassesKey = &MTLModelCachedAllowedClassesKey; + +// Returns whether the given NSCoder requires secure coding. +static BOOL coderRequiresSecureCoding(NSCoder *coder) { + SEL requiresSecureCodingSelector = @selector(requiresSecureCoding); + + // Only invoke the method if it's implemented (i.e., only on OS X 10.8+ and + // iOS 6+). + if (![coder respondsToSelector:requiresSecureCodingSelector]) return NO; + + BOOL (*requiresSecureCodingIMP)(NSCoder *, SEL) = (__typeof__(requiresSecureCodingIMP))[coder methodForSelector:requiresSecureCodingSelector]; + if (requiresSecureCodingIMP == NULL) return NO; + + return requiresSecureCodingIMP(coder, requiresSecureCodingSelector); +} + +// Returns all of the given class' encodable property keys (those that will not +// be excluded from archives). +static NSSet *encodablePropertyKeysForClass(Class modelClass) { + return [[modelClass encodingBehaviorsByPropertyKey] keysOfEntriesPassingTest:^ BOOL (NSString *propertyKey, NSNumber *behavior, BOOL *stop) { + return behavior.unsignedIntegerValue != MTLModelEncodingBehaviorExcluded; + }]; +} + +// Verifies that all of the specified class' encodable property keys are present +// in +allowedSecureCodingClassesByPropertyKey, and throws an exception if not. +static void verifyAllowedClassesByPropertyKey(Class modelClass) { + NSDictionary *allowedClasses = [modelClass allowedSecureCodingClassesByPropertyKey]; + + NSMutableSet *specifiedPropertyKeys = [[NSMutableSet alloc] initWithArray:allowedClasses.allKeys]; + [specifiedPropertyKeys minusSet:encodablePropertyKeysForClass(modelClass)]; + + if (specifiedPropertyKeys.count > 0) { + [NSException raise:NSInvalidArgumentException format:@"Cannot encode %@ securely, because keys are missing from +allowedSecureCodingClassesByPropertyKey: %@", modelClass, specifiedPropertyKeys]; + } +} + +@implementation MTLModel (NSCoding) + +#pragma mark Versioning + ++ (NSUInteger)modelVersion { + return 0; +} + +#pragma mark Encoding Behaviors + ++ (NSDictionary *)encodingBehaviorsByPropertyKey { + NSSet *propertyKeys = self.propertyKeys; + NSMutableDictionary *behaviors = [[NSMutableDictionary alloc] initWithCapacity:propertyKeys.count]; + + for (NSString *key in propertyKeys) { + objc_property_t property = class_getProperty(self, key.UTF8String); + NSAssert(property != NULL, @"Could not find property \"%@\" on %@", key, self); + + mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property); + @onExit { + free(attributes); + }; + + MTLModelEncodingBehavior behavior = (attributes->weak ? MTLModelEncodingBehaviorConditional : MTLModelEncodingBehaviorUnconditional); + behaviors[key] = @(behavior); + } + + return behaviors; +} + ++ (NSDictionary *)allowedSecureCodingClassesByPropertyKey { + NSDictionary *cachedClasses = objc_getAssociatedObject(self, MTLModelCachedAllowedClassesKey); + if (cachedClasses != nil) return cachedClasses; + + // Get all property keys that could potentially be encoded. + NSSet *propertyKeys = [self.encodingBehaviorsByPropertyKey keysOfEntriesPassingTest:^ BOOL (NSString *propertyKey, NSNumber *behavior, BOOL *stop) { + return behavior.unsignedIntegerValue != MTLModelEncodingBehaviorExcluded; + }]; + + NSMutableDictionary *allowedClasses = [[NSMutableDictionary alloc] initWithCapacity:propertyKeys.count]; + + for (NSString *key in propertyKeys) { + objc_property_t property = class_getProperty(self, key.UTF8String); + NSAssert(property != NULL, @"Could not find property \"%@\" on %@", key, self); + + mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property); + @onExit { + free(attributes); + }; + + // If the property is not of object or class type, assume that it's + // a primitive which would be boxed into an NSValue. + if (attributes->type[0] != '@' && attributes->type[0] != '#') { + allowedClasses[key] = @[ NSValue.class ]; + continue; + } + + // Omit this property from the dictionary if its class isn't known. + if (attributes->objectClass != nil) { + allowedClasses[key] = @[ attributes->objectClass ]; + } + } + + // It doesn't really matter if we replace another thread's work, since we do + // it atomically and the result should be the same. + objc_setAssociatedObject(self, MTLModelCachedAllowedClassesKey, allowedClasses, OBJC_ASSOCIATION_COPY); + + return allowedClasses; +} + +- (id)decodeValueForKey:(NSString *)key withCoder:(NSCoder *)coder modelVersion:(NSUInteger)modelVersion { + NSParameterAssert(key != nil); + NSParameterAssert(coder != nil); + + SEL selector = MTLSelectorWithCapitalizedKeyPattern("decode", key, "WithCoder:modelVersion:"); + if ([self respondsToSelector:selector]) { + IMP imp = [self methodForSelector:selector]; + id (*function)(id, SEL, NSCoder *, NSUInteger) = (__typeof__(function))imp; + id result = function(self, selector, coder, modelVersion); + + return result; + } + + @try { + if (coderRequiresSecureCoding(coder)) { + NSArray *allowedClasses = self.class.allowedSecureCodingClassesByPropertyKey[key]; + NSAssert(allowedClasses != nil, @"No allowed classes specified for securely decoding key \"%@\" on %@", key, self.class); + + return [coder decodeObjectOfClasses:[NSSet setWithArray:allowedClasses] forKey:key]; + } else { + return [coder decodeObjectForKey:key]; + } + } @catch (NSException *ex) { + NSLog(@"*** Caught exception decoding value for key \"%@\" on class %@: %@", key, self.class, ex); + @throw ex; + } +} + +#pragma mark NSCoding + +- (instancetype)initWithCoder:(NSCoder *)coder { + BOOL requiresSecureCoding = coderRequiresSecureCoding(coder); + NSNumber *version = nil; + if (requiresSecureCoding) { + version = [coder decodeObjectOfClass:NSNumber.class forKey:MTLModelVersionKey]; + } else { + version = [coder decodeObjectForKey:MTLModelVersionKey]; + } + + if (version == nil) { + NSLog(@"Warning: decoding an archive of %@ without a version, assuming 0", self.class); + } else if (version.unsignedIntegerValue > self.class.modelVersion) { + // Don't try to decode newer versions. + return nil; + } + + if (requiresSecureCoding) { + verifyAllowedClassesByPropertyKey(self.class); + } else { + // Handle the old archive format. + NSDictionary *externalRepresentation = [coder decodeObjectForKey:@"externalRepresentation"]; + if (externalRepresentation != nil) { + NSAssert([self.class methodForSelector:@selector(dictionaryValueFromArchivedExternalRepresentation:version:)] != [MTLModel methodForSelector:@selector(dictionaryValueFromArchivedExternalRepresentation:version:)], @"Decoded an old archive of %@ that contains an externalRepresentation, but +dictionaryValueFromArchivedExternalRepresentation:version: is not overridden to handle it", self.class); + + NSDictionary *dictionaryValue = [self.class dictionaryValueFromArchivedExternalRepresentation:externalRepresentation version:version.unsignedIntegerValue]; + if (dictionaryValue == nil) return nil; + + NSError *error = nil; + self = [self initWithDictionary:dictionaryValue error:&error]; + if (self == nil) NSLog(@"*** Could not decode old %@ archive: %@", self.class, error); + + return self; + } + } + + NSSet *propertyKeys = self.class.propertyKeys; + NSMutableDictionary *dictionaryValue = [[NSMutableDictionary alloc] initWithCapacity:propertyKeys.count]; + + for (NSString *key in propertyKeys) { + id value = [self decodeValueForKey:key withCoder:coder modelVersion:version.unsignedIntegerValue]; + if (value == nil) continue; + + dictionaryValue[key] = value; + } + + NSError *error = nil; + self = [self initWithDictionary:dictionaryValue error:&error]; + if (self == nil) NSLog(@"*** Could not unarchive %@: %@", self.class, error); + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + if (coderRequiresSecureCoding(coder)) verifyAllowedClassesByPropertyKey(self.class); + + [coder encodeObject:@(self.class.modelVersion) forKey:MTLModelVersionKey]; + + NSDictionary *encodingBehaviors = self.class.encodingBehaviorsByPropertyKey; + [self.dictionaryValue enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { + @try { + // Skip nil values. + if ([value isEqual:NSNull.null]) return; + + switch ([encodingBehaviors[key] unsignedIntegerValue]) { + // This will also match a nil behavior. + case MTLModelEncodingBehaviorExcluded: + break; + + case MTLModelEncodingBehaviorUnconditional: + [coder encodeObject:value forKey:key]; + break; + + case MTLModelEncodingBehaviorConditional: + [coder encodeConditionalObject:value forKey:key]; + break; + + default: + NSAssert(NO, @"Unrecognized encoding behavior %@ on class %@ for key \"%@\"", self.class, encodingBehaviors[key], key); + } + } @catch (NSException *ex) { + NSLog(@"*** Caught exception encoding value for key \"%@\" on class %@: %@", key, self.class, ex); + @throw ex; + } + }]; +} + +#pragma mark NSSecureCoding + ++ (BOOL)supportsSecureCoding { + // Disable secure coding support by default, so subclasses are forced to + // opt-in by conforming to the protocol and overriding this method. + // + // We only implement this method because XPC complains if a subclass tries + // to implement it but does not override -initWithCoder:. See + // https://github.com/github/Mantle/issues/74. + return NO; +} + +@end + +@implementation MTLModel (OldArchiveSupport) + ++ (NSDictionary *)dictionaryValueFromArchivedExternalRepresentation:(NSDictionary *)externalRepresentation version:(NSUInteger)fromVersion { + return nil; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLModel.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLModel.m new file mode 100644 index 0000000..b3b756b --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLModel.m @@ -0,0 +1,334 @@ +// +// MTLModel.m +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-11. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import "MTLEXTRuntimeExtensions.h" +#import "MTLEXTScope.h" +#import "MTLModel.h" +#import "MTLReflection.h" +#import "NSError+MTLModelException.h" +#import + +// Used to cache the reflection performed in +propertyKeys. +static void *MTLModelCachedPropertyKeysKey = &MTLModelCachedPropertyKeysKey; + +// Associated in +generateAndCachePropertyKeys with a set of all transitory +// property keys. +static void *MTLModelCachedTransitoryPropertyKeysKey = &MTLModelCachedTransitoryPropertyKeysKey; + +// Associated in +generateAndCachePropertyKeys with a set of all permanent +// property keys. +static void *MTLModelCachedPermanentPropertyKeysKey = &MTLModelCachedPermanentPropertyKeysKey; + +// Validates a value for an object and sets it if necessary. +// +// obj - The object for which the value is being validated. This value +// must not be nil. +// key - The name of one of `obj`s properties. This value must not be +// nil. +// value - The new value for the property identified by `key`. +// forceUpdate - If set to `YES`, the value is being updated even if validating +// it did not change it. +// error - If not NULL, this may be set to any error that occurs during +// validation +// +// Returns YES if `value` could be validated and set, or NO if an error +// occurred. +static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUpdate, NSError **error) { + // Mark this as being autoreleased, because validateValue may return + // a new object to be stored in this variable (and we don't want ARC to + // double-free or leak the old or new values). + __autoreleasing id validatedValue = value; + + @try { + if (![obj validateValue:&validatedValue forKey:key error:error]) return NO; + + if (forceUpdate || value != validatedValue) { + [obj setValue:validatedValue forKey:key]; + } + + return YES; + } @catch (NSException *ex) { + NSLog(@"*** Caught exception setting key \"%@\" : %@", key, ex); + + // Fail fast in Debug builds. + #if DEBUG + @throw ex; + #else + if (error != NULL) { + *error = [NSError mtl_modelErrorWithException:ex]; + } + + return NO; + #endif + } +} + +@interface MTLModel () + +// Inspects all properties of returned by +propertyKeys using +// +storageBehaviorForPropertyWithKey and caches the results. ++ (void)generateAndCacheStorageBehaviors; + +// Returns a set of all property keys for which +// +storageBehaviorForPropertyWithKey returned MTLPropertyStorageTransitory. ++ (NSSet *)transitoryPropertyKeys; + +// Returns a set of all property keys for which +// +storageBehaviorForPropertyWithKey returned MTLPropertyStoragePermanent. ++ (NSSet *)permanentPropertyKeys; + +// Enumerates all properties of the receiver's class hierarchy, starting at the +// receiver, and continuing up until (but not including) MTLModel. +// +// The given block will be invoked multiple times for any properties declared on +// multiple classes in the hierarchy. ++ (void)enumeratePropertiesUsingBlock:(void (^)(objc_property_t property, BOOL *stop))block; + +@end + +@implementation MTLModel + +#pragma mark Lifecycle + ++ (void)generateAndCacheStorageBehaviors { + NSMutableSet *transitoryKeys = [NSMutableSet set]; + NSMutableSet *permanentKeys = [NSMutableSet set]; + + for (NSString *propertyKey in self.propertyKeys) { + switch ([self storageBehaviorForPropertyWithKey:propertyKey]) { + case MTLPropertyStorageNone: + break; + + case MTLPropertyStorageTransitory: + [transitoryKeys addObject:propertyKey]; + break; + + case MTLPropertyStoragePermanent: + [permanentKeys addObject:propertyKey]; + break; + } + } + + // It doesn't really matter if we replace another thread's work, since we do + // it atomically and the result should be the same. + objc_setAssociatedObject(self, MTLModelCachedTransitoryPropertyKeysKey, transitoryKeys, OBJC_ASSOCIATION_COPY); + objc_setAssociatedObject(self, MTLModelCachedPermanentPropertyKeysKey, permanentKeys, OBJC_ASSOCIATION_COPY); +} + ++ (instancetype)modelWithDictionary:(NSDictionary *)dictionary error:(NSError **)error { + return [[self alloc] initWithDictionary:dictionary error:error]; +} + +- (instancetype)init { + // Nothing special by default, but we have a declaration in the header. + return [super init]; +} + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary error:(NSError **)error { + self = [self init]; + if (self == nil) return nil; + + for (NSString *key in dictionary) { + // Mark this as being autoreleased, because validateValue may return + // a new object to be stored in this variable (and we don't want ARC to + // double-free or leak the old or new values). + __autoreleasing id value = [dictionary objectForKey:key]; + + if ([value isEqual:NSNull.null]) value = nil; + + BOOL success = MTLValidateAndSetValue(self, key, value, YES, error); + if (!success) return nil; + } + + return self; +} + +#pragma mark Reflection + ++ (void)enumeratePropertiesUsingBlock:(void (^)(objc_property_t property, BOOL *stop))block { + Class cls = self; + BOOL stop = NO; + + while (!stop && ![cls isEqual:MTLModel.class]) { + unsigned count = 0; + objc_property_t *properties = class_copyPropertyList(cls, &count); + + cls = cls.superclass; + if (properties == NULL) continue; + + @onExit { + free(properties); + }; + + for (unsigned i = 0; i < count; i++) { + block(properties[i], &stop); + if (stop) break; + } + } +} + ++ (NSSet *)propertyKeys { + NSSet *cachedKeys = objc_getAssociatedObject(self, MTLModelCachedPropertyKeysKey); + if (cachedKeys != nil) return cachedKeys; + + NSMutableSet *keys = [NSMutableSet set]; + + [self enumeratePropertiesUsingBlock:^(objc_property_t property, BOOL *stop) { + NSString *key = @(property_getName(property)); + + if ([self storageBehaviorForPropertyWithKey:key] != MTLPropertyStorageNone) { + [keys addObject:key]; + } + }]; + + // It doesn't really matter if we replace another thread's work, since we do + // it atomically and the result should be the same. + objc_setAssociatedObject(self, MTLModelCachedPropertyKeysKey, keys, OBJC_ASSOCIATION_COPY); + + return keys; +} + ++ (NSSet *)transitoryPropertyKeys { + NSSet *transitoryPropertyKeys = objc_getAssociatedObject(self, MTLModelCachedTransitoryPropertyKeysKey); + + if (transitoryPropertyKeys == nil) { + [self generateAndCacheStorageBehaviors]; + transitoryPropertyKeys = objc_getAssociatedObject(self, MTLModelCachedTransitoryPropertyKeysKey); + } + + return transitoryPropertyKeys; +} + ++ (NSSet *)permanentPropertyKeys { + NSSet *permanentPropertyKeys = objc_getAssociatedObject(self, MTLModelCachedPermanentPropertyKeysKey); + + if (permanentPropertyKeys == nil) { + [self generateAndCacheStorageBehaviors]; + permanentPropertyKeys = objc_getAssociatedObject(self, MTLModelCachedPermanentPropertyKeysKey); + } + + return permanentPropertyKeys; +} + +- (NSDictionary *)dictionaryValue { + NSSet *keys = [self.class.transitoryPropertyKeys setByAddingObjectsFromSet:self.class.permanentPropertyKeys]; + + return [self dictionaryWithValuesForKeys:keys.allObjects]; +} + ++ (MTLPropertyStorage)storageBehaviorForPropertyWithKey:(NSString *)propertyKey { + objc_property_t property = class_getProperty(self.class, propertyKey.UTF8String); + + if (property == NULL) return MTLPropertyStorageNone; + + mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property); + @onExit { + free(attributes); + }; + + BOOL hasGetter = [self instancesRespondToSelector:attributes->getter]; + BOOL hasSetter = [self instancesRespondToSelector:attributes->setter]; + if (!attributes->dynamic && attributes->ivar == NULL && !hasGetter && !hasSetter) { + return MTLPropertyStorageNone; + } else if (attributes->readonly && attributes->ivar == NULL) { + if ([self isEqual:MTLModel.class]) { + return MTLPropertyStorageNone; + } else { + // Check superclass in case the subclass redeclares a property that + // falls through + return [self.superclass storageBehaviorForPropertyWithKey:propertyKey]; + } + } else { + return MTLPropertyStoragePermanent; + } +} + +#pragma mark Merging + +- (void)mergeValueForKey:(NSString *)key fromModel:(NSObject *)model { + NSParameterAssert(key != nil); + + SEL selector = MTLSelectorWithCapitalizedKeyPattern("merge", key, "FromModel:"); + if (![self respondsToSelector:selector]) { + if (model != nil) { + [self setValue:[model valueForKey:key] forKey:key]; + } + + return; + } + + IMP imp = [self methodForSelector:selector]; + void (*function)(id, SEL, id) = (__typeof__(function))imp; + function(self, selector, model); +} + +- (void)mergeValuesForKeysFromModel:(id)model { + NSSet *propertyKeys = model.class.propertyKeys; + + for (NSString *key in self.class.propertyKeys) { + if (![propertyKeys containsObject:key]) continue; + + [self mergeValueForKey:key fromModel:model]; + } +} + +#pragma mark Validation + +- (BOOL)validate:(NSError **)error { + for (NSString *key in self.class.propertyKeys) { + id value = [self valueForKey:key]; + + BOOL success = MTLValidateAndSetValue(self, key, value, NO, error); + if (!success) return NO; + } + + return YES; +} + +#pragma mark NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + MTLModel *copy = [[self.class allocWithZone:zone] init]; + [copy setValuesForKeysWithDictionary:self.dictionaryValue]; + return copy; +} + +#pragma mark NSObject + +- (NSString *)description { + NSDictionary *permanentProperties = [self dictionaryWithValuesForKeys:self.class.permanentPropertyKeys.allObjects]; + + return [NSString stringWithFormat:@"<%@: %p> %@", self.class, self, permanentProperties]; +} + +- (NSUInteger)hash { + NSUInteger value = 0; + + for (NSString *key in self.class.permanentPropertyKeys) { + value ^= [[self valueForKey:key] hash]; + } + + return value; +} + +- (BOOL)isEqual:(MTLModel *)model { + if (self == model) return YES; + if (![model isMemberOfClass:self.class]) return NO; + + for (NSString *key in self.class.permanentPropertyKeys) { + id selfValue = [self valueForKey:key]; + id modelValue = [model valueForKey:key]; + + BOOL valuesEqual = ((selfValue == nil && modelValue == nil) || [selfValue isEqual:modelValue]); + if (!valuesEqual) return NO; + } + + return YES; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLReflection.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLReflection.h new file mode 100644 index 0000000..52c920b --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLReflection.h @@ -0,0 +1,31 @@ +// +// MTLReflection.h +// Mantle +// +// Created by Justin Spahr-Summers on 2013-03-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +/// Creates a selector from a key and a constant string. +/// +/// key - The key to insert into the generated selector. This key should be in +/// its natural case. +/// suffix - A string to append to the key as part of the selector. +/// +/// Returns a selector, or NULL if the input strings cannot form a valid +/// selector. +SEL MTLSelectorWithKeyPattern(NSString *key, const char *suffix) __attribute__((pure, nonnull(1, 2))); + +/// Creates a selector from a key and a constant prefix and suffix. +/// +/// prefix - A string to prepend to the key as part of the selector. +/// key - The key to insert into the generated selector. This key should be in +/// its natural case, and will have its first letter capitalized when +/// inserted. +/// suffix - A string to append to the key as part of the selector. +/// +/// Returns a selector, or NULL if the input strings cannot form a valid +/// selector. +SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, const char *suffix) __attribute__((pure, nonnull(1, 2, 3))); diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLReflection.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLReflection.m new file mode 100644 index 0000000..923e9e2 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLReflection.m @@ -0,0 +1,50 @@ +// +// MTLReflection.m +// Mantle +// +// Created by Justin Spahr-Summers on 2013-03-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "MTLReflection.h" +#import + +SEL MTLSelectorWithKeyPattern(NSString *key, const char *suffix) { + NSUInteger keyLength = [key maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + NSUInteger suffixLength = strlen(suffix); + + char selector[keyLength + suffixLength + 1]; + + BOOL success = [key getBytes:selector maxLength:keyLength usedLength:&keyLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, key.length) remainingRange:NULL]; + if (!success) return NULL; + + memcpy(selector + keyLength, suffix, suffixLength); + selector[keyLength + suffixLength] = '\0'; + + return sel_registerName(selector); +} + +SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, const char *suffix) { + NSUInteger prefixLength = strlen(prefix); + NSUInteger suffixLength = strlen(suffix); + + NSString *initial = [key substringToIndex:1].uppercaseString; + NSUInteger initialLength = [initial maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + + NSString *rest = [key substringFromIndex:1]; + NSUInteger restLength = [rest maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + + char selector[prefixLength + initialLength + restLength + suffixLength + 1]; + memcpy(selector, prefix, prefixLength); + + BOOL success = [initial getBytes:selector + prefixLength maxLength:initialLength usedLength:&initialLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, initial.length) remainingRange:NULL]; + if (!success) return NULL; + + success = [rest getBytes:selector + prefixLength + initialLength maxLength:restLength usedLength:&restLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, rest.length) remainingRange:NULL]; + if (!success) return NULL; + + memcpy(selector + prefixLength + initialLength + restLength, suffix, suffixLength); + selector[prefixLength + initialLength + restLength + suffixLength] = '\0'; + + return sel_registerName(selector); +} diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLTransformerErrorHandling.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLTransformerErrorHandling.m new file mode 100644 index 0000000..962e2c0 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLTransformerErrorHandling.m @@ -0,0 +1,15 @@ +// +// MTLTransformerErrorHandling.h +// Mantle +// +// Created by Robert Böhnke on 10/6/13. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "MTLTransformerErrorHandling.h" + +NSString * const MTLTransformerErrorHandlingErrorDomain = @"MTLTransformerErrorHandlingErrorDomain"; + +const NSInteger MTLTransformerErrorHandlingErrorInvalidInput = 1; + +NSString * const MTLTransformerErrorHandlingInputValueErrorKey = @"MTLTransformerErrorHandlingInputValueErrorKey"; diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLValueTransformer.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLValueTransformer.m new file mode 100644 index 0000000..701627b --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/MTLValueTransformer.m @@ -0,0 +1,150 @@ +// +// MTLValueTransformer.m +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-11. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import "MTLValueTransformer.h" + +// +// Any MTLValueTransformer supporting reverse transformation. Necessary because +// +allowsReverseTransformation is a class method. +// +@interface MTLReversibleValueTransformer : MTLValueTransformer +@end + +@interface MTLValueTransformer () + +@property (nonatomic, copy, readonly) MTLValueTransformerBlock forwardBlock; +@property (nonatomic, copy, readonly) MTLValueTransformerBlock reverseBlock; + +@end + +@implementation MTLValueTransformer + +#pragma mark Lifecycle + ++ (instancetype)transformerUsingForwardBlock:(MTLValueTransformerBlock)forwardBlock { + return [[self alloc] initWithForwardBlock:forwardBlock reverseBlock:nil]; +} + ++ (instancetype)transformerUsingReversibleBlock:(MTLValueTransformerBlock)reversibleBlock { + return [self transformerUsingForwardBlock:reversibleBlock reverseBlock:reversibleBlock]; +} + ++ (instancetype)transformerUsingForwardBlock:(MTLValueTransformerBlock)forwardBlock reverseBlock:(MTLValueTransformerBlock)reverseBlock { + return [[MTLReversibleValueTransformer alloc] initWithForwardBlock:forwardBlock reverseBlock:reverseBlock]; +} + +- (id)initWithForwardBlock:(MTLValueTransformerBlock)forwardBlock reverseBlock:(MTLValueTransformerBlock)reverseBlock { + NSParameterAssert(forwardBlock != nil); + + self = [super init]; + if (self == nil) return nil; + + _forwardBlock = [forwardBlock copy]; + _reverseBlock = [reverseBlock copy]; + + return self; +} + +#pragma mark NSValueTransformer + ++ (BOOL)allowsReverseTransformation { + return NO; +} + ++ (Class)transformedValueClass { + return NSObject.class; +} + +- (id)transformedValue:(id)value { + NSError *error = nil; + BOOL success = YES; + + return self.forwardBlock(value, &success, &error); +} + +- (id)transformedValue:(id)value success:(BOOL *)outerSuccess error:(NSError **)outerError { + NSError *error = nil; + BOOL success = YES; + + id transformedValue = self.forwardBlock(value, &success, &error); + + if (outerSuccess != NULL) *outerSuccess = success; + if (outerError != NULL) *outerError = error; + + return transformedValue; +} + +@end + +@implementation MTLReversibleValueTransformer + +#pragma mark Lifecycle + +- (id)initWithForwardBlock:(MTLValueTransformerBlock)forwardBlock reverseBlock:(MTLValueTransformerBlock)reverseBlock { + NSParameterAssert(reverseBlock != nil); + return [super initWithForwardBlock:forwardBlock reverseBlock:reverseBlock]; +} + +#pragma mark NSValueTransformer + ++ (BOOL)allowsReverseTransformation { + return YES; +} + +- (id)reverseTransformedValue:(id)value { + NSError *error = nil; + BOOL success = YES; + + return self.reverseBlock(value, &success, &error); +} + +- (id)reverseTransformedValue:(id)value success:(BOOL *)outerSuccess error:(NSError **)outerError { + NSError *error = nil; + BOOL success = YES; + + id transformedValue = self.reverseBlock(value, &success, &error); + + if (outerSuccess != NULL) *outerSuccess = success; + if (outerError != NULL) *outerError = error; + + return transformedValue; +} + +@end + + +@implementation MTLValueTransformer (Deprecated) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" + ++ (instancetype)transformerWithBlock:(id (^)(id))transformationBlock { + return [self transformerUsingForwardBlock:^(id value, BOOL *success, NSError **error) { + return transformationBlock(value); + }]; +} + ++ (instancetype)reversibleTransformerWithBlock:(id (^)(id))transformationBlock { + return [self transformerUsingReversibleBlock:^(id value, BOOL *success, NSError **error) { + return transformationBlock(value); + }]; +} + ++ (instancetype)reversibleTransformerWithForwardBlock:(id (^)(id))forwardBlock reverseBlock:(id (^)(id))reverseBlock { + return [self + transformerUsingForwardBlock:^(id value, BOOL *success, NSError **error) { + return forwardBlock(value); + } + reverseBlock:^(id value, BOOL *success, NSError **error) { + return reverseBlock(value); + }]; +} + +#pragma clang diagnostic pop + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSArray+MTLManipulationAdditions.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSArray+MTLManipulationAdditions.m new file mode 100644 index 0000000..e6932c9 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSArray+MTLManipulationAdditions.m @@ -0,0 +1,42 @@ +// +// NSArray+MTLManipulationAdditions.m +// Mantle +// +// Created by Josh Abernathy on 9/19/12. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import "NSArray+MTLManipulationAdditions.h" + +@interface NSArray (MTLDeclarations) + +// This declaration is needed so Mantle can be compiled with SDK 6 / 10.8. +- (id)firstObject; + +@end + +@implementation NSArray (MTLManipulationAdditions) + +- (id)mtl_firstObject { + return self.firstObject; +} + +- (instancetype)mtl_arrayByRemovingObject:(id)object { + NSMutableArray *result = [self mutableCopy]; + [result removeObject:object]; + return result; +} + +- (instancetype)mtl_arrayByRemovingFirstObject { + if (self.count == 0) return self; + + return [self subarrayWithRange:NSMakeRange(1, self.count - 1)]; +} + +- (instancetype)mtl_arrayByRemovingLastObject { + if (self.count == 0) return self; + + return [self subarrayWithRange:NSMakeRange(0, self.count - 1)]; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLJSONKeyPath.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLJSONKeyPath.h new file mode 100644 index 0000000..eb70151 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLJSONKeyPath.h @@ -0,0 +1,27 @@ +// +// NSDictionary+MTLJSONKeyPath.h +// Mantle +// +// Created by Robert Böhnke on 19/03/14. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +#import + +@interface NSDictionary (MTLJSONKeyPath) + +/// Looks up the value of a key path in the receiver. +/// +/// JSONKeyPath - The key path that should be resolved. Every element along this +/// key path needs to be an instance of NSDictionary for the +/// resolving to be successful. +/// success - If not NULL, this will be set to a boolean indicating whether +/// the key path was resolved successfully. +/// error - If not NULL, this may be set to an error that occurs during +/// resolving the value. +/// +/// Returns the value for the key path which may be nil. Clients should inspect +/// the success parameter to decide how to proceed with the result. +- (id)mtl_valueForJSONKeyPath:(NSString *)JSONKeyPath success:(BOOL *)success error:(NSError **)error; + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLJSONKeyPath.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLJSONKeyPath.m new file mode 100644 index 0000000..03ee2ad --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLJSONKeyPath.m @@ -0,0 +1,47 @@ +// +// NSDictionary+MTLJSONKeyPath.m +// Mantle +// +// Created by Robert Böhnke on 19/03/14. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +#import "NSDictionary+MTLJSONKeyPath.h" + +#import "MTLJSONAdapter.h" + +@implementation NSDictionary (MTLJSONKeyPath) + +- (id)mtl_valueForJSONKeyPath:(NSString *)JSONKeyPath success:(BOOL *)success error:(NSError **)error { + NSArray *components = [JSONKeyPath componentsSeparatedByString:@"."]; + + id result = self; + for (NSString *component in components) { + // Check the result before resolving the key path component to not + // affect the last value of the path. + if (result == nil || result == NSNull.null) break; + + if (![result isKindOfClass:NSDictionary.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid JSON dictionary", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"JSON key path %1$@ could not resolved because an incompatible JSON dictionary was supplied: \"%2$@\"", @""), JSONKeyPath, self] + }; + + *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorInvalidJSONDictionary userInfo:userInfo]; + } + + if (success != NULL) *success = NO; + + return nil; + } + + result = result[component]; + } + + if (success != NULL) *success = YES; + + return result; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLManipulationAdditions.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLManipulationAdditions.m new file mode 100644 index 0000000..a7a1d7a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLManipulationAdditions.m @@ -0,0 +1,38 @@ +// +// NSDictionary+MTLManipulationAdditions.m +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-24. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import "NSDictionary+MTLManipulationAdditions.h" + +@implementation NSDictionary (MTLManipulationAdditions) + +- (NSDictionary *)mtl_dictionaryByAddingEntriesFromDictionary:(NSDictionary *)dictionary { + NSMutableDictionary *result = [self mutableCopy]; + [result addEntriesFromDictionary:dictionary]; + return result; +} + +- (NSDictionary *)mtl_dictionaryByRemovingValuesForKeys:(NSArray *)keys { + NSMutableDictionary *result = [self mutableCopy]; + [result removeObjectsForKeys:keys]; + return result; +} + +@end + +@implementation NSDictionary (MTLManipulationAdditions_Deprecated) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + +- (NSDictionary *)mtl_dictionaryByRemovingEntriesWithKeys:(NSSet *)keys { + return [self mtl_dictionaryByRemovingValuesForKeys:keys.allObjects]; +} + +#pragma clang diagnostic pop + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLMappingAdditions.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLMappingAdditions.m new file mode 100644 index 0000000..6071906 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSDictionary+MTLMappingAdditions.m @@ -0,0 +1,23 @@ +// +// NSDictionary+MTLMappingAdditions.m +// Mantle +// +// Created by Robert Böhnke on 10/31/13. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "MTLModel.h" + +#import "NSDictionary+MTLMappingAdditions.h" + +@implementation NSDictionary (MTLMappingAdditions) + ++ (NSDictionary *)mtl_identityPropertyMapWithModel:(Class)modelClass { + NSCParameterAssert([modelClass conformsToProtocol:@protocol(MTLModel)]); + + NSArray *propertyKeys = [modelClass propertyKeys].allObjects; + + return [NSDictionary dictionaryWithObjects:propertyKeys forKeys:propertyKeys]; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSError+MTLModelException.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSError+MTLModelException.h new file mode 100644 index 0000000..852fbdb --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSError+MTLModelException.h @@ -0,0 +1,23 @@ +// +// NSError+MTLModelException.h +// Mantle +// +// Created by Robert Böhnke on 7/6/13. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +@interface NSError (MTLModelException) + +/// Creates a new error for an exception that occurred during updating an +/// MTLModel. +/// +/// exception - The exception that was thrown while updating the model. +/// This argument must not be nil. +/// +/// Returns an error that takes its localized description and failure reason +/// from the exception. ++ (instancetype)mtl_modelErrorWithException:(NSException *)exception; + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSError+MTLModelException.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSError+MTLModelException.m new file mode 100644 index 0000000..8b71e06 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSError+MTLModelException.m @@ -0,0 +1,36 @@ +// +// NSError+MTLModelException.m +// Mantle +// +// Created by Robert Böhnke on 7/6/13. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "MTLModel.h" + +#import "NSError+MTLModelException.h" + +// The domain for errors originating from MTLModel. +static NSString * const MTLModelErrorDomain = @"MTLModelErrorDomain"; + +// An exception was thrown and caught. +static const NSInteger MTLModelErrorExceptionThrown = 1; + +// Associated with the NSException that was caught. +static NSString * const MTLModelThrownExceptionErrorKey = @"MTLModelThrownException"; + +@implementation NSError (MTLModelException) + ++ (instancetype)mtl_modelErrorWithException:(NSException *)exception { + NSParameterAssert(exception != nil); + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: exception.description, + NSLocalizedFailureReasonErrorKey: exception.reason, + MTLModelThrownExceptionErrorKey: exception + }; + + return [NSError errorWithDomain:MTLModelErrorDomain code:MTLModelErrorExceptionThrown userInfo:userInfo]; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSObject+MTLComparisonAdditions.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSObject+MTLComparisonAdditions.m new file mode 100644 index 0000000..3b77b35 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSObject+MTLComparisonAdditions.m @@ -0,0 +1,16 @@ +// +// NSObject+MTLComparisonAdditions.m +// Mantle +// +// Created by Josh Vera on 10/26/12. +// Copyright (c) 2012 GitHub. All rights reserved. +// +// Portions copyright (c) 2011 Bitswift. All rights reserved. +// See the LICENSE file for more information. +// + +#import "NSObject+MTLComparisonAdditions.h" + +BOOL MTLEqualObjects(id obj1, id obj2) { + return (obj1 == obj2 || [obj1 isEqual:obj2]); +} diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSValueTransformer+MTLInversionAdditions.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSValueTransformer+MTLInversionAdditions.m new file mode 100644 index 0000000..2e536d6 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSValueTransformer+MTLInversionAdditions.m @@ -0,0 +1,37 @@ +// +// NSValueTransformer+MTLInversionAdditions.m +// Mantle +// +// Created by Justin Spahr-Summers on 2013-05-18. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import "NSValueTransformer+MTLInversionAdditions.h" +#import "MTLTransformerErrorHandling.h" +#import "MTLValueTransformer.h" + +@implementation NSValueTransformer (MTLInversionAdditions) + +- (NSValueTransformer *)mtl_invertedTransformer { + NSParameterAssert(self.class.allowsReverseTransformation); + + if ([self conformsToProtocol:@protocol(MTLTransformerErrorHandling)]) { + NSParameterAssert([self respondsToSelector:@selector(reverseTransformedValue:success:error:)]); + + id errorHandlingSelf = (id)self; + + return [MTLValueTransformer transformerUsingForwardBlock:^(id value, BOOL *success, NSError **error) { + return [errorHandlingSelf reverseTransformedValue:value success:success error:error]; + } reverseBlock:^(id value, BOOL *success, NSError **error) { + return [errorHandlingSelf transformedValue:value success:success error:error]; + }]; + } else { + return [MTLValueTransformer transformerUsingForwardBlock:^(id value, BOOL *success, NSError **error) { + return [self reverseTransformedValue:value]; + } reverseBlock:^(id value, BOOL *success, NSError **error) { + return [self transformedValue:value]; + }]; + } +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m new file mode 100644 index 0000000..2ef182b --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m @@ -0,0 +1,447 @@ +// +// NSValueTransformer+MTLPredefinedTransformerAdditions.m +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-27. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import "NSValueTransformer+MTLPredefinedTransformerAdditions.h" +#import "MTLJSONAdapter.h" +#import "MTLModel.h" +#import "MTLValueTransformer.h" + +NSString * const MTLURLValueTransformerName = @"MTLURLValueTransformerName"; +NSString * const MTLUUIDValueTransformerName = @"MTLUUIDValueTransformerName"; +NSString * const MTLBooleanValueTransformerName = @"MTLBooleanValueTransformerName"; + +@implementation NSValueTransformer (MTLPredefinedTransformerAdditions) + +#pragma mark Category Loading + ++ (void)load { + @autoreleasepool { + MTLValueTransformer *URLValueTransformer = [MTLValueTransformer + transformerUsingForwardBlock:^ id (NSString *str, BOOL *success, NSError **error) { + if (str == nil) return nil; + + if (![str isKindOfClass:NSString.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert string to URL", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSString, got: %@.", @""), str], + MTLTransformerErrorHandlingInputValueErrorKey : str + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + NSURL *result = [NSURL URLWithString:str]; + + if (result == nil) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert string to URL", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Input URL string %@ was malformed", @""), str], + MTLTransformerErrorHandlingInputValueErrorKey : str + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + return result; + } + reverseBlock:^ id (NSURL *URL, BOOL *success, NSError **error) { + if (URL == nil) return nil; + + if (![URL isKindOfClass:NSURL.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert URL to string", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSURL, got: %@.", @""), URL], + MTLTransformerErrorHandlingInputValueErrorKey : URL + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + return URL.absoluteString; + }]; + + [NSValueTransformer setValueTransformer:URLValueTransformer forName:MTLURLValueTransformerName]; + + MTLValueTransformer *UUIDValueTransformer = [MTLValueTransformer + transformerUsingForwardBlock:^id(NSString *string, BOOL *success, NSError **error) { + if (string == nil) return nil; + + if (![string isKindOfClass:NSString.class]) { + if (error) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert string to UUID", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSString, got: %@.", @""), string], + MTLTransformerErrorHandlingInputValueErrorKey : string + }; + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + NSUUID *result = [[NSUUID alloc] initWithUUIDString:string]; + + if (result == nil) { + if (error) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert string to UUID", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Input UUID string %@ was malformed", @""), string], + MTLTransformerErrorHandlingInputValueErrorKey : string + }; + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + return result; + } + reverseBlock:^id(NSUUID *uuid, BOOL *success, NSError **error) { + if (uuid == nil) return nil; + + if (![uuid isKindOfClass:NSUUID.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert UUID to string", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSUUID, got: %@.", @""), uuid], + MTLTransformerErrorHandlingInputValueErrorKey : uuid}; + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + return uuid.UUIDString; + }]; + + [NSValueTransformer setValueTransformer:UUIDValueTransformer forName:MTLUUIDValueTransformerName]; + + MTLValueTransformer *booleanValueTransformer = [MTLValueTransformer + transformerUsingReversibleBlock:^ id (NSNumber *boolean, BOOL *success, NSError **error) { + if (boolean == nil) return nil; + + if (![boolean isKindOfClass:NSNumber.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not convert number to boolean-backed number or vice-versa", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSNumber, got: %@.", @""), boolean], + MTLTransformerErrorHandlingInputValueErrorKey : boolean + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + return (NSNumber *)(boolean.boolValue ? kCFBooleanTrue : kCFBooleanFalse); + }]; + + [NSValueTransformer setValueTransformer:booleanValueTransformer forName:MTLBooleanValueTransformerName]; + } +} + +#pragma mark Customizable Transformers + ++ (NSValueTransformer *)mtl_arrayMappingTransformerWithTransformer:(NSValueTransformer *)transformer { + NSParameterAssert(transformer != nil); + + id (^forwardBlock)(NSArray *values, BOOL *success, NSError **error) = ^ id (NSArray *values, BOOL *success, NSError **error) { + if (values == nil) return nil; + + if (![values isKindOfClass:NSArray.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not transform non-array type", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSArray, got: %@.", @""), values], + MTLTransformerErrorHandlingInputValueErrorKey: values + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + NSMutableArray *transformedValues = [NSMutableArray arrayWithCapacity:values.count]; + NSInteger index = -1; + for (id value in values) { + index++; + if (value == NSNull.null) { + [transformedValues addObject:NSNull.null]; + continue; + } + + id transformedValue = nil; + if ([transformer conformsToProtocol:@protocol(MTLTransformerErrorHandling)]) { + NSError *underlyingError = nil; + transformedValue = [(id)transformer transformedValue:value success:success error:&underlyingError]; + + if (*success == NO) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not transform array", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Could not transform value at index %ld", @""), (long)index], + NSUnderlyingErrorKey: underlyingError, + MTLTransformerErrorHandlingInputValueErrorKey: values + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + return nil; + } + } else { + transformedValue = [transformer transformedValue:value]; + } + + if (transformedValue == nil) continue; + + [transformedValues addObject:transformedValue]; + } + + return transformedValues; + }; + + id (^reverseBlock)(NSArray *values, BOOL *success, NSError **error) = nil; + if (transformer.class.allowsReverseTransformation) { + reverseBlock = ^ id (NSArray *values, BOOL *success, NSError **error) { + if (values == nil) return nil; + + if (![values isKindOfClass:NSArray.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not transform non-array type", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSArray, got: %@.", @""), values], + MTLTransformerErrorHandlingInputValueErrorKey: values + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + NSMutableArray *transformedValues = [NSMutableArray arrayWithCapacity:values.count]; + NSInteger index = -1; + for (id value in values) { + index++; + if (value == NSNull.null) { + [transformedValues addObject:NSNull.null]; + + continue; + } + + id transformedValue = nil; + if ([transformer respondsToSelector:@selector(reverseTransformedValue:success:error:)]) { + NSError *underlyingError = nil; + transformedValue = [(id)transformer reverseTransformedValue:value success:success error:&underlyingError]; + + if (*success == NO) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Could not transform array", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Could not transform value at index %ld", @""), (long)index], + NSUnderlyingErrorKey: underlyingError, + MTLTransformerErrorHandlingInputValueErrorKey: values + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + return nil; + } + } else { + transformedValue = [transformer reverseTransformedValue:value]; + } + + if (transformedValue == nil) continue; + + [transformedValues addObject:transformedValue]; + } + + return transformedValues; + }; + } + if (reverseBlock != nil) { + return [MTLValueTransformer transformerUsingForwardBlock:forwardBlock reverseBlock:reverseBlock]; + } else { + return [MTLValueTransformer transformerUsingForwardBlock:forwardBlock]; + } +} + ++ (NSValueTransformer *)mtl_validatingTransformerForClass:(Class)modelClass { + NSParameterAssert(modelClass != nil); + + return [MTLValueTransformer transformerUsingForwardBlock:^ id (id value, BOOL *success, NSError **error) { + if (value != nil && ![value isKindOfClass:modelClass]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Value did not match expected type", @""), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected %1$@ to be of class %2$@ but got %3$@", @""), value, modelClass, [value class]], + MTLTransformerErrorHandlingInputValueErrorKey : value + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + return value; + }]; +} + ++ (NSValueTransformer *)mtl_valueMappingTransformerWithDictionary:(NSDictionary *)dictionary defaultValue:(id)defaultValue reverseDefaultValue:(id)reverseDefaultValue { + NSParameterAssert(dictionary != nil); + NSParameterAssert(dictionary.count == [[NSSet setWithArray:dictionary.allValues] count]); + + return [MTLValueTransformer + transformerUsingForwardBlock:^ id (id key, BOOL *success, NSError **error) { + return dictionary[key ?: NSNull.null] ?: defaultValue; + } + reverseBlock:^ id (id value, BOOL *success, NSError **error) { + __block id result = nil; + [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id anObject, BOOL *stop) { + if ([value isEqual:anObject]) { + result = key; + *stop = YES; + } + }]; + + return result ?: reverseDefaultValue; + }]; +} + ++ (NSValueTransformer *)mtl_valueMappingTransformerWithDictionary:(NSDictionary *)dictionary { + return [self mtl_valueMappingTransformerWithDictionary:dictionary defaultValue:nil reverseDefaultValue:nil]; +} + ++ (NSValueTransformer *)mtl_dateTransformerWithDateFormat:(NSString *)dateFormat calendar:(NSCalendar *)calendar locale:(NSLocale *)locale timeZone:(NSTimeZone *)timeZone defaultDate:(NSDate *)defaultDate { + NSParameterAssert(dateFormat.length); + + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = dateFormat; + dateFormatter.calendar = calendar; + dateFormatter.locale = locale; + dateFormatter.timeZone = timeZone; + dateFormatter.defaultDate = defaultDate; + + return [NSValueTransformer mtl_transformerWithFormatter:dateFormatter forObjectClass:NSDate.class]; +} + + ++ (NSValueTransformer *)mtl_dateTransformerWithDateFormat:(NSString *)dateFormat locale:(NSLocale *)locale { + return [self mtl_dateTransformerWithDateFormat:dateFormat calendar:nil locale:locale timeZone:nil defaultDate:nil]; +} + ++ (NSValueTransformer *)mtl_numberTransformerWithNumberStyle:(NSNumberFormatterStyle)numberStyle locale:(NSLocale *)locale { + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + numberFormatter.numberStyle = numberStyle; + numberFormatter.locale = locale; + + return [self mtl_transformerWithFormatter:numberFormatter forObjectClass:NSNumber.class]; +} + ++ (NSValueTransformer *)mtl_transformerWithFormatter:(NSFormatter *)formatter forObjectClass:(Class)objectClass { + NSParameterAssert(formatter != nil); + NSParameterAssert(objectClass != nil); + return [MTLValueTransformer + transformerUsingForwardBlock:^ id (NSString *str, BOOL *success, NSError *__autoreleasing *error) { + if (str == nil) return nil; + + if (![str isKindOfClass:NSString.class]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"Could not convert string to %@", @""), objectClass], + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an NSString as input, got: %@.", @""), str], + MTLTransformerErrorHandlingInputValueErrorKey : str + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + id object = nil; + NSString *errorDescription = nil; + *success = [formatter getObjectValue:&object forString:str errorDescription:&errorDescription]; + + if (errorDescription != nil) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"Could not convert string to %@", @""), objectClass], + NSLocalizedFailureReasonErrorKey: errorDescription, + MTLTransformerErrorHandlingInputValueErrorKey : str + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + if (![object isKindOfClass:objectClass]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"Could not convert string to %@", @""), objectClass], + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an %@ as output from the formatter, got: %@.", @""), objectClass, object], + }; + + *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFormattingError userInfo:userInfo]; + } + *success = NO; + return nil; + } + + return object; + } reverseBlock:^id(id object, BOOL *success, NSError *__autoreleasing *error) { + if (object == nil) return nil; + + if (![object isKindOfClass:objectClass]) { + if (error != NULL) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"Could not convert %@ to string", @""), objectClass], + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"Expected an %@ as input, got: %@.", @""), objectClass, object], + MTLTransformerErrorHandlingInputValueErrorKey : object + }; + + *error = [NSError errorWithDomain:MTLTransformerErrorHandlingErrorDomain code:MTLTransformerErrorHandlingErrorInvalidInput userInfo:userInfo]; + } + *success = NO; + return nil; + } + + NSString *string = [formatter stringForObjectValue:object]; + *success = (string != nil); + return string; + }]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" + ++ (NSValueTransformer *)mtl_JSONDictionaryTransformerWithModelClass:(Class)modelClass { + return [MTLJSONAdapter dictionaryTransformerWithModelClass:modelClass]; +} + ++ (NSValueTransformer *)mtl_JSONArrayTransformerWithModelClass:(Class)modelClass { + return [MTLJSONAdapter arrayTransformerWithModelClass:modelClass]; +} + +#pragma clang diagnostic pop + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/MTLEXTRuntimeExtensions.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/MTLEXTRuntimeExtensions.m new file mode 100644 index 0000000..4a4f397 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/MTLEXTRuntimeExtensions.m @@ -0,0 +1,944 @@ +// +// MTLEXTRuntimeExtensions.m +// extobjc +// +// Created by Justin Spahr-Summers on 2011-03-05. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import "MTLEXTRuntimeExtensions.h" +#import +#import +#import +#import +#import +#import +#import +#import + +typedef NSMethodSignature *(*methodSignatureForSelectorIMP)(id, SEL, SEL); +typedef void (^mtl_specialProtocolInjectionBlock)(Class); + +// a `const char *` equivalent to system struct objc_method_description +typedef struct { + SEL name; + const char *types; +} mtl_methodDescription; + +// contains the information needed to reference a full special protocol +typedef struct { + // the actual protocol declaration (@protocol block) + __unsafe_unretained Protocol *protocol; + + // the injection block associated with this protocol + // + // this block is RETAINED and must eventually be released by transferring it + // back to ARC + void *injectionBlock; + + // whether this protocol is ready to be injected to its conforming classes + // + // this does NOT refer to a special protocol having been injected already + BOOL ready; +} MTLSpecialProtocol; + +// the full list of special protocols (an array of MTLSpecialProtocol structs) +static MTLSpecialProtocol * restrict specialProtocols = NULL; + +// the number of special protocols stored in the array +static size_t specialProtocolCount = 0; + +// the total capacity of the array +// we use a doubling algorithm to amortize the cost of insertion, so this is +// generally going to be a power-of-two +static size_t specialProtocolCapacity = 0; + +// the number of MTLSpecialProtocols which have been marked as ready for +// injection (though not necessary injected) +// +// in other words, the total count which have 'ready' set to YES +static size_t specialProtocolsReady = 0; + +// a mutex is used to guard against multiple threads changing the above static +// variables +static pthread_mutex_t specialProtocolsLock = PTHREAD_MUTEX_INITIALIZER; + +/** + * This function actually performs the hard work of special protocol injection. + * It obtains a full list of all classes registered with the Objective-C + * runtime, finds those conforming to special protocols, and then runs the + * injection blocks as appropriate. + */ +static void mtl_injectSpecialProtocols (void) { + /* + * don't lock specialProtocolsLock in this function, as it is called only + * from public functions which already perform the synchronization + */ + + /* + * This will sort special protocols in the order they should be loaded. If + * a special protocol conforms to another special protocol, the former + * will be prioritized above the latter. + */ + qsort_b(specialProtocols, specialProtocolCount, sizeof(MTLSpecialProtocol), ^(const void *a, const void *b){ + // if the pointers are equal, it must be the same protocol + if (a == b) + return 0; + + const MTLSpecialProtocol *protoA = a; + const MTLSpecialProtocol *protoB = b; + + // A higher return value here means a higher priority + int (^protocolInjectionPriority)(const MTLSpecialProtocol *) = ^(const MTLSpecialProtocol *specialProtocol){ + int runningTotal = 0; + + for (size_t i = 0;i < specialProtocolCount;++i) { + // the pointer passed into this block is guaranteed to point + // into the 'specialProtocols' array, so we can compare the + // pointers directly for identity + if (specialProtocol == specialProtocols + i) + continue; + + if (protocol_conformsToProtocol(specialProtocol->protocol, specialProtocols[i].protocol)) + runningTotal++; + } + + return runningTotal; + }; + + /* + * This will return: + * 0 if the protocols are equal in priority (such that load order does not matter) + * < 0 if A is more important than B + * > 0 if B is more important than A + */ + return protocolInjectionPriority(protoB) - protocolInjectionPriority(protoA); + }); + + unsigned classCount = objc_getClassList(NULL, 0); + if (!classCount) { + fprintf(stderr, "ERROR: No classes registered with the runtime\n"); + return; + } + + Class *allClasses = (Class *)malloc(sizeof(Class) * (classCount + 1)); + if (!allClasses) { + fprintf(stderr, "ERROR: Could not allocate space for %u classes\n", classCount); + return; + } + + // use this instead of mtl_copyClassList() to avoid sending +initialize to + // classes that we don't plan to inject into (this avoids some SenTestingKit + // timing issues) + classCount = objc_getClassList(allClasses, classCount); + + /* + * set up an autorelease pool in case any Cocoa classes get used during + * the injection process or +initialize + */ + @autoreleasepool { + // loop through the special protocols, and apply each one to all the + // classes in turn + // + // ORDER IS IMPORTANT HERE: protocols have to be injected to all classes in + // the order in which they appear in specialProtocols. Consider classes + // X and Y that implement protocols A and B, respectively. B needs to get + // its implementation into Y before A gets into X. + for (size_t i = 0;i < specialProtocolCount;++i) { + Protocol *protocol = specialProtocols[i].protocol; + + // transfer ownership of the injection block to ARC and remove it + // from the structure + mtl_specialProtocolInjectionBlock injectionBlock = (__bridge_transfer id)specialProtocols[i].injectionBlock; + specialProtocols[i].injectionBlock = NULL; + + // loop through all classes + for (unsigned classIndex = 0;classIndex < classCount;++classIndex) { + Class class = allClasses[classIndex]; + + // if this class doesn't conform to the protocol, continue to the + // next class immediately + if (!class_conformsToProtocol(class, protocol)) + continue; + + injectionBlock(class); + } + } + } + + // free the allocated class list + free(allClasses); + + // now that everything's injected, the special protocol list can also be + // destroyed + free(specialProtocols); specialProtocols = NULL; + specialProtocolCount = 0; + specialProtocolCapacity = 0; + specialProtocolsReady = 0; +} + +unsigned mtl_injectMethods ( + Class aClass, + Method *methods, + unsigned count, + mtl_methodInjectionBehavior behavior, + mtl_failedMethodCallback failedToAddCallback +) { + unsigned successes = 0; + + /* + * set up an autorelease pool in case any Cocoa classes invoke +initialize + * during this process + */ + @autoreleasepool { + BOOL isMeta = class_isMetaClass(aClass); + + if (!isMeta) { + // clear any +load and +initialize ignore flags + behavior &= ~(mtl_methodInjectionIgnoreLoad | mtl_methodInjectionIgnoreInitialize); + } + + for (unsigned methodIndex = 0;methodIndex < count;++methodIndex) { + Method method = methods[methodIndex]; + SEL methodName = method_getName(method); + + if (behavior & mtl_methodInjectionIgnoreLoad) { + if (methodName == @selector(load)) { + ++successes; + continue; + } + } + + if (behavior & mtl_methodInjectionIgnoreInitialize) { + if (methodName == @selector(initialize)) { + ++successes; + continue; + } + } + + BOOL success = YES; + IMP impl = method_getImplementation(method); + const char *type = method_getTypeEncoding(method); + + switch (behavior & mtl_methodInjectionOverwriteBehaviorMask) { + case mtl_methodInjectionFailOnExisting: + success = class_addMethod(aClass, methodName, impl, type); + break; + + case mtl_methodInjectionFailOnAnyExisting: + if (class_getInstanceMethod(aClass, methodName)) { + success = NO; + break; + } + + // else fall through + + case mtl_methodInjectionReplace: + class_replaceMethod(aClass, methodName, impl, type); + break; + + case mtl_methodInjectionFailOnSuperclassExisting: + { + Class superclass = class_getSuperclass(aClass); + if (superclass && class_getInstanceMethod(superclass, methodName)) + success = NO; + else + class_replaceMethod(aClass, methodName, impl, type); + } + + break; + + default: + fprintf(stderr, "ERROR: Unrecognized method injection behavior: %i\n", (int)(behavior & mtl_methodInjectionOverwriteBehaviorMask)); + } + + if (success) + ++successes; + else + failedToAddCallback(aClass, method); + } + } + + return successes; +} + +BOOL mtl_injectMethodsFromClass ( + Class srcClass, + Class dstClass, + mtl_methodInjectionBehavior behavior, + mtl_failedMethodCallback failedToAddCallback) +{ + unsigned count, addedCount; + BOOL success = YES; + + count = 0; + Method *instanceMethods = class_copyMethodList(srcClass, &count); + + addedCount = mtl_injectMethods( + dstClass, + instanceMethods, + count, + behavior, + failedToAddCallback + ); + + free(instanceMethods); + if (addedCount < count) + success = NO; + + count = 0; + Method *classMethods = class_copyMethodList(object_getClass(srcClass), &count); + + // ignore +load + behavior |= mtl_methodInjectionIgnoreLoad; + addedCount = mtl_injectMethods( + object_getClass(dstClass), + classMethods, + count, + behavior, + failedToAddCallback + ); + + free(classMethods); + if (addedCount < count) + success = NO; + + return success; +} + +Class mtl_classBeforeSuperclass (Class receiver, Class superclass) { + Class previousClass = nil; + + while (![receiver isEqual:superclass]) { + previousClass = receiver; + receiver = class_getSuperclass(receiver); + } + + return previousClass; +} + +Class *mtl_copyClassList (unsigned *count) { + // get the number of classes registered with the runtime + int classCount = objc_getClassList(NULL, 0); + if (!classCount) { + if (count) + *count = 0; + + return NULL; + } + + // allocate space for them plus NULL + Class *allClasses = (Class *)malloc(sizeof(Class) * (classCount + 1)); + if (!allClasses) { + fprintf(stderr, "ERROR: Could allocate memory for all classes\n"); + if (count) + *count = 0; + + return NULL; + } + + // and then actually pull the list of the class objects + classCount = objc_getClassList(allClasses, classCount); + allClasses[classCount] = NULL; + + @autoreleasepool { + // weed out classes that do weird things when reflected upon + for (int i = 0;i < classCount;) { + Class class = allClasses[i]; + BOOL keep = YES; + + if (keep) + keep &= class_respondsToSelector(class, @selector(methodSignatureForSelector:)); + + if (keep) { + if (class_respondsToSelector(class, @selector(isProxy))) + keep &= ![class isProxy]; + } + + if (!keep) { + if (--classCount > i) { + memmove(allClasses + i, allClasses + i + 1, (classCount - i) * sizeof(*allClasses)); + } + + continue; + } + + ++i; + } + } + + if (count) + *count = (unsigned)classCount; + + return allClasses; +} + +unsigned mtl_addMethods (Class aClass, Method *methods, unsigned count, BOOL checkSuperclasses, mtl_failedMethodCallback failedToAddCallback) { + mtl_methodInjectionBehavior behavior = mtl_methodInjectionFailOnExisting; + if (checkSuperclasses) + behavior |= mtl_methodInjectionFailOnSuperclassExisting; + + return mtl_injectMethods( + aClass, + methods, + count, + behavior, + failedToAddCallback + ); +} + +BOOL mtl_addMethodsFromClass (Class srcClass, Class dstClass, BOOL checkSuperclasses, mtl_failedMethodCallback failedToAddCallback) { + mtl_methodInjectionBehavior behavior = mtl_methodInjectionFailOnExisting; + if (checkSuperclasses) + behavior |= mtl_methodInjectionFailOnSuperclassExisting; + + return mtl_injectMethodsFromClass(srcClass, dstClass, behavior, failedToAddCallback); +} + +BOOL mtl_classIsKindOfClass (Class receiver, Class aClass) { + while (receiver) { + if (receiver == aClass) + return YES; + + receiver = class_getSuperclass(receiver); + } + + return NO; +} + +Class *mtl_copyClassListConformingToProtocol (Protocol *protocol, unsigned *count) { + Class *allClasses; + + /* + * set up an autorelease pool in case any Cocoa classes invoke +initialize + * during this process + */ + @autoreleasepool { + unsigned classCount = 0; + allClasses = mtl_copyClassList(&classCount); + + if (!allClasses) + return NULL; + + // we're going to reuse allClasses for the return value, so returnIndex will + // keep track of the indices we replace with new values + unsigned returnIndex = 0; + + for (unsigned classIndex = 0;classIndex < classCount;++classIndex) { + Class cls = allClasses[classIndex]; + if (class_conformsToProtocol(cls, protocol)) + allClasses[returnIndex++] = cls; + } + + allClasses[returnIndex] = NULL; + if (count) + *count = returnIndex; + } + + return allClasses; +} + +mtl_propertyAttributes *mtl_copyPropertyAttributes (objc_property_t property) { + const char * const attrString = property_getAttributes(property); + if (!attrString) { + fprintf(stderr, "ERROR: Could not get attribute string from property %s\n", property_getName(property)); + return NULL; + } + + if (attrString[0] != 'T') { + fprintf(stderr, "ERROR: Expected attribute string \"%s\" for property %s to start with 'T'\n", attrString, property_getName(property)); + return NULL; + } + + const char *typeString = attrString + 1; + const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL); + if (!next) { + fprintf(stderr, "ERROR: Could not read past type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + return NULL; + } + + size_t typeLength = next - typeString; + if (!typeLength) { + fprintf(stderr, "ERROR: Invalid type in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + return NULL; + } + + // allocate enough space for the structure and the type string (plus a NUL) + mtl_propertyAttributes *attributes = calloc(1, sizeof(mtl_propertyAttributes) + typeLength + 1); + if (!attributes) { + fprintf(stderr, "ERROR: Could not allocate mtl_propertyAttributes structure for attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + return NULL; + } + + // copy the type string + strncpy(attributes->type, typeString, typeLength); + attributes->type[typeLength] = '\0'; + + // if this is an object type, and immediately followed by a quoted string... + if (typeString[0] == *(@encode(id)) && typeString[1] == '"') { + // we should be able to extract a class name + const char *className = typeString + 2; + next = strchr(className, '"'); + + if (!next) { + fprintf(stderr, "ERROR: Could not read class name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + goto errorOut; + } + + if (className != next) { + size_t classNameLength = next - className; + char trimmedName[classNameLength + 1]; + + strncpy(trimmedName, className, classNameLength); + trimmedName[classNameLength] = '\0'; + + // attempt to look up the class in the runtime + attributes->objectClass = objc_getClass(trimmedName); + } + } + + if (*next != '\0') { + // skip past any junk before the first flag + next = strchr(next, ','); + } + + while (next && *next == ',') { + char flag = next[1]; + next += 2; + + switch (flag) { + case '\0': + break; + + case 'R': + attributes->readonly = YES; + break; + + case 'C': + attributes->memoryManagementPolicy = mtl_propertyMemoryManagementPolicyCopy; + break; + + case '&': + attributes->memoryManagementPolicy = mtl_propertyMemoryManagementPolicyRetain; + break; + + case 'N': + attributes->nonatomic = YES; + break; + + case 'G': + case 'S': + { + const char *nextFlag = strchr(next, ','); + SEL name = NULL; + + if (!nextFlag) { + // assume that the rest of the string is the selector + const char *selectorString = next; + next = ""; + + name = sel_registerName(selectorString); + } else { + size_t selectorLength = nextFlag - next; + if (!selectorLength) { + fprintf(stderr, "ERROR: Found zero length selector name in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + goto errorOut; + } + + char selectorString[selectorLength + 1]; + + strncpy(selectorString, next, selectorLength); + selectorString[selectorLength] = '\0'; + + name = sel_registerName(selectorString); + next = nextFlag; + } + + if (flag == 'G') + attributes->getter = name; + else + attributes->setter = name; + } + + break; + + case 'D': + attributes->dynamic = YES; + attributes->ivar = NULL; + break; + + case 'V': + // assume that the rest of the string (if present) is the ivar name + if (*next == '\0') { + // if there's nothing there, let's assume this is dynamic + attributes->ivar = NULL; + } else { + attributes->ivar = next; + next = ""; + } + + break; + + case 'W': + attributes->weak = YES; + break; + + case 'P': + attributes->canBeCollected = YES; + break; + + case 't': + fprintf(stderr, "ERROR: Old-style type encoding is unsupported in attribute string \"%s\" for property %s\n", attrString, property_getName(property)); + + // skip over this type encoding + while (*next != ',' && *next != '\0') + ++next; + + break; + + default: + fprintf(stderr, "ERROR: Unrecognized attribute string flag '%c' in attribute string \"%s\" for property %s\n", flag, attrString, property_getName(property)); + } + } + + if (next && *next != '\0') { + fprintf(stderr, "Warning: Unparsed data \"%s\" in attribute string \"%s\" for property %s\n", next, attrString, property_getName(property)); + } + + if (!attributes->getter) { + // use the property name as the getter by default + attributes->getter = sel_registerName(property_getName(property)); + } + + if (!attributes->setter) { + const char *propertyName = property_getName(property); + size_t propertyNameLength = strlen(propertyName); + + // we want to transform the name to setProperty: style + size_t setterLength = propertyNameLength + 4; + + char setterName[setterLength + 1]; + strncpy(setterName, "set", 3); + strncpy(setterName + 3, propertyName, propertyNameLength); + + // capitalize property name for the setter + setterName[3] = (char)toupper(setterName[3]); + + setterName[setterLength - 1] = ':'; + setterName[setterLength] = '\0'; + + attributes->setter = sel_registerName(setterName); + } + + return attributes; + +errorOut: + free(attributes); + return NULL; +} + +Class *mtl_copySubclassList (Class targetClass, unsigned *subclassCount) { + unsigned classCount = 0; + Class *allClasses = mtl_copyClassList(&classCount); + if (!allClasses || !classCount) { + fprintf(stderr, "ERROR: No classes registered with the runtime, cannot find %s!\n", class_getName(targetClass)); + return NULL; + } + + // we're going to reuse allClasses for the return value, so returnIndex will + // keep track of the indices we replace with new values + unsigned returnIndex = 0; + + BOOL isMeta = class_isMetaClass(targetClass); + + for (unsigned classIndex = 0;classIndex < classCount;++classIndex) { + Class cls = allClasses[classIndex]; + Class superclass = class_getSuperclass(cls); + + while (superclass != NULL) { + if (isMeta) { + if (object_getClass(superclass) == targetClass) + break; + } else if (superclass == targetClass) + break; + + superclass = class_getSuperclass(superclass); + } + + if (!superclass) + continue; + + // at this point, 'cls' is definitively a subclass of targetClass + if (isMeta) + cls = object_getClass(cls); + + allClasses[returnIndex++] = cls; + } + + allClasses[returnIndex] = NULL; + if (subclassCount) + *subclassCount = returnIndex; + + return allClasses; +} + +Method mtl_getImmediateInstanceMethod (Class aClass, SEL aSelector) { + unsigned methodCount = 0; + Method *methods = class_copyMethodList(aClass, &methodCount); + Method foundMethod = NULL; + + for (unsigned methodIndex = 0;methodIndex < methodCount;++methodIndex) { + if (method_getName(methods[methodIndex]) == aSelector) { + foundMethod = methods[methodIndex]; + break; + } + } + + free(methods); + return foundMethod; +} + +BOOL mtl_getPropertyAccessorsForClass (objc_property_t property, Class aClass, Method *getter, Method *setter) { + mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property); + if (!attributes) + return NO; + + SEL getterName = attributes->getter; + SEL setterName = attributes->setter; + + free(attributes); + attributes = NULL; + + /* + * set up an autorelease pool in case this sends aClass its first message + */ + @autoreleasepool { + Method foundGetter = class_getInstanceMethod(aClass, getterName); + if (!foundGetter) { + return NO; + } + + if (getter) + *getter = foundGetter; + + if (setter) { + Method foundSetter = class_getInstanceMethod(aClass, setterName); + if (foundSetter) + *setter = foundSetter; + } + } + + return YES; +} + +BOOL mtl_loadSpecialProtocol (Protocol *protocol, void (^injectionBehavior)(Class destinationClass)) { + @autoreleasepool { + NSCParameterAssert(protocol != nil); + NSCParameterAssert(injectionBehavior != nil); + + // lock the mutex to prevent accesses from other threads while we perform + // this work + if (pthread_mutex_lock(&specialProtocolsLock) != 0) { + fprintf(stderr, "ERROR: Could not synchronize on special protocol data\n"); + return NO; + } + + // if we've hit the hard maximum for number of special protocols, we can't + // continue + if (specialProtocolCount == SIZE_MAX) { + pthread_mutex_unlock(&specialProtocolsLock); + return NO; + } + + // if the array has no more space, we will need to allocate additional + // entries + if (specialProtocolCount >= specialProtocolCapacity) { + size_t newCapacity; + if (specialProtocolCapacity == 0) + // if there are no entries, make space for just one + newCapacity = 1; + else { + // otherwise, double the current capacity + newCapacity = specialProtocolCapacity << 1; + + // if the new capacity is less than the current capacity, that's + // unsigned integer overflow + if (newCapacity < specialProtocolCapacity) { + // set it to the maximum possible instead + newCapacity = SIZE_MAX; + + // if the new capacity is still not greater than the current + // (for instance, if it was already SIZE_MAX), we can't continue + if (newCapacity <= specialProtocolCapacity) { + pthread_mutex_unlock(&specialProtocolsLock); + return NO; + } + } + } + + // we have a new capacity, so resize the list of all special protocols + // to add the new entries + void * restrict ptr = realloc(specialProtocols, sizeof(*specialProtocols) * newCapacity); + if (!ptr) { + // the allocation failed, abort + pthread_mutex_unlock(&specialProtocolsLock); + return NO; + } + + specialProtocols = ptr; + specialProtocolCapacity = newCapacity; + } + + // at this point, there absolutely must be at least one empty entry in the + // array + assert(specialProtocolCount < specialProtocolCapacity); + + // disable warning about "leaking" this block, which is released in + // mtl_injectSpecialProtocols() + #ifndef __clang_analyzer__ + mtl_specialProtocolInjectionBlock copiedBlock = [injectionBehavior copy]; + + // construct a new MTLSpecialProtocol structure and add it to the first + // empty space in the array + specialProtocols[specialProtocolCount] = (MTLSpecialProtocol){ + .protocol = protocol, + .injectionBlock = (__bridge_retained void *)copiedBlock, + .ready = NO + }; + #endif + + ++specialProtocolCount; + pthread_mutex_unlock(&specialProtocolsLock); + } + + // success! + return YES; +} + +void mtl_specialProtocolReadyForInjection (Protocol *protocol) { + @autoreleasepool { + NSCParameterAssert(protocol != nil); + + // lock the mutex to prevent accesses from other threads while we perform + // this work + if (pthread_mutex_lock(&specialProtocolsLock) != 0) { + fprintf(stderr, "ERROR: Could not synchronize on special protocol data\n"); + return; + } + + // loop through all the special protocols in our list, trying to find the + // one associated with 'protocol' + for (size_t i = 0;i < specialProtocolCount;++i) { + if (specialProtocols[i].protocol == protocol) { + // found the matching special protocol, check to see if it's + // already ready + if (!specialProtocols[i].ready) { + // if it's not, mark it as being ready now + specialProtocols[i].ready = YES; + + // since this special protocol was in our array, and it was not + // loaded, the total number of protocols loaded must be less + // than the total count at this point in time + assert(specialProtocolsReady < specialProtocolCount); + + // ... and then increment the total number of special protocols + // loaded – if it now matches the total count of special + // protocols, begin the injection process + if (++specialProtocolsReady == specialProtocolCount) + mtl_injectSpecialProtocols(); + } + + break; + } + } + + pthread_mutex_unlock(&specialProtocolsLock); + } +} + +void mtl_removeMethod (Class aClass, SEL methodName) { + Method existingMethod = mtl_getImmediateInstanceMethod(aClass, methodName); + if (!existingMethod) { + return; + } + + /* + * set up an autorelease pool in case any Cocoa classes invoke +initialize + * during this process + */ + @autoreleasepool { + Method superclassMethod = NULL; + Class superclass = class_getSuperclass(aClass); + if (superclass) + superclassMethod = class_getInstanceMethod(superclass, methodName); + + if (superclassMethod) { + method_setImplementation(existingMethod, method_getImplementation(superclassMethod)); + } else { + // since we now know that the method doesn't exist on any + // superclass, get an IMP internal to the runtime for message forwarding + IMP forward = class_getMethodImplementation(superclass, methodName); + + method_setImplementation(existingMethod, forward); + } + } +} + +void mtl_replaceMethods (Class aClass, Method *methods, unsigned count) { + mtl_injectMethods( + aClass, + methods, + count, + mtl_methodInjectionReplace, + NULL + ); +} + +void mtl_replaceMethodsFromClass (Class srcClass, Class dstClass) { + mtl_injectMethodsFromClass(srcClass, dstClass, mtl_methodInjectionReplace, NULL); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat" +NSString *mtl_stringFromTypedBytes (const void *bytes, const char *encoding) { + switch (*encoding) { + case 'c': return @(*(char *)bytes).description; + case 'C': return @(*(unsigned char *)bytes).description; + case 'i': return @(*(int *)bytes).description; + case 'I': return @(*(unsigned int *)bytes).description; + case 's': return @(*(short *)bytes).description; + case 'S': return @(*(unsigned short *)bytes).description; + case 'l': return @(*(long *)bytes).description; + case 'L': return @(*(unsigned long *)bytes).description; + case 'q': return @(*(long long *)bytes).description; + case 'Q': return @(*(unsigned long long *)bytes).description; + case 'f': return @(*(float *)bytes).description; + case 'd': return @(*(double *)bytes).description; + case 'B': return @(*(_Bool *)bytes).description; + case 'v': return @"(void)"; + case '*': return [NSString stringWithFormat:@"\"%s\"", bytes]; + + case '@': + case '#': { + id obj = *(__unsafe_unretained id *)bytes; + if (obj) + return [obj description]; + else + return @"(nil)"; + } + + case '?': + case '^': { + const void *ptr = *(const void **)bytes; + if (ptr) + return [NSString stringWithFormat:@"%p", ptr]; + else + return @"(null)"; + } + + default: + return [[NSValue valueWithBytes:bytes objCType:encoding] description]; + } +} +#pragma clang diagnostic pop diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/MTLEXTScope.m b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/MTLEXTScope.m new file mode 100644 index 0000000..60cf275 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/MTLEXTScope.m @@ -0,0 +1,15 @@ +// +// MTLEXTScope.m +// extobjc +// +// Created by Justin Spahr-Summers on 2011-05-04. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import "MTLEXTScope.h" + +void mtl_executeCleanupBlock (__strong mtl_cleanupBlock_t *block) { + (*block)(); +} + diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLEXTKeyPathCoding.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLEXTKeyPathCoding.h new file mode 100644 index 0000000..eea5457 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLEXTKeyPathCoding.h @@ -0,0 +1,77 @@ +// +// MTLEXTKeyPathCoding.h +// extobjc +// +// Created by Justin Spahr-Summers on 19.06.12. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import +#import "MTLMetamacros.h" + +/** + * \@keypath allows compile-time verification of key paths. Given a real object + * receiver and key path: + * + * @code + +NSString *UTF8StringPath = @keypath(str.lowercaseString.UTF8String); +// => @"lowercaseString.UTF8String" + +NSString *versionPath = @keypath(NSObject, version); +// => @"version" + +NSString *lowercaseStringPath = @keypath(NSString.new, lowercaseString); +// => @"lowercaseString" + + * @endcode + * + * ... the macro returns an \c NSString containing all but the first path + * component or argument (e.g., @"lowercaseString.UTF8String", @"version"). + * + * In addition to simply creating a key path, this macro ensures that the key + * path is valid at compile-time (causing a syntax error if not), and supports + * refactoring, such that changing the name of the property will also update any + * uses of \@keypath. + */ +#define keypath(...) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Warc-repeated-use-of-weak\"") \ + (NO).boolValue ? ((NSString * _Nonnull)nil) : ((NSString * _Nonnull)@(cStringKeypath(__VA_ARGS__))) \ + _Pragma("clang diagnostic pop") \ + +#define cStringKeypath(...) \ + metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__)) + +#define keypath1(PATH) \ + (((void)(NO && ((void)PATH, NO)), \ + ({ char *__extobjckeypath__ = strchr(# PATH, '.'); NSCAssert(__extobjckeypath__, @"Provided key path is invalid."); __extobjckeypath__ + 1; }))) + +#define keypath2(OBJ, PATH) \ + (((void)(NO && ((void)OBJ.PATH, NO)), # PATH)) + +/** + * \@collectionKeypath allows compile-time verification of key paths across collections NSArray/NSSet etc. Given a real object + * receiver, collection object receiver and related keypaths: + * + * @code + + NSString *employeesFirstNamePath = @collectionKeypath(department.employees, Employee.new, firstName) + // => @"employees.firstName" + + NSString *employeesFirstNamePath = @collectionKeypath(Department.new, employees, Employee.new, firstName) + // => @"employees.firstName" + + * @endcode + * + */ +#define collectionKeypath(...) \ + metamacro_if_eq(3, metamacro_argcount(__VA_ARGS__))(collectionKeypath3(__VA_ARGS__))(collectionKeypath4(__VA_ARGS__)) + +#define collectionKeypath3(PATH, COLLECTION_OBJECT, COLLECTION_PATH) \ + (YES).boolValue ? (NSString * _Nonnull)@((const char * _Nonnull)[[NSString stringWithFormat:@"%s.%s", cStringKeypath(PATH), cStringKeypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) : (NSString * _Nonnull)nil + +#define collectionKeypath4(OBJ, PATH, COLLECTION_OBJECT, COLLECTION_PATH) \ + (YES).boolValue ? (NSString * _Nonnull)@((const char * _Nonnull)[[NSString stringWithFormat:@"%s.%s", cStringKeypath(OBJ, PATH), cStringKeypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) : (NSString * _Nonnull)nil + diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLEXTRuntimeExtensions.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLEXTRuntimeExtensions.h new file mode 100644 index 0000000..01f5c3c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLEXTRuntimeExtensions.h @@ -0,0 +1,389 @@ +// +// MTLEXTRuntimeExtensions.h +// extobjc +// +// Created by Justin Spahr-Summers on 2011-03-05. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import +#import + +/** + * A callback indicating that the given method failed to be added to the given + * class. The reason for the failure depends on the attempted task. + */ +typedef void (*mtl_failedMethodCallback)(Class, Method); + +/** + * Used with #mtl_injectMethods to determine injection behavior. + */ +typedef NS_OPTIONS(NSUInteger, mtl_methodInjectionBehavior) { + /** + * Indicates that any existing methods on the destination class should be + * overwritten. + */ + mtl_methodInjectionReplace = 0x00, + + /** + * Avoid overwriting methods on the immediate destination class. + */ + mtl_methodInjectionFailOnExisting = 0x01, + + /** + * Avoid overriding methods implemented in any superclass of the destination + * class. + */ + mtl_methodInjectionFailOnSuperclassExisting = 0x02, + + /** + * Avoid overwriting methods implemented in the immediate destination class + * or any superclass. This is equivalent to + * mtl_methodInjectionFailOnExisting | mtl_methodInjectionFailOnSuperclassExisting. + */ + mtl_methodInjectionFailOnAnyExisting = 0x03, + + /** + * Ignore the \c +load class method. This does not affect instance method + * injection. + */ + mtl_methodInjectionIgnoreLoad = 1U << 2, + + /** + * Ignore the \c +initialize class method. This does not affect instance method + * injection. + */ + mtl_methodInjectionIgnoreInitialize = 1U << 3 +}; + +/** + * A mask for the overwriting behavior flags of #mtl_methodInjectionBehavior. + */ +static const mtl_methodInjectionBehavior mtl_methodInjectionOverwriteBehaviorMask = 0x3; + +/** + * Describes the memory management policy of a property. + */ +typedef enum { + /** + * The value is assigned. + */ + mtl_propertyMemoryManagementPolicyAssign = 0, + + /** + * The value is retained. + */ + mtl_propertyMemoryManagementPolicyRetain, + + /** + * The value is copied. + */ + mtl_propertyMemoryManagementPolicyCopy +} mtl_propertyMemoryManagementPolicy; + +/** + * Describes the attributes and type information of a property. + */ +typedef struct { + /** + * Whether this property was declared with the \c readonly attribute. + */ + BOOL readonly; + + /** + * Whether this property was declared with the \c nonatomic attribute. + */ + BOOL nonatomic; + + /** + * Whether the property is a weak reference. + */ + BOOL weak; + + /** + * Whether the property is eligible for garbage collection. + */ + BOOL canBeCollected; + + /** + * Whether this property is defined with \c \@dynamic. + */ + BOOL dynamic; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" + + /** + * The memory management policy for this property. This will always be + * #mtl_propertyMemoryManagementPolicyAssign if #readonly is \c YES. + */ + mtl_propertyMemoryManagementPolicy memoryManagementPolicy; + + + /** + * The selector for the getter of this property. This will reflect any + * custom \c getter= attribute provided in the property declaration, or the + * inferred getter name otherwise. + */ + SEL getter; + + /** + * The selector for the setter of this property. This will reflect any + * custom \c setter= attribute provided in the property declaration, or the + * inferred setter name otherwise. + * + * @note If #readonly is \c YES, this value will represent what the setter + * \e would be, if the property were writable. + */ + SEL setter; + +#pragma clang diagnostic pop + + /** + * The backing instance variable for this property, or \c NULL if \c + * \c @synthesize was not used, and therefore no instance variable exists. This + * would also be the case if the property is implemented dynamically. + */ + const char *ivar; + + /** + * If this property is defined as being an instance of a specific class, + * this will be the class object representing it. + * + * This will be \c nil if the property was defined as type \c id, if the + * property is not of an object type, or if the class could not be found at + * runtime. + */ + Class objectClass; + + /** + * The type encoding for the value of this property. This is the type as it + * would be returned by the \c \@encode() directive. + */ + char type[]; +} mtl_propertyAttributes; + +/** + * Iterates through the first \a count entries in \a methods and attempts to add + * each one to \a aClass. If a method by the same name already exists on \a + * aClass, it is \e not overridden. If \a checkSuperclasses is \c YES, and + * a method by the same name already exists on any superclass of \a aClass, it + * is not overridden. + * + * Returns the number of methods added successfully. For each method that fails + * to be added, \a failedToAddCallback (if provided) is invoked. + */ +unsigned mtl_addMethods (Class aClass, Method *methods, unsigned count, BOOL checkSuperclasses, mtl_failedMethodCallback failedToAddCallback); + +/** + * Iterates through all instance and class methods of \a srcClass and attempts + * to add each one to \a dstClass. If a method by the same name already exists + * on \a aClass, it is \e not overridden. If \a checkSuperclasses is \c YES, and + * a method by the same name already exists on any superclass of \a aClass, it + * is not overridden. + * + * Returns whether all methods were added successfully. For each method that fails + * to be added, \a failedToAddCallback (if provided) is invoked. + * + * @note This ignores any \c +load method on \a srcClass. \a srcClass and \a + * dstClass must not be metaclasses. + */ +BOOL mtl_addMethodsFromClass (Class srcClass, Class dstClass, BOOL checkSuperclasses, mtl_failedMethodCallback failedToAddCallback); + +/** + * Returns the superclass of \a receiver which immediately descends from \a + * superclass. If \a superclass is not in the hierarchy of \a receiver, or is + * equal to \a receiver, \c nil is returned. + */ +Class mtl_classBeforeSuperclass (Class receiver, Class superclass); + +/** + * Returns whether \a receiver is \a aClass, or inherits directly from it. + */ +BOOL mtl_classIsKindOfClass (Class receiver, Class aClass); + +/** + * Returns the full list of classes registered with the runtime, terminated with + * \c NULL. If \a count is not \c NULL, it is filled in with the total number of + * classes returned. You must \c free() the returned array. + */ +Class *mtl_copyClassList (unsigned *count); + +/** + * Looks through the complete list of classes registered with the runtime and + * finds all classes which conform to \a protocol. Returns \c *count classes + * terminated by a \c NULL. You must \c free() the returned array. If there are no + * classes conforming to \a protocol, \c NULL is returned. + * + * @note \a count may be \c NULL. + */ +Class *mtl_copyClassListConformingToProtocol (Protocol *protocol, unsigned *count); + +/** + * Returns a pointer to a structure containing information about \a property. + * You must \c free() the returned pointer. Returns \c NULL if there is an error + * obtaining information from \a property. + */ +mtl_propertyAttributes *mtl_copyPropertyAttributes (objc_property_t property); + +/** + * Looks through the complete list of classes registered with the runtime and + * finds all classes which are descendant from \a aClass. Returns \c + * *subclassCount classes terminated by a \c NULL. You must \c free() the + * returned array. If there are no subclasses of \a aClass, \c NULL is + * returned. + * + * @note \a subclassCount may be \c NULL. \a aClass may be a metaclass to get + * all subclass metaclass objects. + */ +Class *mtl_copySubclassList (Class aClass, unsigned *subclassCount); + +/** + * Finds the instance method named \a aSelector on \a aClass and returns it, or + * returns \c NULL if no such instance method exists. Unlike \c + * class_getInstanceMethod(), this does not search superclasses. + * + * @note To get class methods in this manner, use a metaclass for \a aClass. + */ +Method mtl_getImmediateInstanceMethod (Class aClass, SEL aSelector); + +/** + * Returns the value of \c Ivar \a IVAR from instance \a OBJ. The instance + * variable must be of type \a TYPE, and is returned as such. + * + * @warning Depending on the platform, this may or may not work with aggregate + * or floating-point types. + */ +#define mtl_getIvar(OBJ, IVAR, TYPE) \ + ((TYPE (*)(id, Ivar)object_getIvar)((OBJ), (IVAR))) + +/** + * Returns the value of the instance variable identified by the string \a NAME + * from instance \a OBJ. The instance variable must be of type \a TYPE, and is + * returned as such. + * + * @note \a OBJ is evaluated twice. + * + * @warning Depending on the platform, this may or may not work with aggregate + * or floating-point types. + */ +#define mtl_getIvarByName(OBJ, NAME, TYPE) \ + mtl_getIvar((OBJ), class_getInstanceVariable(object_getClass((OBJ)), (NAME)), TYPE) + +/** + * Returns the accessor methods for \a property, as implemented in \a aClass or + * any of its superclasses. The getter, if implemented, is returned in \a + * getter, and the setter, if implemented, is returned in \a setter. If either + * \a getter or \a setter are \c NULL, that accessor is not returned. If either + * accessor is not implemented, the argument is left unmodified. + * + * Returns \c YES if a valid accessor was found, or \c NO if \a aClass and its + * superclasses do not implement \a property or if an error occurs. + */ +BOOL mtl_getPropertyAccessorsForClass (objc_property_t property, Class aClass, Method *getter, Method *setter); + +/** + * For all classes registered with the runtime, invokes \c + * methodSignatureForSelector: and \c instanceMethodSignatureForSelector: to + * determine a method signature for \a aSelector. If one or more valid + * signatures is found, the first one is returned. If no valid signatures were + * found, \c nil is returned. + */ +NSMethodSignature *mtl_globalMethodSignatureForSelector (SEL aSelector); + +/** + * Highly-configurable method injection. Adds the first \a count entries from \a + * methods into \a aClass according to \a behavior. + * + * Returns the number of methods added successfully. For each method that fails + * to be added, \a failedToAddCallback (if provided) is invoked. + * + * @note \c +load and \c +initialize methods are included in the number of + * successful methods when ignored for injection. + */ +unsigned mtl_injectMethods (Class aClass, Method *methods, unsigned count, mtl_methodInjectionBehavior behavior, mtl_failedMethodCallback failedToAddCallback); + +/** + * Invokes #mtl_injectMethods with the instance methods and class methods from + * \a srcClass. #mtl_methodInjectionIgnoreLoad is added to #behavior for class + * method injection. + * + * Returns whether all methods were added successfully. For each method that fails + * to be added, \a failedToAddCallback (if provided) is invoked. + * + * @note \c +load and \c +initialize methods are considered to be added + * successfully when ignored for injection. + */ +BOOL mtl_injectMethodsFromClass (Class srcClass, Class dstClass, mtl_methodInjectionBehavior behavior, mtl_failedMethodCallback failedToAddCallback); + +/** + * Loads a "special protocol" into an internal list. A special protocol is any + * protocol for which implementing classes need injection behavior (i.e., any + * class conforming to the protocol needs to be reflected upon). Returns \c NO + * if loading failed. + * + * Using this facility proceeds as follows: + * + * @li Each protocol is loaded with #mtl_loadSpecialProtocol and a custom block + * that describes its injection behavior on each conforming class. + * @li Each protocol is marked as being ready for injection with + * #mtl_specialProtocolReadyForInjection. + * @li The entire Objective-C class list is retrieved, and each special + * protocol's \a injectionBehavior block is run for all conforming classes. + * + * It is an error to call this function without later calling + * #mtl_specialProtocolReadyForInjection as well. + * + * @note A special protocol X which conforms to another special protocol Y is + * always injected \e after Y. + */ +BOOL mtl_loadSpecialProtocol (Protocol *protocol, void (^injectionBehavior)(Class destinationClass)); + +/** + * Marks a special protocol as being ready for injection. Injection is actually + * performed only after all special protocols have been marked in this way. + * + * @sa mtl_loadSpecialProtocol + */ +void mtl_specialProtocolReadyForInjection (Protocol *protocol); + +/** + * Creates a human-readable description of the data in \a bytes, interpreting it + * according to the given Objective-C type encoding. + * + * This is intended for use with debugging, and code should not depend upon the + * format of the returned string (just like a call to \c -description). + */ +NSString *mtl_stringFromTypedBytes (const void *bytes, const char *encoding); + +/** + * "Removes" any instance method matching \a methodName from \a aClass. This + * removal can mean one of two things: + * + * @li If any superclass of \a aClass implements a method by the same name, the + * implementation of the closest such superclass is used. + * @li If no superclasses of \a aClass implement a method by the same name, the + * method is replaced with an implementation internal to the runtime, used for + * message forwarding. + * + * @warning Adding a method by the same name into a superclass of \a aClass \e + * after using this function may obscure it from the subclass. + */ +void mtl_removeMethod (Class aClass, SEL methodName); + +/** + * Iterates through the first \a count entries in \a methods and adds each one + * to \a aClass, replacing any existing implementation. + */ +void mtl_replaceMethods (Class aClass, Method *methods, unsigned count); + +/** + * Iterates through all instance and class methods of \a srcClass and adds each + * one to \a dstClass, replacing any existing implementation. + * + * @note This ignores any \c +load method on \a srcClass. \a srcClass and \a + * dstClass must not be metaclasses. + */ +void mtl_replaceMethodsFromClass (Class srcClass, Class dstClass); + diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLEXTScope.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLEXTScope.h new file mode 100644 index 0000000..815297c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLEXTScope.h @@ -0,0 +1,122 @@ +// +// MTLEXTScope.h +// extobjc +// +// Created by Justin Spahr-Summers on 2011-05-04. +// Copyright (C) 2012 Justin Spahr-Summers. +// Released under the MIT license. +// + +#import "MTLMetamacros.h" + +/** + * \@onExit defines some code to be executed when the current scope exits. The + * code must be enclosed in braces and terminated with a semicolon, and will be + * executed regardless of how the scope is exited, including from exceptions, + * \c goto, \c return, \c break, and \c continue. + * + * Provided code will go into a block to be executed later. Keep this in mind as + * it pertains to memory management, restrictions on assignment, etc. Because + * the code is used within a block, \c return is a legal (though perhaps + * confusing) way to exit the cleanup block early. + * + * Multiple \@onExit statements in the same scope are executed in reverse + * lexical order. This helps when pairing resource acquisition with \@onExit + * statements, as it guarantees teardown in the opposite order of acquisition. + * + * @note This statement cannot be used within scopes defined without braces + * (like a one line \c if). In practice, this is not an issue, since \@onExit is + * a useless construct in such a case anyways. + */ +#define onExit \ + mtl_keywordify \ + __strong mtl_cleanupBlock_t metamacro_concat(mtl_exitBlock_, __LINE__) __attribute__((cleanup(mtl_executeCleanupBlock), unused)) = ^ + +/** + * Creates \c __weak shadow variables for each of the variables provided as + * arguments, which can later be made strong again with #strongify. + * + * This is typically used to weakly reference variables in a block, but then + * ensure that the variables stay alive during the actual execution of the block + * (if they were live upon entry). + * + * See #strongify for an example of usage. + */ +#define weakify(...) \ + mtl_keywordify \ + metamacro_foreach_cxt(mtl_weakify_,, __weak, __VA_ARGS__) + +/** + * Like #weakify, but uses \c __unsafe_unretained instead, for targets or + * classes that do not support weak references. + */ +#define unsafeify(...) \ + mtl_keywordify \ + metamacro_foreach_cxt(mtl_weakify_,, __unsafe_unretained, __VA_ARGS__) + +/** + * Strongly references each of the variables provided as arguments, which must + * have previously been passed to #weakify. + * + * The strong references created will shadow the original variable names, such + * that the original names can be used without issue (and a significantly + * reduced risk of retain cycles) in the current scope. + * + * @code + + id foo = [[NSObject alloc] init]; + id bar = [[NSObject alloc] init]; + + @weakify(foo, bar); + + // this block will not keep 'foo' or 'bar' alive + BOOL (^matchesFooOrBar)(id) = ^ BOOL (id obj){ + // but now, upon entry, 'foo' and 'bar' will stay alive until the block has + // finished executing + @strongify(foo, bar); + + return [foo isEqual:obj] || [bar isEqual:obj]; + }; + + * @endcode + */ +#define strongify(...) \ + mtl_keywordify \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wshadow\"") \ + metamacro_foreach(mtl_strongify_,, __VA_ARGS__) \ + _Pragma("clang diagnostic pop") + +/*** implementation details follow ***/ +typedef void (^mtl_cleanupBlock_t)(void); + +#if defined(__cplusplus) +extern "C" { +#endif + void mtl_executeCleanupBlock (__strong mtl_cleanupBlock_t *block); +#if defined(__cplusplus) +} +#endif + +#define mtl_weakify_(INDEX, CONTEXT, VAR) \ + CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR); + +#define mtl_strongify_(INDEX, VAR) \ + __strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_); + +// Details about the choice of backing keyword: +// +// The use of @try/@catch/@finally can cause the compiler to suppress +// return-type warnings. +// The use of @autoreleasepool {} is not optimized away by the compiler, +// resulting in superfluous creation of autorelease pools. +// +// Since neither option is perfect, and with no other alternatives, the +// compromise is to use @autorelease in DEBUG builds to maintain compiler +// analysis, and to use @try/@catch otherwise to avoid insertion of unnecessary +// autorelease pools. +#if defined(DEBUG) && !defined(NDEBUG) +#define mtl_keywordify autoreleasepool {} +#else +#define mtl_keywordify try {} @catch (...) {} +#endif diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLMetamacros.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLMetamacros.h new file mode 100644 index 0000000..dd90d99 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/extobjc/include/MTLMetamacros.h @@ -0,0 +1,667 @@ +/** + * Macros for metaprogramming + * ExtendedC + * + * Copyright (C) 2012 Justin Spahr-Summers + * Released under the MIT license + */ + +#ifndef EXTC_METAMACROS_H +#define EXTC_METAMACROS_H + + +/** + * Executes one or more expressions (which may have a void type, such as a call + * to a function that returns no value) and always returns true. + */ +#define metamacro_exprify(...) \ + ((__VA_ARGS__), true) + +/** + * Returns a string representation of VALUE after full macro expansion. + */ +#define metamacro_stringify(VALUE) \ + metamacro_stringify_(VALUE) + +/** + * Returns A and B concatenated after full macro expansion. + */ +#define metamacro_concat(A, B) \ + metamacro_concat_(A, B) + +/** + * Returns the Nth variadic argument (starting from zero). At least + * N + 1 variadic arguments must be given. N must be between zero and twenty, + * inclusive. + */ +#define metamacro_at(N, ...) \ + metamacro_concat(metamacro_at, N)(__VA_ARGS__) + +/** + * Returns the number of arguments (up to twenty) provided to the macro. At + * least one argument must be provided. + * + * Inspired by P99: http://p99.gforge.inria.fr + */ +#define metamacro_argcount(...) \ + metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) + +/** + * Identical to #metamacro_foreach_cxt, except that no CONTEXT argument is + * given. Only the index and current argument will thus be passed to MACRO. + */ +#define metamacro_foreach(MACRO, SEP, ...) \ + metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__) + +/** + * For each consecutive variadic argument (up to twenty), MACRO is passed the + * zero-based index of the current argument, CONTEXT, and then the argument + * itself. The results of adjoining invocations of MACRO are then separated by + * SEP. + * + * Inspired by P99: http://p99.gforge.inria.fr + */ +#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \ + metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) + +/** + * Identical to #metamacro_foreach_cxt. This can be used when the former would + * fail due to recursive macro expansion. + */ +#define metamacro_foreach_cxt_recursive(MACRO, SEP, CONTEXT, ...) \ + metamacro_concat(metamacro_foreach_cxt_recursive, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) + +/** + * In consecutive order, appends each variadic argument (up to twenty) onto + * BASE. The resulting concatenations are then separated by SEP. + * + * This is primarily useful to manipulate a list of macro invocations into instead + * invoking a different, possibly related macro. + */ +#define metamacro_foreach_concat(BASE, SEP, ...) \ + metamacro_foreach_cxt(metamacro_foreach_concat_iter, SEP, BASE, __VA_ARGS__) + +/** + * Iterates COUNT times, each time invoking MACRO with the current index + * (starting at zero) and CONTEXT. The results of adjoining invocations of MACRO + * are then separated by SEP. + * + * COUNT must be an integer between zero and twenty, inclusive. + */ +#define metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT) \ + metamacro_concat(metamacro_for_cxt, COUNT)(MACRO, SEP, CONTEXT) + +/** + * Returns the first argument given. At least one argument must be provided. + * + * This is useful when implementing a variadic macro, where you may have only + * one variadic argument, but no way to retrieve it (for example, because \c ... + * always needs to match at least one argument). + * + * @code + +#define varmacro(...) \ + metamacro_head(__VA_ARGS__) + + * @endcode + */ +#define metamacro_head(...) \ + metamacro_head_(__VA_ARGS__, 0) + +/** + * Returns every argument except the first. At least two arguments must be + * provided. + */ +#define metamacro_tail(...) \ + metamacro_tail_(__VA_ARGS__) + +/** + * Returns the first N (up to twenty) variadic arguments as a new argument list. + * At least N variadic arguments must be provided. + */ +#define metamacro_take(N, ...) \ + metamacro_concat(metamacro_take, N)(__VA_ARGS__) + +/** + * Removes the first N (up to twenty) variadic arguments from the given argument + * list. At least N variadic arguments must be provided. + */ +#define metamacro_drop(N, ...) \ + metamacro_concat(metamacro_drop, N)(__VA_ARGS__) + +/** + * Decrements VAL, which must be a number between zero and twenty, inclusive. + * + * This is primarily useful when dealing with indexes and counts in + * metaprogramming. + */ +#define metamacro_dec(VAL) \ + metamacro_at(VAL, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) + +/** + * Increments VAL, which must be a number between zero and twenty, inclusive. + * + * This is primarily useful when dealing with indexes and counts in + * metaprogramming. + */ +#define metamacro_inc(VAL) \ + metamacro_at(VAL, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) + +/** + * If A is equal to B, the next argument list is expanded; otherwise, the + * argument list after that is expanded. A and B must be numbers between zero + * and twenty, inclusive. Additionally, B must be greater than or equal to A. + * + * @code + +// expands to true +metamacro_if_eq(0, 0)(true)(false) + +// expands to false +metamacro_if_eq(0, 1)(true)(false) + + * @endcode + * + * This is primarily useful when dealing with indexes and counts in + * metaprogramming. + */ +#define metamacro_if_eq(A, B) \ + metamacro_concat(metamacro_if_eq, A)(B) + +/** + * Identical to #metamacro_if_eq. This can be used when the former would fail + * due to recursive macro expansion. + */ +#define metamacro_if_eq_recursive(A, B) \ + metamacro_concat(metamacro_if_eq_recursive, A)(B) + +/** + * Returns 1 if N is an even number, or 0 otherwise. N must be between zero and + * twenty, inclusive. + * + * For the purposes of this test, zero is considered even. + */ +#define metamacro_is_even(N) \ + metamacro_at(N, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1) + +/** + * Returns the logical NOT of B, which must be the number zero or one. + */ +#define metamacro_not(B) \ + metamacro_at(B, 1, 0) + +// IMPLEMENTATION DETAILS FOLLOW! +// Do not write code that depends on anything below this line. +#define metamacro_stringify_(VALUE) # VALUE +#define metamacro_concat_(A, B) A ## B +#define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG) +#define metamacro_head_(FIRST, ...) FIRST +#define metamacro_tail_(FIRST, ...) __VA_ARGS__ +#define metamacro_consume_(...) +#define metamacro_expand_(...) __VA_ARGS__ + +// implemented from scratch so that metamacro_concat() doesn't end up nesting +#define metamacro_foreach_concat_iter(INDEX, BASE, ARG) metamacro_foreach_concat_iter_(BASE, ARG) +#define metamacro_foreach_concat_iter_(BASE, ARG) BASE ## ARG + +// metamacro_at expansions +#define metamacro_at0(...) metamacro_head(__VA_ARGS__) +#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__) + +// metamacro_foreach_cxt expansions +#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT) +#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) + +#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ + metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \ + SEP \ + MACRO(1, CONTEXT, _1) + +#define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ + metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ + SEP \ + MACRO(2, CONTEXT, _2) + +#define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ + metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ + SEP \ + MACRO(3, CONTEXT, _3) + +#define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ + metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ + SEP \ + MACRO(4, CONTEXT, _4) + +#define metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ + metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ + SEP \ + MACRO(5, CONTEXT, _5) + +#define metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ + metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ + SEP \ + MACRO(6, CONTEXT, _6) + +#define metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ + metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ + SEP \ + MACRO(7, CONTEXT, _7) + +#define metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ + metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ + SEP \ + MACRO(8, CONTEXT, _8) + +#define metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ + SEP \ + MACRO(9, CONTEXT, _9) + +#define metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + SEP \ + MACRO(10, CONTEXT, _10) + +#define metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ + metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + SEP \ + MACRO(11, CONTEXT, _11) + +#define metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ + metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ + SEP \ + MACRO(12, CONTEXT, _12) + +#define metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ + metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ + SEP \ + MACRO(13, CONTEXT, _13) + +#define metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ + metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ + SEP \ + MACRO(14, CONTEXT, _14) + +#define metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ + metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ + SEP \ + MACRO(15, CONTEXT, _15) + +#define metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ + metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ + SEP \ + MACRO(16, CONTEXT, _16) + +#define metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ + metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ + SEP \ + MACRO(17, CONTEXT, _17) + +#define metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ + metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ + SEP \ + MACRO(18, CONTEXT, _18) + +#define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ + metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ + SEP \ + MACRO(19, CONTEXT, _19) + +// metamacro_foreach_cxt_recursive expansions +#define metamacro_foreach_cxt_recursive0(MACRO, SEP, CONTEXT) +#define metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) + +#define metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ + metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) \ + SEP \ + MACRO(1, CONTEXT, _1) + +#define metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ + metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ + SEP \ + MACRO(2, CONTEXT, _2) + +#define metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ + metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ + SEP \ + MACRO(3, CONTEXT, _3) + +#define metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ + metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ + SEP \ + MACRO(4, CONTEXT, _4) + +#define metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ + metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ + SEP \ + MACRO(5, CONTEXT, _5) + +#define metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ + metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ + SEP \ + MACRO(6, CONTEXT, _6) + +#define metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ + metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ + SEP \ + MACRO(7, CONTEXT, _7) + +#define metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ + metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ + SEP \ + MACRO(8, CONTEXT, _8) + +#define metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ + SEP \ + MACRO(9, CONTEXT, _9) + +#define metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ + SEP \ + MACRO(10, CONTEXT, _10) + +#define metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ + metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + SEP \ + MACRO(11, CONTEXT, _11) + +#define metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ + metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ + SEP \ + MACRO(12, CONTEXT, _12) + +#define metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ + metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ + SEP \ + MACRO(13, CONTEXT, _13) + +#define metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ + metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ + SEP \ + MACRO(14, CONTEXT, _14) + +#define metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ + metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ + SEP \ + MACRO(15, CONTEXT, _15) + +#define metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ + metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ + SEP \ + MACRO(16, CONTEXT, _16) + +#define metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ + metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ + SEP \ + MACRO(17, CONTEXT, _17) + +#define metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ + metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ + SEP \ + MACRO(18, CONTEXT, _18) + +#define metamacro_foreach_cxt_recursive20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ + metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ + SEP \ + MACRO(19, CONTEXT, _19) + +// metamacro_for_cxt expansions +#define metamacro_for_cxt0(MACRO, SEP, CONTEXT) +#define metamacro_for_cxt1(MACRO, SEP, CONTEXT) MACRO(0, CONTEXT) + +#define metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt1(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(1, CONTEXT) + +#define metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(2, CONTEXT) + +#define metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(3, CONTEXT) + +#define metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(4, CONTEXT) + +#define metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(5, CONTEXT) + +#define metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(6, CONTEXT) + +#define metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(7, CONTEXT) + +#define metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(8, CONTEXT) + +#define metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(9, CONTEXT) + +#define metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(10, CONTEXT) + +#define metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(11, CONTEXT) + +#define metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(12, CONTEXT) + +#define metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(13, CONTEXT) + +#define metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(14, CONTEXT) + +#define metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(15, CONTEXT) + +#define metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(16, CONTEXT) + +#define metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(17, CONTEXT) + +#define metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(18, CONTEXT) + +#define metamacro_for_cxt20(MACRO, SEP, CONTEXT) \ + metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ + SEP \ + MACRO(19, CONTEXT) + +// metamacro_if_eq expansions +#define metamacro_if_eq0(VALUE) \ + metamacro_concat(metamacro_if_eq0_, VALUE) + +#define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_ +#define metamacro_if_eq0_1(...) metamacro_expand_ +#define metamacro_if_eq0_2(...) metamacro_expand_ +#define metamacro_if_eq0_3(...) metamacro_expand_ +#define metamacro_if_eq0_4(...) metamacro_expand_ +#define metamacro_if_eq0_5(...) metamacro_expand_ +#define metamacro_if_eq0_6(...) metamacro_expand_ +#define metamacro_if_eq0_7(...) metamacro_expand_ +#define metamacro_if_eq0_8(...) metamacro_expand_ +#define metamacro_if_eq0_9(...) metamacro_expand_ +#define metamacro_if_eq0_10(...) metamacro_expand_ +#define metamacro_if_eq0_11(...) metamacro_expand_ +#define metamacro_if_eq0_12(...) metamacro_expand_ +#define metamacro_if_eq0_13(...) metamacro_expand_ +#define metamacro_if_eq0_14(...) metamacro_expand_ +#define metamacro_if_eq0_15(...) metamacro_expand_ +#define metamacro_if_eq0_16(...) metamacro_expand_ +#define metamacro_if_eq0_17(...) metamacro_expand_ +#define metamacro_if_eq0_18(...) metamacro_expand_ +#define metamacro_if_eq0_19(...) metamacro_expand_ +#define metamacro_if_eq0_20(...) metamacro_expand_ + +#define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE)) +#define metamacro_if_eq2(VALUE) metamacro_if_eq1(metamacro_dec(VALUE)) +#define metamacro_if_eq3(VALUE) metamacro_if_eq2(metamacro_dec(VALUE)) +#define metamacro_if_eq4(VALUE) metamacro_if_eq3(metamacro_dec(VALUE)) +#define metamacro_if_eq5(VALUE) metamacro_if_eq4(metamacro_dec(VALUE)) +#define metamacro_if_eq6(VALUE) metamacro_if_eq5(metamacro_dec(VALUE)) +#define metamacro_if_eq7(VALUE) metamacro_if_eq6(metamacro_dec(VALUE)) +#define metamacro_if_eq8(VALUE) metamacro_if_eq7(metamacro_dec(VALUE)) +#define metamacro_if_eq9(VALUE) metamacro_if_eq8(metamacro_dec(VALUE)) +#define metamacro_if_eq10(VALUE) metamacro_if_eq9(metamacro_dec(VALUE)) +#define metamacro_if_eq11(VALUE) metamacro_if_eq10(metamacro_dec(VALUE)) +#define metamacro_if_eq12(VALUE) metamacro_if_eq11(metamacro_dec(VALUE)) +#define metamacro_if_eq13(VALUE) metamacro_if_eq12(metamacro_dec(VALUE)) +#define metamacro_if_eq14(VALUE) metamacro_if_eq13(metamacro_dec(VALUE)) +#define metamacro_if_eq15(VALUE) metamacro_if_eq14(metamacro_dec(VALUE)) +#define metamacro_if_eq16(VALUE) metamacro_if_eq15(metamacro_dec(VALUE)) +#define metamacro_if_eq17(VALUE) metamacro_if_eq16(metamacro_dec(VALUE)) +#define metamacro_if_eq18(VALUE) metamacro_if_eq17(metamacro_dec(VALUE)) +#define metamacro_if_eq19(VALUE) metamacro_if_eq18(metamacro_dec(VALUE)) +#define metamacro_if_eq20(VALUE) metamacro_if_eq19(metamacro_dec(VALUE)) + +// metamacro_if_eq_recursive expansions +#define metamacro_if_eq_recursive0(VALUE) \ + metamacro_concat(metamacro_if_eq_recursive0_, VALUE) + +#define metamacro_if_eq_recursive0_0(...) __VA_ARGS__ metamacro_consume_ +#define metamacro_if_eq_recursive0_1(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_2(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_3(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_4(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_5(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_6(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_7(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_8(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_9(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_10(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_11(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_12(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_13(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_14(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_15(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_16(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_17(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_18(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_19(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_20(...) metamacro_expand_ + +#define metamacro_if_eq_recursive1(VALUE) metamacro_if_eq_recursive0(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive2(VALUE) metamacro_if_eq_recursive1(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive3(VALUE) metamacro_if_eq_recursive2(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive4(VALUE) metamacro_if_eq_recursive3(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive5(VALUE) metamacro_if_eq_recursive4(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive6(VALUE) metamacro_if_eq_recursive5(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive7(VALUE) metamacro_if_eq_recursive6(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive8(VALUE) metamacro_if_eq_recursive7(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive9(VALUE) metamacro_if_eq_recursive8(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive10(VALUE) metamacro_if_eq_recursive9(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive11(VALUE) metamacro_if_eq_recursive10(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive12(VALUE) metamacro_if_eq_recursive11(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive13(VALUE) metamacro_if_eq_recursive12(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive14(VALUE) metamacro_if_eq_recursive13(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive15(VALUE) metamacro_if_eq_recursive14(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive16(VALUE) metamacro_if_eq_recursive15(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive17(VALUE) metamacro_if_eq_recursive16(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive18(VALUE) metamacro_if_eq_recursive17(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive19(VALUE) metamacro_if_eq_recursive18(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive20(VALUE) metamacro_if_eq_recursive19(metamacro_dec(VALUE)) + +// metamacro_take expansions +#define metamacro_take0(...) +#define metamacro_take1(...) metamacro_head(__VA_ARGS__) +#define metamacro_take2(...) metamacro_head(__VA_ARGS__), metamacro_take1(metamacro_tail(__VA_ARGS__)) +#define metamacro_take3(...) metamacro_head(__VA_ARGS__), metamacro_take2(metamacro_tail(__VA_ARGS__)) +#define metamacro_take4(...) metamacro_head(__VA_ARGS__), metamacro_take3(metamacro_tail(__VA_ARGS__)) +#define metamacro_take5(...) metamacro_head(__VA_ARGS__), metamacro_take4(metamacro_tail(__VA_ARGS__)) +#define metamacro_take6(...) metamacro_head(__VA_ARGS__), metamacro_take5(metamacro_tail(__VA_ARGS__)) +#define metamacro_take7(...) metamacro_head(__VA_ARGS__), metamacro_take6(metamacro_tail(__VA_ARGS__)) +#define metamacro_take8(...) metamacro_head(__VA_ARGS__), metamacro_take7(metamacro_tail(__VA_ARGS__)) +#define metamacro_take9(...) metamacro_head(__VA_ARGS__), metamacro_take8(metamacro_tail(__VA_ARGS__)) +#define metamacro_take10(...) metamacro_head(__VA_ARGS__), metamacro_take9(metamacro_tail(__VA_ARGS__)) +#define metamacro_take11(...) metamacro_head(__VA_ARGS__), metamacro_take10(metamacro_tail(__VA_ARGS__)) +#define metamacro_take12(...) metamacro_head(__VA_ARGS__), metamacro_take11(metamacro_tail(__VA_ARGS__)) +#define metamacro_take13(...) metamacro_head(__VA_ARGS__), metamacro_take12(metamacro_tail(__VA_ARGS__)) +#define metamacro_take14(...) metamacro_head(__VA_ARGS__), metamacro_take13(metamacro_tail(__VA_ARGS__)) +#define metamacro_take15(...) metamacro_head(__VA_ARGS__), metamacro_take14(metamacro_tail(__VA_ARGS__)) +#define metamacro_take16(...) metamacro_head(__VA_ARGS__), metamacro_take15(metamacro_tail(__VA_ARGS__)) +#define metamacro_take17(...) metamacro_head(__VA_ARGS__), metamacro_take16(metamacro_tail(__VA_ARGS__)) +#define metamacro_take18(...) metamacro_head(__VA_ARGS__), metamacro_take17(metamacro_tail(__VA_ARGS__)) +#define metamacro_take19(...) metamacro_head(__VA_ARGS__), metamacro_take18(metamacro_tail(__VA_ARGS__)) +#define metamacro_take20(...) metamacro_head(__VA_ARGS__), metamacro_take19(metamacro_tail(__VA_ARGS__)) + +// metamacro_drop expansions +#define metamacro_drop0(...) __VA_ARGS__ +#define metamacro_drop1(...) metamacro_tail(__VA_ARGS__) +#define metamacro_drop2(...) metamacro_drop1(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop3(...) metamacro_drop2(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop4(...) metamacro_drop3(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop5(...) metamacro_drop4(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop6(...) metamacro_drop5(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop7(...) metamacro_drop6(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop8(...) metamacro_drop7(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop9(...) metamacro_drop8(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop10(...) metamacro_drop9(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop11(...) metamacro_drop10(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop12(...) metamacro_drop11(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop13(...) metamacro_drop12(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop14(...) metamacro_drop13(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop15(...) metamacro_drop14(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop16(...) metamacro_drop15(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop17(...) metamacro_drop16(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop18(...) metamacro_drop17(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop19(...) metamacro_drop18(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop20(...) metamacro_drop19(metamacro_tail(__VA_ARGS__)) + +#endif diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLJSONAdapter.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLJSONAdapter.h new file mode 100644 index 0000000..42a7e82 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLJSONAdapter.h @@ -0,0 +1,287 @@ +// +// MTLJSONAdapter.h +// Mantle +// +// Created by Justin Spahr-Summers on 2013-02-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +@protocol MTLModel; +@protocol MTLTransformerErrorHandling; + +/// A MTLModel object that supports being parsed from and serialized to JSON. +@protocol MTLJSONSerializing +@required + +/// Specifies how to map property keys to different key paths in JSON. +/// +/// Subclasses overriding this method should combine their values with those of +/// `super`. +/// +/// Values in the dictionary can either be key paths in the JSON representation +/// of the receiver or an array of such key paths. If an array is used, the +/// deserialized value will be a dictionary containing all of the keys in the +/// array. +/// +/// Any keys omitted will not participate in JSON serialization. +/// +/// Examples +/// +/// + (NSDictionary *)JSONKeyPathsByPropertyKey { +/// return @{ +/// @"name": @"POI.name", +/// @"point": @[ @"latitude", @"longitude" ], +/// @"starred": @"starred" +/// }; +/// } +/// +/// This will map the `starred` property to `JSONDictionary[@"starred"]`, `name` +/// to `JSONDictionary[@"POI"][@"name"]` and `point` to a dictionary equivalent +/// to: +/// +/// @{ +/// @"latitude": JSONDictionary[@"latitude"], +/// @"longitude": JSONDictionary[@"longitude"] +/// } +/// +/// Returns a dictionary mapping property keys to one or multiple JSON key paths +/// (as strings or arrays of strings). ++ (NSDictionary *)JSONKeyPathsByPropertyKey; + +@optional + +/// Specifies how to convert a JSON value to the given property key. If +/// reversible, the transformer will also be used to convert the property value +/// back to JSON. +/// +/// If the receiver implements a `+JSONTransformer` method, MTLJSONAdapter +/// will use the result of that method instead. +/// +/// Returns a value transformer, or nil if no transformation should be performed. ++ (NSValueTransformer *)JSONTransformerForKey:(NSString *)key; + +/// Overridden to parse the receiver as a different class, based on information +/// in the provided dictionary. +/// +/// This is mostly useful for class clusters, where the abstract base class would +/// be passed into -[MTLJSONAdapter initWithJSONDictionary:modelClass:], but +/// a subclass should be instantiated instead. +/// +/// JSONDictionary - The JSON dictionary that will be parsed. +/// +/// Returns the class that should be parsed (which may be the receiver), or nil +/// to abort parsing (e.g., if the data is invalid). ++ (Class)classForParsingJSONDictionary:(NSDictionary *)JSONDictionary; + +@end + +/// The domain for errors originating from MTLJSONAdapter. +extern NSString * const MTLJSONAdapterErrorDomain; + +/// +classForParsingJSONDictionary: returned nil for the given dictionary. +extern const NSInteger MTLJSONAdapterErrorNoClassFound; + +/// The provided JSONDictionary is not valid. +extern const NSInteger MTLJSONAdapterErrorInvalidJSONDictionary; + +/// The model's implementation of +JSONKeyPathsByPropertyKey included a key which +/// does not actually exist in +propertyKeys. +extern const NSInteger MTLJSONAdapterErrorInvalidJSONMapping; + +/// An exception was thrown and caught. +extern const NSInteger MTLJSONAdapterErrorExceptionThrown; + +/// Associated with the NSException that was caught. +extern NSString * const MTLJSONAdapterThrownExceptionErrorKey; + +/// Converts a MTLModel object to and from a JSON dictionary. +@interface MTLJSONAdapter : NSObject + +/// Attempts to parse a JSON dictionary into a model object. +/// +/// modelClass - The MTLModel subclass to attempt to parse from the JSON. +/// This class must conform to . This +/// argument must not be nil. +/// JSONDictionary - A dictionary representing JSON data. This should match the +/// format returned by NSJSONSerialization. If this argument is +/// nil, the method returns nil. +/// error - If not NULL, this may be set to an error that occurs during +/// parsing or initializing an instance of `modelClass`. +/// +/// Returns an instance of `modelClass` upon success, or nil if a parsing error +/// occurred. ++ (id)modelOfClass:(Class)modelClass fromJSONDictionary:(NSDictionary *)JSONDictionary error:(NSError **)error; + +/// Attempts to parse an array of JSON dictionary objects into a model objects +/// of a specific class. +/// +/// modelClass - The MTLModel subclass to attempt to parse from the JSON. This +/// class must conform to . This argument must +/// not be nil. +/// JSONArray - A array of dictionaries representing JSON data. This should +/// match the format returned by NSJSONSerialization. If this +/// argument is nil, the method returns nil. +/// error - If not NULL, this may be set to an error that occurs during +/// parsing or initializing an any of the instances of +/// `modelClass`. +/// +/// Returns an array of `modelClass` instances upon success, or nil if a parsing +/// error occurred. ++ (NSArray *)modelsOfClass:(Class)modelClass fromJSONArray:(NSArray *)JSONArray error:(NSError **)error; + +/// Converts a model into a JSON representation. +/// +/// model - The model to use for JSON serialization. This argument must not be +/// nil. +/// error - If not NULL, this may be set to an error that occurs during +/// serializing. +/// +/// Returns a JSON dictionary, or nil if a serialization error occurred. ++ (NSDictionary *)JSONDictionaryFromModel:(id)model error:(NSError **)error; + +/// Converts a array of models into a JSON representation. +/// +/// models - The array of models to use for JSON serialization. This argument +/// must not be nil. +/// error - If not NULL, this may be set to an error that occurs during +/// serializing. +/// +/// Returns a JSON array, or nil if a serialization error occurred for any +/// model. ++ (NSArray *)JSONArrayFromModels:(NSArray *)models error:(NSError **)error; + +/// Initializes the receiver with a given model class. +/// +/// modelClass - The MTLModel subclass to attempt to parse from the JSON and +/// back. This class must conform to . This +/// argument must not be nil. +/// +/// Returns an initialized adapter. +- (id)initWithModelClass:(Class)modelClass; + +/// Deserializes a model from a JSON dictionary. +/// +/// The adapter will call -validate: on the model and consider it an error if the +/// validation fails. +/// +/// JSONDictionary - A dictionary representing JSON data. This should match the +/// format returned by NSJSONSerialization. This argument must +/// not be nil. +/// error - If not NULL, this may be set to an error that occurs during +/// deserializing or validation. +/// +/// Returns a model object, or nil if a deserialization error occurred or the +/// model did not validate successfully. +- (id)modelFromJSONDictionary:(NSDictionary *)JSONDictionary error:(NSError **)error; + +/// Serializes a model into JSON. +/// +/// model - The model to use for JSON serialization. This argument must not be +/// nil. +/// error - If not NULL, this may be set to an error that occurs during +/// serializing. +/// +/// Returns a model object, or nil if a serialization error occurred. +- (NSDictionary *)JSONDictionaryFromModel:(id)model error:(NSError **)error; + +/// Filters the property keys used to serialize a given model. +/// +/// propertyKeys - The property keys for which `model` provides a mapping. +/// model - The model being serialized. +/// +/// Subclasses may override this method to determine which property keys should +/// be used when serializing `model`. For instance, this method can be used to +/// create more efficient updates of server-side resources. +/// +/// The default implementation simply returns `propertyKeys`. +/// +/// Returns a subset of propertyKeys that should be serialized for a given +/// model. +- (NSSet *)serializablePropertyKeys:(NSSet *)propertyKeys forModel:(id)model; + +/// An optional value transformer that should be used for properties of the given +/// class. +/// +/// A value transformer returned by the model's +JSONTransformerForKey: method +/// is given precedence over the one returned by this method. +/// +/// The default implementation invokes `+JSONTransformer` on the +/// receiver if it's implemented. It supports NSURL conversion through +/// +NSURLJSONTransformer. +/// +/// modelClass - The class of the property to serialize. This property must not be +/// nil. +/// +/// Returns a value transformer or nil if no transformation should be used. ++ (NSValueTransformer *)transformerForModelPropertiesOfClass:(Class)modelClass; + +/// A value transformer that should be used for a properties of the given +/// primitive type. +/// +/// If `objCType` matches @‎encode(id), the value transformer returned by +/// +transformerForModelPropertiesOfClass: is used instead. +/// +/// The default implementation transforms properties that match @‎encode(BOOL) +/// using the MTLBooleanValueTransformerName transformer. +/// +/// objCType - The type encoding for the value of this property. This is the type +/// as it would be returned by the @‎encode() directive. +/// +/// Returns a value transformer or nil if no transformation should be used. ++ (NSValueTransformer *)transformerForModelPropertiesOfObjCType:(const char *)objCType; + +@end + +@interface MTLJSONAdapter (ValueTransformers) + +/// Creates a reversible transformer to convert a JSON dictionary into a MTLModel +/// object, and vice-versa. +/// +/// modelClass - The MTLModel subclass to attempt to parse from the JSON. This +/// class must conform to . This argument must +/// not be nil. +/// +/// Returns a reversible transformer which uses the class of the receiver for +/// transforming values back and forth. ++ (NSValueTransformer *)dictionaryTransformerWithModelClass:(Class)modelClass; + +/// Creates a reversible transformer to convert an array of JSON dictionaries +/// into an array of MTLModel objects, and vice-versa. +/// +/// modelClass - The MTLModel subclass to attempt to parse from each JSON +/// dictionary. This class must conform to . +/// This argument must not be nil. +/// +/// Returns a reversible transformer which uses the class of the receiver for +/// transforming array elements back and forth. ++ (NSValueTransformer *)arrayTransformerWithModelClass:(Class)modelClass; + +/// This value transformer is used by MTLJSONAdapter to automatically convert +/// NSURL properties to JSON strings and vice versa. ++ (NSValueTransformer *)NSURLJSONTransformer; + +/// This value transformer is used by MTLJSONAdapter to automatically convert +/// NSUUID properties to JSON strings and vice versa. ++ (NSValueTransformer *)NSUUIDJSONTransformer; + +@end + +@class MTLModel; + +@interface MTLJSONAdapter (Deprecated) + +@property (nonatomic, strong, readonly) id model __attribute__((unavailable("Replaced by -modelFromJSONDictionary:error:"))); + ++ (NSArray *)JSONArrayFromModels:(NSArray *)models __attribute__((deprecated("Replaced by +JSONArrayFromModels:error:"))) NS_SWIFT_UNAVAILABLE("Replaced by +JSONArrayFromModels:error:"); + ++ (NSDictionary *)JSONDictionaryFromModel:(MTLModel *)model __attribute__((deprecated("Replaced by +JSONDictionaryFromModel:error:"))) NS_SWIFT_UNAVAILABLE("Replaced by +JSONDictionaryFromModel:error:"); + +- (NSDictionary *)JSONDictionary __attribute__((unavailable("Replaced by -JSONDictionaryFromModel:error:"))) NS_SWIFT_UNAVAILABLE("Replaced by -JSONDictionaryFromModel:error:"); +- (NSString *)JSONKeyPathForPropertyKey:(NSString *)key __attribute__((unavailable("Replaced by -serializablePropertyKeys:forModel:"))); +- (id)initWithJSONDictionary:(NSDictionary *)JSONDictionary modelClass:(Class)modelClass error:(NSError **)error __attribute__((unavailable("Replaced by -initWithModelClass:"))); +- (id)initWithModel:(id)model __attribute__((unavailable("Replaced by -initWithModelClass:"))) NS_SWIFT_UNAVAILABLE("Replaced by -initWithModelClass:"); +- (NSDictionary *)serializeToJSONDictionary:(NSError **)error __attribute__((unavailable("Replaced by -JSONDictionaryFromModel:error:"))) NS_SWIFT_UNAVAILABLE("Replaced by -JSONDictionaryFromModel:error:"); + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLModel+NSCoding.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLModel+NSCoding.h new file mode 100644 index 0000000..13cbd55 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLModel+NSCoding.h @@ -0,0 +1,132 @@ +// +// MTLModel+NSCoding.h +// Mantle +// +// Created by Justin Spahr-Summers on 2013-02-12. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#if __has_include() +#import +#else +#import "MTLModel.h" +#endif + +/// Defines how a MTLModel property key should be encoded into an archive. +/// +/// MTLModelEncodingBehaviorExcluded - The property should never be encoded. +/// MTLModelEncodingBehaviorUnconditional - The property should always be +/// encoded. +/// MTLModelEncodingBehaviorConditional - The object should be encoded only +/// if unconditionally encoded elsewhere. +/// This should only be used for object +/// properties. +typedef enum : NSUInteger { + MTLModelEncodingBehaviorExcluded = 0, + MTLModelEncodingBehaviorUnconditional, + MTLModelEncodingBehaviorConditional, +} MTLModelEncodingBehavior; + +/// Implements default archiving and unarchiving behaviors for MTLModel. +@interface MTLModel (NSCoding) + +/// Initializes the receiver from an archive. +/// +/// This will decode the original +modelVersion of the archived object, then +/// invoke -decodeValueForKey:withCoder:modelVersion: for each of the receiver's +/// +propertyKeys. +/// +/// Returns an initialized model object, or nil if a decoding error occurred. +- (id)initWithCoder:(NSCoder *)coder; + +/// Archives the receiver using the given coder. +/// +/// This will encode the receiver's +modelVersion, then the receiver's properties +/// according to the behaviors specified in +encodingBehaviorsByPropertyKey. +- (void)encodeWithCoder:(NSCoder *)coder; + +/// Determines how the +propertyKeys of the class are encoded into an archive. +/// The values of this dictionary should be boxed MTLModelEncodingBehavior +/// values. +/// +/// Any keys not present in the dictionary will be excluded from the archive. +/// +/// Subclasses overriding this method should combine their values with those of +/// `super`. +/// +/// Returns a dictionary mapping the receiver's +propertyKeys to default encoding +/// behaviors. If a property is an object with `weak` semantics, the default +/// behavior is MTLModelEncodingBehaviorConditional; otherwise, the default is +/// MTLModelEncodingBehaviorUnconditional. ++ (NSDictionary *)encodingBehaviorsByPropertyKey; + +/// Determines the classes that are allowed to be decoded for each of the +/// receiver's properties when using . The values of this +/// dictionary should be NSArrays of Class objects. +/// +/// If any encodable keys (as determined by +encodingBehaviorsByPropertyKey) are +/// not present in the dictionary, an exception will be thrown during secure +/// encoding or decoding. +/// +/// Subclasses overriding this method should combine their values with those of +/// `super`. +/// +/// Returns a dictionary mapping the receiver's encodable keys (as determined by +/// +encodingBehaviorsByPropertyKey) to default allowed classes, based on the +/// type that each property is declared as. If type of an encodable property +/// cannot be determined (e.g., it is declared as `id`), it will be omitted from +/// the dictionary, and subclasses must provide a valid value to prevent an +/// exception being thrown during encoding/decoding. ++ (NSDictionary *)allowedSecureCodingClassesByPropertyKey; + +/// Decodes the value of the given property key from an archive. +/// +/// By default, this method looks for a `-decodeWithCoder:modelVersion:` +/// method on the receiver, and invokes it if found. +/// +/// If the custom method is not implemented and `coder` does not require secure +/// coding, `-[NSCoder decodeObjectForKey:]` will be invoked with the given +/// `key`. +/// +/// If the custom method is not implemented and `coder` requires secure coding, +/// `-[NSCoder decodeObjectOfClasses:forKey:]` will be invoked with the +/// information from +allowedSecureCodingClassesByPropertyKey and the given `key`. The +/// receiver must conform to for this to work correctly. +/// +/// key - The property key to decode the value for. This argument cannot +/// be nil. +/// coder - The NSCoder representing the archive being decoded. This +/// argument cannot be nil. +/// modelVersion - The version of the original model object that was encoded. +/// +/// Returns the decoded and boxed value, or nil if the key was not present. +- (id)decodeValueForKey:(NSString *)key withCoder:(NSCoder *)coder modelVersion:(NSUInteger)modelVersion; + +/// The version of this MTLModel subclass. +/// +/// This version number is saved in archives so that later model changes can be +/// made backwards-compatible with old versions. +/// +/// Subclasses should override this method to return a higher version number +/// whenever a breaking change is made to the model. +/// +/// Returns 0. ++ (NSUInteger)modelVersion; + +@end + +/// This method must be overridden to support archives created by older versions +/// of Mantle (before the `MTLModel+NSCoding` interface existed). +@interface MTLModel (OldArchiveSupport) + +/// Converts an archived external representation to a dictionary suitable for +/// passing to -initWithDictionary:. +/// +/// externalRepresentation - The decoded external representation of the receiver. +/// fromVersion - The model version at the time the external +/// representation was encoded. +/// +/// Returns nil by default, indicating that conversion failed. ++ (NSDictionary *)dictionaryValueFromArchivedExternalRepresentation:(NSDictionary *)externalRepresentation version:(NSUInteger)fromVersion; + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLModel.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLModel.h new file mode 100644 index 0000000..96042b4 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLModel.h @@ -0,0 +1,179 @@ +// +// MTLModel.h +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-11. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +/// Defines a property's storage behavior, which affects how it will be copied, +/// compared, and persisted. +/// +/// MTLPropertyStorageNone - This property is not included in -description, +/// -hash, or anything else. +/// MTLPropertyStorageTransitory - This property is included in one-off +/// operations like -copy and -dictionaryValue but +/// does not affect -isEqual: or -hash. +/// It may disappear at any time. +/// MTLPropertyStoragePermanent - The property is included in serialization +/// (like `NSCoding`) and equality, since it can +/// be expected to stick around. +typedef enum : NSUInteger { + MTLPropertyStorageNone, + MTLPropertyStorageTransitory, + MTLPropertyStoragePermanent, +} MTLPropertyStorage; + +/// This protocol defines the minimal interface that classes need to implement to +/// interact with Mantle adapters. +/// +/// It is intended for scenarios where inheriting from MTLModel is not feasible. +/// However, clients are encouraged to subclass the MTLModel class if they can. +/// +/// Clients that wish to implement their own adapters should target classes +/// conforming to this protocol rather than subclasses of MTLModel to ensure +/// maximum compatibility. +@protocol MTLModel + +/// Initializes a new instance of the receiver using key-value coding, setting +/// the keys and values in the given dictionary. +/// +/// dictionaryValue - Property keys and values to set on the instance. Any NSNull +/// values will be converted to nil before being used. KVC +/// validation methods will automatically be invoked for all of +/// the properties given. +/// error - If not NULL, this may be set to any error that occurs +/// (like a KVC validation error). +/// +/// Returns an initialized model object, or nil if validation failed. ++ (instancetype)modelWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error; + +/// A dictionary representing the properties of the receiver. +/// +/// Combines the values corresponding to all +propertyKeys into a dictionary, +/// with any nil values represented by NSNull. +/// +/// This property must never be nil. +@property (nonatomic, copy, readonly) NSDictionary *dictionaryValue; + +/// Initializes the receiver using key-value coding, setting the keys and values +/// in the given dictionary. +/// +/// Subclass implementations may override this method, calling the super +/// implementation, in order to perform further processing and initialization +/// after deserialization. +/// +/// dictionaryValue - Property keys and values to set on the receiver. Any NSNull +/// values will be converted to nil before being used. KVC +/// validation methods will automatically be invoked for all of +/// the properties given. If nil, this method is equivalent to +/// -init. +/// error - If not NULL, this may be set to any error that occurs +/// (like a KVC validation error). +/// +/// Returns an initialized model object, or nil if validation failed. +- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error; + +/// Merges the value of the given key on the receiver with the value of the same +/// key from the given model object, giving precedence to the other model object. +- (void)mergeValueForKey:(NSString *)key fromModel:(id)model; + +/// Returns the keys for all @property declarations, except for `readonly` +/// properties without ivars, or properties on MTLModel itself. ++ (NSSet *)propertyKeys; + +/// Validates the model. +/// +/// error - If not NULL, this may be set to any error that occurs during +/// validation +/// +/// Returns YES if the model is valid, or NO if the validation failed. +- (BOOL)validate:(NSError **)error; + +@end + +/// An abstract base class for model objects, using reflection to provide +/// sensible default behaviors. +/// +/// The default implementations of , -hash, and -isEqual: make use of +/// the +propertyKeys method. +@interface MTLModel : NSObject + +/// Initializes the receiver using key-value coding, setting the keys and values +/// in the given dictionary. +/// +/// dictionaryValue - Property keys and values to set on the receiver. Any NSNull +/// values will be converted to nil before being used. KVC +/// validation methods will automatically be invoked for all of +/// the properties given. If nil, this method is equivalent to +/// -init. +/// error - If not NULL, this may be set to any error that occurs +/// (like a KVC validation error). +/// +/// Returns an initialized model object, or nil if validation failed. +- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error; + +/// Initializes the receiver with default values. +/// +/// This is the designated initializer for this class. +- (instancetype)init; + +/// By default, this method looks for a `-mergeFromModel:` method on the +/// receiver, and invokes it if found. If not found, and `model` is not nil, the +/// value for the given key is taken from `model`. +- (void)mergeValueForKey:(NSString *)key fromModel:(id)model; + +/// Merges the values of the given model object into the receiver, using +/// -mergeValueForKey:fromModel: for each key in +propertyKeys. +/// +/// `model` must be an instance of the receiver's class or a subclass thereof. +- (void)mergeValuesForKeysFromModel:(id)model; + +/// The storage behavior of a given key. +/// +/// The default implementation returns MTLPropertyStorageNone for properties that +/// are readonly and not backed by an instance variable and +/// MTLPropertyStoragePermanent otherwise. +/// +/// Subclasses can use this method to prevent MTLModel from resolving circular +/// references by returning MTLPropertyStorageTransitory. +/// +/// Returns the storage behavior for a given key on the receiver. ++ (MTLPropertyStorage)storageBehaviorForPropertyWithKey:(NSString *)propertyKey; + +/// Compares the receiver with another object for equality. +/// +/// The default implementation is equivalent to comparing all properties of both +/// models for which +storageBehaviorForPropertyWithKey: returns +/// MTLPropertyStoragePermanent. +/// +/// Returns YES if the two models are considered equal, NO otherwise. +- (BOOL)isEqual:(id)object; + +/// A string that describes the contents of the receiver. +/// +/// The default implementation is based on the receiver's class and all its +/// properties for which +storageBehaviorForPropertyWithKey: returns +/// MTLPropertyStoragePermanent. +- (NSString *)description; + +@end + +/// Implements validation logic for MTLModel. +@interface MTLModel (Validation) + +/// Validates the model. +/// +/// The default implementation simply invokes -validateValue:forKey:error: with +/// all +propertyKeys and their current value. If -validateValue:forKey:error: +/// returns a new value, the property is set to that new value. +/// +/// error - If not NULL, this may be set to any error that occurs during +/// validation +/// +/// Returns YES if the model is valid, or NO if the validation failed. +- (BOOL)validate:(NSError **)error; + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLTransformerErrorHandling.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLTransformerErrorHandling.h new file mode 100644 index 0000000..2726d91 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLTransformerErrorHandling.h @@ -0,0 +1,66 @@ +// +// MTLTransformerErrorHandling.h +// Mantle +// +// Created by Robert Böhnke on 10/6/13. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +/// The domain for errors originating from the MTLTransformerErrorHandling +/// protocol. +/// +/// Transformers conforming to this protocol are expected to use this error +/// domain if the transformation fails. +extern NSString * const MTLTransformerErrorHandlingErrorDomain; + +/// Used to indicate that the input value was illegal. +/// +/// Transformers conforming to this protocol are expected to use this error code +/// if the transformation fails due to an invalid input value. +extern const NSInteger MTLTransformerErrorHandlingErrorInvalidInput; + +/// Associated with the invalid input value. +/// +/// Transformers conforming to this protocol are expected to associate this key +/// with the invalid input in the userInfo dictionary. +extern NSString * const MTLTransformerErrorHandlingInputValueErrorKey; + +/// This protocol can be implemented by NSValueTransformer subclasses to +/// communicate errors that occur during transformation. +@protocol MTLTransformerErrorHandling +@required + +/// Transforms a value, returning any error that occurred during transformation. +/// +/// value - The value to transform. +/// success - If not NULL, this will be set to a boolean indicating whether the +/// transformation was successful. +/// error - If not NULL, this may be set to an error that occurs during +/// transforming the value. +/// +/// Returns the result of the transformation which may be nil. Clients should +/// inspect the success parameter to decide how to proceed with the result. +- (id)transformedValue:(id)value success:(BOOL *)success error:(NSError **)error; + +@optional + +/// Reverse-transforms a value, returning any error that occurred during +/// transformation. +/// +/// Transformers conforming to this protocol are expected to implemented this +/// method if they support reverse transformation. +/// +/// value - The value to transform. +/// success - If not NULL, this will be set to a boolean indicating whether the +/// transformation was successful. +/// error - If not NULL, this may be set to an error that occurs during +/// transforming the value. +/// +/// Returns the result of the reverse transformation which may be nil. Clients +/// should inspect the success parameter to decide how to proceed with the +/// result. +- (id)reverseTransformedValue:(id)value success:(BOOL *)success error:(NSError **)error; + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLValueTransformer.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLValueTransformer.h new file mode 100644 index 0000000..a10ae7b --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/MTLValueTransformer.h @@ -0,0 +1,56 @@ +// +// MTLValueTransformer.h +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-11. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +#if __has_include() +#import +#else +#import "MTLTransformerErrorHandling.h" +#endif + +/// A block that represents a transformation. +/// +/// value - The value to transform. +/// success - The block must set this parameter to indicate whether the +/// transformation was successful. +/// MTLValueTransformer will always call this block with *success +/// initialized to YES. +/// error - If not NULL, this may be set to an error that occurs during +/// transforming the value. +/// +/// Returns the result of the transformation, which may be nil. +typedef id (^MTLValueTransformerBlock)(id value, BOOL *success, NSError **error); + +/// +/// A value transformer supporting block-based transformation. +/// +@interface MTLValueTransformer : NSValueTransformer + +/// Returns a transformer which transforms values using the given block. Reverse +/// transformations will not be allowed. ++ (instancetype)transformerUsingForwardBlock:(MTLValueTransformerBlock)transformation; + +/// Returns a transformer which transforms values using the given block, for +/// forward or reverse transformations. ++ (instancetype)transformerUsingReversibleBlock:(MTLValueTransformerBlock)transformation; + +/// Returns a transformer which transforms values using the given blocks. ++ (instancetype)transformerUsingForwardBlock:(MTLValueTransformerBlock)forwardTransformation reverseBlock:(MTLValueTransformerBlock)reverseTransformation; + +@end + +@interface MTLValueTransformer (Deprecated) + ++ (NSValueTransformer *)transformerWithBlock:(id (^)(id))transformationBlock __attribute__((deprecated("Replaced by +transformerUsingForwardBlock:"))); + ++ (NSValueTransformer *)reversibleTransformerWithBlock:(id (^)(id))transformationBlock __attribute__((deprecated("Replaced by +transformerUsingReversibleBlock:"))); + ++ (NSValueTransformer *)reversibleTransformerWithForwardBlock:(id (^)(id))forwardBlock reverseBlock:(id (^)(id))reverseBlock __attribute__((deprecated("Replaced by +transformerUsingForwardBlock:reverseBlock:"))); + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/Mantle.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/Mantle.h new file mode 100644 index 0000000..9bf42ff --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/Mantle.h @@ -0,0 +1,41 @@ +// +// Mantle.h +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-04. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +//! Project version number for Mantle. +FOUNDATION_EXPORT double MantleVersionNumber; + +//! Project version string for Mantle. +FOUNDATION_EXPORT const unsigned char MantleVersionString[]; + +#if __has_include() +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#else +#import "MTLJSONAdapter.h" +#import "MTLModel.h" +#import "MTLModel+NSCoding.h" +#import "MTLValueTransformer.h" +#import "MTLTransformerErrorHandling.h" +#import "NSArray+MTLManipulationAdditions.h" +#import "NSDictionary+MTLManipulationAdditions.h" +#import "NSDictionary+MTLMappingAdditions.h" +#import "NSObject+MTLComparisonAdditions.h" +#import "NSValueTransformer+MTLInversionAdditions.h" +#import "NSValueTransformer+MTLPredefinedTransformerAdditions.h" +#endif diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSArray+MTLManipulationAdditions.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSArray+MTLManipulationAdditions.h new file mode 100644 index 0000000..b8c2b56 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSArray+MTLManipulationAdditions.h @@ -0,0 +1,28 @@ +// +// NSArray+MTLManipulationAdditions.h +// Mantle +// +// Created by Josh Abernathy on 9/19/12. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +@interface NSArray (MTLManipulationAdditions) + +/// The first object in the array or nil if the array is empty. +/// Forwards to `firstObject` which has been first declared in iOS7, but works with iOS4/10.6. +@property (nonatomic, readonly, strong) id mtl_firstObject; + +/// Returns a new array without all instances of the given object. +- (NSArray *)mtl_arrayByRemovingObject:(id)object; + +/// Returns a new array without the first object. If the array is empty, it +/// returns the empty array. +- (NSArray *)mtl_arrayByRemovingFirstObject; + +/// Returns a new array without the last object. If the array is empty, it +/// returns the empty array. +- (NSArray *)mtl_arrayByRemovingLastObject; + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSDictionary+MTLManipulationAdditions.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSDictionary+MTLManipulationAdditions.h new file mode 100644 index 0000000..6126293 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSDictionary+MTLManipulationAdditions.h @@ -0,0 +1,31 @@ +// +// NSDictionary+MTLManipulationAdditions.h +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-24. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +@interface NSDictionary (MTLManipulationAdditions) + +/// Merges the keys and values from the given dictionary into the receiver. If +/// both the receiver and `dictionary` have a given key, the value from +/// `dictionary` is used. +/// +/// Returns a new dictionary containing the entries of the receiver combined with +/// those of `dictionary`. +- (NSDictionary *)mtl_dictionaryByAddingEntriesFromDictionary:(NSDictionary *)dictionary; + +/// Creates a new dictionary with all the entries for the given keys removed from +/// the receiver. +- (NSDictionary *)mtl_dictionaryByRemovingValuesForKeys:(NSArray *)keys; + +@end + +@interface NSDictionary (MTLManipulationAdditions_Deprecated) + +- (NSDictionary *)mtl_dictionaryByRemovingEntriesWithKeys:(NSSet *)keys __attribute__((deprecated("Replaced by -mtl_dictionaryByRemovingValuesForKeys:"))); + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSDictionary+MTLMappingAdditions.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSDictionary+MTLMappingAdditions.h new file mode 100644 index 0000000..1d908c4 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSDictionary+MTLMappingAdditions.h @@ -0,0 +1,21 @@ +// +// NSDictionary+MTLMappingAdditions.h +// Mantle +// +// Created by Robert Böhnke on 10/31/13. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +@interface NSDictionary (MTLMappingAdditions) + +/// Creates an identity mapping for serialization. +/// +/// class - A subclass of MTLModel. +/// +/// Returns a dictionary that maps all properties of the given class to +/// themselves. ++ (NSDictionary *)mtl_identityPropertyMapWithModel:(Class)modelClass; + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSObject+MTLComparisonAdditions.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSObject+MTLComparisonAdditions.h new file mode 100644 index 0000000..0ca13e6 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSObject+MTLComparisonAdditions.h @@ -0,0 +1,15 @@ +// +// NSObject+MTLComparisonAdditions.h +// Mantle +// +// Created by Josh Vera on 10/26/12. +// Copyright (c) 2012 GitHub. All rights reserved. +// +// Portions copyright (c) 2011 Bitswift. All rights reserved. +// See the LICENSE file for more information. +// + +#import + +/// Returns whether both objects are identical or equal via -isEqual: +BOOL MTLEqualObjects(id obj1, id obj2); diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSValueTransformer+MTLInversionAdditions.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSValueTransformer+MTLInversionAdditions.h new file mode 100644 index 0000000..ca29b98 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSValueTransformer+MTLInversionAdditions.h @@ -0,0 +1,21 @@ +// +// NSValueTransformer+MTLInversionAdditions.h +// Mantle +// +// Created by Justin Spahr-Summers on 2013-05-18. +// Copyright (c) 2013 GitHub. All rights reserved. +// + +#import + +@interface NSValueTransformer (MTLInversionAdditions) + +/// Flips the direction of the receiver's transformation, such that +/// -transformedValue: will become -reverseTransformedValue:, and vice-versa. +/// +/// The receiver must allow reverse transformation. +/// +/// Returns an inverted transformer. +- (NSValueTransformer *)mtl_invertedTransformer; + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSValueTransformer+MTLPredefinedTransformerAdditions.h b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSValueTransformer+MTLPredefinedTransformerAdditions.h new file mode 100644 index 0000000..ef4ea65 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/Mantle/include/NSValueTransformer+MTLPredefinedTransformerAdditions.h @@ -0,0 +1,122 @@ +// +// NSValueTransformer+MTLPredefinedTransformerAdditions.h +// Mantle +// +// Created by Justin Spahr-Summers on 2012-09-27. +// Copyright (c) 2012 GitHub. All rights reserved. +// + +#import + +#if __has_include() +#import +#else +#import "MTLTransformerErrorHandling.h" +#endif + +/// The name for a value transformer that converts strings into URLs and back. +extern NSString * const MTLURLValueTransformerName; + +/// The name for a value transformer that converts strings into NSUUIDs and back. +extern NSString * const MTLUUIDValueTransformerName; + +/// Ensure an NSNumber is backed by __NSCFBoolean/CFBooleanRef +/// +/// NSJSONSerialization, and likely other serialization libraries, ordinarily +/// serialize NSNumbers as numbers, and thus booleans would be serialized as +/// 0/1. The exception is when the NSNumber is backed by __NSCFBoolean, which, +/// though very much an implementation detail, is detected and serialized as a +/// proper boolean. +extern NSString * const MTLBooleanValueTransformerName; + +@interface NSValueTransformer (MTLPredefinedTransformerAdditions) + +/// An optionally reversible transformer which applies the given transformer to +/// each element of an array. +/// +/// transformer - The transformer to apply to each element. If the transformer +/// is reversible, the transformer returned by this method will be +/// reversible. This argument must not be nil. +/// +/// Returns a transformer which applies a transformation to each element of an +/// array. ++ (NSValueTransformer *)mtl_arrayMappingTransformerWithTransformer:(NSValueTransformer *)transformer; + +/// A reversible value transformer to transform between the keys and objects of a +/// dictionary. +/// +/// dictionary - The dictionary whose keys and values should be +/// transformed between. This argument must not be nil. +/// defaultValue - The result to fall back to, in case no key matching the +/// input value was found during a forward transformation. +/// reverseDefaultValue - The result to fall back to, in case no value matching +/// the input value was found during a reverse +/// transformation. +/// +/// Can for example be used for transforming between enum values and their string +/// representation. +/// +/// NSValueTransformer *valueTransformer = [NSValueTransformer mtl_valueMappingTransformerWithDictionary:@{ +/// @"foo": @(EnumDataTypeFoo), +/// @"bar": @(EnumDataTypeBar), +/// } defaultValue: @(EnumDataTypeUndefined) reverseDefaultValue: @"undefined"]; +/// +/// Returns a transformer which will map from keys to objects for forward +/// transformations, and from objects to keys for reverse transformations. ++ (NSValueTransformer *)mtl_valueMappingTransformerWithDictionary:(NSDictionary *)dictionary defaultValue:(id)defaultValue reverseDefaultValue:(id)reverseDefaultValue; + +/// Returns a value transformer created by calling +/// `+mtl_valueMappingTransformerWithDictionary:defaultValue:reverseDefaultValue:` +/// with a default value of `nil` and a reverse default value of `nil`. ++ (NSValueTransformer *)mtl_valueMappingTransformerWithDictionary:(NSDictionary *)dictionary; + +/// A reversible value transformer to transform between a date and its string +/// representation +/// +/// dateFormat - The date format used by the date formatter (http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Field_Symbol_Table) +/// calendar - The calendar used by the date formatter +/// locale - The locale used by the date formatter +/// timeZone - The time zone used by the date formatter +/// +/// Returns a transformer which will map from strings to dates for forward +/// transformations, and from dates to strings for reverse transformations. ++ (NSValueTransformer *)mtl_dateTransformerWithDateFormat:(NSString *)dateFormat calendar:(NSCalendar *)calendar locale:(NSLocale *)locale timeZone:(NSTimeZone *)timeZone defaultDate:(NSDate *)defaultDate; + +/// Returns a value transformer created by calling +/// `+mtl_dateTransformerWithDateFormat:calendar:locale:timeZone:defaultDate:` +/// with a calendar, locale, time zone and default date of `nil`. ++ (NSValueTransformer *)mtl_dateTransformerWithDateFormat:(NSString *)dateFormat locale:(NSLocale *)locale; + +/// A reversible value transformer to transform between a number and its string +/// representation +/// +/// numberStyle - The number style used by the number formatter +/// +/// Returns a transformer which will map from strings to numbers for forward +/// transformations, and from numbers to strings for reverse transformations. ++ (NSValueTransformer *)mtl_numberTransformerWithNumberStyle:(NSNumberFormatterStyle)numberStyle locale:(NSLocale *)locale; + +/// A reversible value transformer to transform between an object and its string +/// representation +/// +/// formatter - The formatter used to perform the transformation +/// objectClass - The class of object that the formatter operates on +/// +/// Returns a transformer which will map from strings to objects for forward +/// transformations, and from objects to strings for reverse transformations. ++ (NSValueTransformer *)mtl_transformerWithFormatter:(NSFormatter *)formatter forObjectClass:(Class)objectClass; + +/// A value transformer that errors if the transformed value are not of the given +/// class. +/// +/// class - The expected class. This argument must not be nil. +/// +/// Returns a transformer which will return an error if the transformed in value +/// is not a member of class. Otherwise, the value is simply passed through. ++ (NSValueTransformer *)mtl_validatingTransformerForClass:(Class)modelClass; + ++ (NSValueTransformer *)mtl_JSONDictionaryTransformerWithModelClass:(Class)modelClass __attribute__((deprecated("Replaced by +[MTLJSONAdapter dictionaryTransformerWithModelClass:]"))); + ++ (NSValueTransformer *)mtl_JSONArrayTransformerWithModelClass:(Class)modelClass __attribute__((deprecated("Replaced by +[MTLJSONAdapter arrayTransformerWithModelClass:]"))); + +@end diff --git a/Apps/Wikipedia/WMF Framework/Third Party/metamacros.h b/Apps/Wikipedia/WMF Framework/Third Party/metamacros.h new file mode 100644 index 0000000..8ecc1cd --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Third Party/metamacros.h @@ -0,0 +1,667 @@ +/** + * Macros for metaprogramming + * ExtendedC + * + * Copyright (C) 2012 Justin Spahr-Summers + * Released under the MIT license + */ + +#ifndef EXTC_METAMACROS_H +#define EXTC_METAMACROS_H + + +/** + * Executes one or more expressions (which may have a void type, such as a call + * to a function that returns no value) and always returns true. + */ +#define metamacro_exprify(...) \ +((__VA_ARGS__), true) + +/** + * Returns a string representation of VALUE after full macro expansion. + */ +#define metamacro_stringify(VALUE) \ +metamacro_stringify_(VALUE) + +/** + * Returns A and B concatenated after full macro expansion. + */ +#define metamacro_concat(A, B) \ +metamacro_concat_(A, B) + +/** + * Returns the Nth variadic argument (starting from zero). At least + * N + 1 variadic arguments must be given. N must be between zero and twenty, + * inclusive. + */ +#define metamacro_at(N, ...) \ +metamacro_concat(metamacro_at, N)(__VA_ARGS__) + +/** + * Returns the number of arguments (up to twenty) provided to the macro. At + * least one argument must be provided. + * + * Inspired by P99: http://p99.gforge.inria.fr + */ +#define metamacro_argcount(...) \ +metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) + +/** + * Identical to #metamacro_foreach_cxt, except that no CONTEXT argument is + * given. Only the index and current argument will thus be passed to MACRO. + */ +#define metamacro_foreach(MACRO, SEP, ...) \ +metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__) + +/** + * For each consecutive variadic argument (up to twenty), MACRO is passed the + * zero-based index of the current argument, CONTEXT, and then the argument + * itself. The results of adjoining invocations of MACRO are then separated by + * SEP. + * + * Inspired by P99: http://p99.gforge.inria.fr + */ +#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \ +metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) + +/** + * Identical to #metamacro_foreach_cxt. This can be used when the former would + * fail due to recursive macro expansion. + */ +#define metamacro_foreach_cxt_recursive(MACRO, SEP, CONTEXT, ...) \ +metamacro_concat(metamacro_foreach_cxt_recursive, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) + +/** + * In consecutive order, appends each variadic argument (up to twenty) onto + * BASE. The resulting concatenations are then separated by SEP. + * + * This is primarily useful to manipulate a list of macro invocations into instead + * invoking a different, possibly related macro. + */ +#define metamacro_foreach_concat(BASE, SEP, ...) \ +metamacro_foreach_cxt(metamacro_foreach_concat_iter, SEP, BASE, __VA_ARGS__) + +/** + * Iterates COUNT times, each time invoking MACRO with the current index + * (starting at zero) and CONTEXT. The results of adjoining invocations of MACRO + * are then separated by SEP. + * + * COUNT must be an integer between zero and twenty, inclusive. + */ +#define metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT) \ +metamacro_concat(metamacro_for_cxt, COUNT)(MACRO, SEP, CONTEXT) + +/** + * Returns the first argument given. At least one argument must be provided. + * + * This is useful when implementing a variadic macro, where you may have only + * one variadic argument, but no way to retrieve it (for example, because \c ... + * always needs to match at least one argument). + * + * @code + + #define varmacro(...) \ + metamacro_head(__VA_ARGS__) + + * @endcode + */ +#define metamacro_head(...) \ +metamacro_head_(__VA_ARGS__, 0) + +/** + * Returns every argument except the first. At least two arguments must be + * provided. + */ +#define metamacro_tail(...) \ +metamacro_tail_(__VA_ARGS__) + +/** + * Returns the first N (up to twenty) variadic arguments as a new argument list. + * At least N variadic arguments must be provided. + */ +#define metamacro_take(N, ...) \ +metamacro_concat(metamacro_take, N)(__VA_ARGS__) + +/** + * Removes the first N (up to twenty) variadic arguments from the given argument + * list. At least N variadic arguments must be provided. + */ +#define metamacro_drop(N, ...) \ +metamacro_concat(metamacro_drop, N)(__VA_ARGS__) + +/** + * Decrements VAL, which must be a number between zero and twenty, inclusive. + * + * This is primarily useful when dealing with indexes and counts in + * metaprogramming. + */ +#define metamacro_dec(VAL) \ +metamacro_at(VAL, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) + +/** + * Increments VAL, which must be a number between zero and twenty, inclusive. + * + * This is primarily useful when dealing with indexes and counts in + * metaprogramming. + */ +#define metamacro_inc(VAL) \ +metamacro_at(VAL, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) + +/** + * If A is equal to B, the next argument list is expanded; otherwise, the + * argument list after that is expanded. A and B must be numbers between zero + * and twenty, inclusive. Additionally, B must be greater than or equal to A. + * + * @code + + // expands to true + metamacro_if_eq(0, 0)(true)(false) + + // expands to false + metamacro_if_eq(0, 1)(true)(false) + + * @endcode + * + * This is primarily useful when dealing with indexes and counts in + * metaprogramming. + */ +#define metamacro_if_eq(A, B) \ +metamacro_concat(metamacro_if_eq, A)(B) + +/** + * Identical to #metamacro_if_eq. This can be used when the former would fail + * due to recursive macro expansion. + */ +#define metamacro_if_eq_recursive(A, B) \ +metamacro_concat(metamacro_if_eq_recursive, A)(B) + +/** + * Returns 1 if N is an even number, or 0 otherwise. N must be between zero and + * twenty, inclusive. + * + * For the purposes of this test, zero is considered even. + */ +#define metamacro_is_even(N) \ +metamacro_at(N, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1) + +/** + * Returns the logical NOT of B, which must be the number zero or one. + */ +#define metamacro_not(B) \ +metamacro_at(B, 1, 0) + +// IMPLEMENTATION DETAILS FOLLOW! +// Do not write code that depends on anything below this line. +#define metamacro_stringify_(VALUE) # VALUE +#define metamacro_concat_(A, B) A ## B +#define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG) +#define metamacro_head_(FIRST, ...) FIRST +#define metamacro_tail_(FIRST, ...) __VA_ARGS__ +#define metamacro_consume_(...) +#define metamacro_expand_(...) __VA_ARGS__ + +// implemented from scratch so that metamacro_concat() doesn't end up nesting +#define metamacro_foreach_concat_iter(INDEX, BASE, ARG) metamacro_foreach_concat_iter_(BASE, ARG) +#define metamacro_foreach_concat_iter_(BASE, ARG) BASE ## ARG + +// metamacro_at expansions +#define metamacro_at0(...) metamacro_head(__VA_ARGS__) +#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__) +#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__) + +// metamacro_foreach_cxt expansions +#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT) +#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) + +#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ +metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \ +SEP \ +MACRO(1, CONTEXT, _1) + +#define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ +metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ +SEP \ +MACRO(2, CONTEXT, _2) + +#define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ +metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ +SEP \ +MACRO(3, CONTEXT, _3) + +#define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ +metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ +SEP \ +MACRO(4, CONTEXT, _4) + +#define metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ +metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ +SEP \ +MACRO(5, CONTEXT, _5) + +#define metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ +metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ +SEP \ +MACRO(6, CONTEXT, _6) + +#define metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ +metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ +SEP \ +MACRO(7, CONTEXT, _7) + +#define metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ +metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ +SEP \ +MACRO(8, CONTEXT, _8) + +#define metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ +metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ +SEP \ +MACRO(9, CONTEXT, _9) + +#define metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ +metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ +SEP \ +MACRO(10, CONTEXT, _10) + +#define metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ +metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ +SEP \ +MACRO(11, CONTEXT, _11) + +#define metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ +metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ +SEP \ +MACRO(12, CONTEXT, _12) + +#define metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ +metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ +SEP \ +MACRO(13, CONTEXT, _13) + +#define metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ +metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ +SEP \ +MACRO(14, CONTEXT, _14) + +#define metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ +metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ +SEP \ +MACRO(15, CONTEXT, _15) + +#define metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ +metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ +SEP \ +MACRO(16, CONTEXT, _16) + +#define metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ +metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ +SEP \ +MACRO(17, CONTEXT, _17) + +#define metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ +metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ +SEP \ +MACRO(18, CONTEXT, _18) + +#define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ +metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ +SEP \ +MACRO(19, CONTEXT, _19) + +// metamacro_foreach_cxt_recursive expansions +#define metamacro_foreach_cxt_recursive0(MACRO, SEP, CONTEXT) +#define metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) + +#define metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ +metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) \ +SEP \ +MACRO(1, CONTEXT, _1) + +#define metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ +metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ +SEP \ +MACRO(2, CONTEXT, _2) + +#define metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ +metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ +SEP \ +MACRO(3, CONTEXT, _3) + +#define metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ +metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ +SEP \ +MACRO(4, CONTEXT, _4) + +#define metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ +metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ +SEP \ +MACRO(5, CONTEXT, _5) + +#define metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ +metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ +SEP \ +MACRO(6, CONTEXT, _6) + +#define metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ +metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ +SEP \ +MACRO(7, CONTEXT, _7) + +#define metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ +metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ +SEP \ +MACRO(8, CONTEXT, _8) + +#define metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ +metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ +SEP \ +MACRO(9, CONTEXT, _9) + +#define metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ +metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ +SEP \ +MACRO(10, CONTEXT, _10) + +#define metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ +metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ +SEP \ +MACRO(11, CONTEXT, _11) + +#define metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ +metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ +SEP \ +MACRO(12, CONTEXT, _12) + +#define metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ +metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ +SEP \ +MACRO(13, CONTEXT, _13) + +#define metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ +metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ +SEP \ +MACRO(14, CONTEXT, _14) + +#define metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ +metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ +SEP \ +MACRO(15, CONTEXT, _15) + +#define metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ +metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ +SEP \ +MACRO(16, CONTEXT, _16) + +#define metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ +metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ +SEP \ +MACRO(17, CONTEXT, _17) + +#define metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ +metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ +SEP \ +MACRO(18, CONTEXT, _18) + +#define metamacro_foreach_cxt_recursive20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ +metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ +SEP \ +MACRO(19, CONTEXT, _19) + +// metamacro_for_cxt expansions +#define metamacro_for_cxt0(MACRO, SEP, CONTEXT) +#define metamacro_for_cxt1(MACRO, SEP, CONTEXT) MACRO(0, CONTEXT) + +#define metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt1(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(1, CONTEXT) + +#define metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(2, CONTEXT) + +#define metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(3, CONTEXT) + +#define metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(4, CONTEXT) + +#define metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(5, CONTEXT) + +#define metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(6, CONTEXT) + +#define metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(7, CONTEXT) + +#define metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(8, CONTEXT) + +#define metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(9, CONTEXT) + +#define metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(10, CONTEXT) + +#define metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(11, CONTEXT) + +#define metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(12, CONTEXT) + +#define metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(13, CONTEXT) + +#define metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(14, CONTEXT) + +#define metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(15, CONTEXT) + +#define metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(16, CONTEXT) + +#define metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(17, CONTEXT) + +#define metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(18, CONTEXT) + +#define metamacro_for_cxt20(MACRO, SEP, CONTEXT) \ +metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ +SEP \ +MACRO(19, CONTEXT) + +// metamacro_if_eq expansions +#define metamacro_if_eq0(VALUE) \ +metamacro_concat(metamacro_if_eq0_, VALUE) + +#define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_ +#define metamacro_if_eq0_1(...) metamacro_expand_ +#define metamacro_if_eq0_2(...) metamacro_expand_ +#define metamacro_if_eq0_3(...) metamacro_expand_ +#define metamacro_if_eq0_4(...) metamacro_expand_ +#define metamacro_if_eq0_5(...) metamacro_expand_ +#define metamacro_if_eq0_6(...) metamacro_expand_ +#define metamacro_if_eq0_7(...) metamacro_expand_ +#define metamacro_if_eq0_8(...) metamacro_expand_ +#define metamacro_if_eq0_9(...) metamacro_expand_ +#define metamacro_if_eq0_10(...) metamacro_expand_ +#define metamacro_if_eq0_11(...) metamacro_expand_ +#define metamacro_if_eq0_12(...) metamacro_expand_ +#define metamacro_if_eq0_13(...) metamacro_expand_ +#define metamacro_if_eq0_14(...) metamacro_expand_ +#define metamacro_if_eq0_15(...) metamacro_expand_ +#define metamacro_if_eq0_16(...) metamacro_expand_ +#define metamacro_if_eq0_17(...) metamacro_expand_ +#define metamacro_if_eq0_18(...) metamacro_expand_ +#define metamacro_if_eq0_19(...) metamacro_expand_ +#define metamacro_if_eq0_20(...) metamacro_expand_ + +#define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE)) +#define metamacro_if_eq2(VALUE) metamacro_if_eq1(metamacro_dec(VALUE)) +#define metamacro_if_eq3(VALUE) metamacro_if_eq2(metamacro_dec(VALUE)) +#define metamacro_if_eq4(VALUE) metamacro_if_eq3(metamacro_dec(VALUE)) +#define metamacro_if_eq5(VALUE) metamacro_if_eq4(metamacro_dec(VALUE)) +#define metamacro_if_eq6(VALUE) metamacro_if_eq5(metamacro_dec(VALUE)) +#define metamacro_if_eq7(VALUE) metamacro_if_eq6(metamacro_dec(VALUE)) +#define metamacro_if_eq8(VALUE) metamacro_if_eq7(metamacro_dec(VALUE)) +#define metamacro_if_eq9(VALUE) metamacro_if_eq8(metamacro_dec(VALUE)) +#define metamacro_if_eq10(VALUE) metamacro_if_eq9(metamacro_dec(VALUE)) +#define metamacro_if_eq11(VALUE) metamacro_if_eq10(metamacro_dec(VALUE)) +#define metamacro_if_eq12(VALUE) metamacro_if_eq11(metamacro_dec(VALUE)) +#define metamacro_if_eq13(VALUE) metamacro_if_eq12(metamacro_dec(VALUE)) +#define metamacro_if_eq14(VALUE) metamacro_if_eq13(metamacro_dec(VALUE)) +#define metamacro_if_eq15(VALUE) metamacro_if_eq14(metamacro_dec(VALUE)) +#define metamacro_if_eq16(VALUE) metamacro_if_eq15(metamacro_dec(VALUE)) +#define metamacro_if_eq17(VALUE) metamacro_if_eq16(metamacro_dec(VALUE)) +#define metamacro_if_eq18(VALUE) metamacro_if_eq17(metamacro_dec(VALUE)) +#define metamacro_if_eq19(VALUE) metamacro_if_eq18(metamacro_dec(VALUE)) +#define metamacro_if_eq20(VALUE) metamacro_if_eq19(metamacro_dec(VALUE)) + +// metamacro_if_eq_recursive expansions +#define metamacro_if_eq_recursive0(VALUE) \ +metamacro_concat(metamacro_if_eq_recursive0_, VALUE) + +#define metamacro_if_eq_recursive0_0(...) __VA_ARGS__ metamacro_consume_ +#define metamacro_if_eq_recursive0_1(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_2(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_3(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_4(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_5(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_6(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_7(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_8(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_9(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_10(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_11(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_12(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_13(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_14(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_15(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_16(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_17(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_18(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_19(...) metamacro_expand_ +#define metamacro_if_eq_recursive0_20(...) metamacro_expand_ + +#define metamacro_if_eq_recursive1(VALUE) metamacro_if_eq_recursive0(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive2(VALUE) metamacro_if_eq_recursive1(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive3(VALUE) metamacro_if_eq_recursive2(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive4(VALUE) metamacro_if_eq_recursive3(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive5(VALUE) metamacro_if_eq_recursive4(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive6(VALUE) metamacro_if_eq_recursive5(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive7(VALUE) metamacro_if_eq_recursive6(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive8(VALUE) metamacro_if_eq_recursive7(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive9(VALUE) metamacro_if_eq_recursive8(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive10(VALUE) metamacro_if_eq_recursive9(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive11(VALUE) metamacro_if_eq_recursive10(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive12(VALUE) metamacro_if_eq_recursive11(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive13(VALUE) metamacro_if_eq_recursive12(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive14(VALUE) metamacro_if_eq_recursive13(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive15(VALUE) metamacro_if_eq_recursive14(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive16(VALUE) metamacro_if_eq_recursive15(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive17(VALUE) metamacro_if_eq_recursive16(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive18(VALUE) metamacro_if_eq_recursive17(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive19(VALUE) metamacro_if_eq_recursive18(metamacro_dec(VALUE)) +#define metamacro_if_eq_recursive20(VALUE) metamacro_if_eq_recursive19(metamacro_dec(VALUE)) + +// metamacro_take expansions +#define metamacro_take0(...) +#define metamacro_take1(...) metamacro_head(__VA_ARGS__) +#define metamacro_take2(...) metamacro_head(__VA_ARGS__), metamacro_take1(metamacro_tail(__VA_ARGS__)) +#define metamacro_take3(...) metamacro_head(__VA_ARGS__), metamacro_take2(metamacro_tail(__VA_ARGS__)) +#define metamacro_take4(...) metamacro_head(__VA_ARGS__), metamacro_take3(metamacro_tail(__VA_ARGS__)) +#define metamacro_take5(...) metamacro_head(__VA_ARGS__), metamacro_take4(metamacro_tail(__VA_ARGS__)) +#define metamacro_take6(...) metamacro_head(__VA_ARGS__), metamacro_take5(metamacro_tail(__VA_ARGS__)) +#define metamacro_take7(...) metamacro_head(__VA_ARGS__), metamacro_take6(metamacro_tail(__VA_ARGS__)) +#define metamacro_take8(...) metamacro_head(__VA_ARGS__), metamacro_take7(metamacro_tail(__VA_ARGS__)) +#define metamacro_take9(...) metamacro_head(__VA_ARGS__), metamacro_take8(metamacro_tail(__VA_ARGS__)) +#define metamacro_take10(...) metamacro_head(__VA_ARGS__), metamacro_take9(metamacro_tail(__VA_ARGS__)) +#define metamacro_take11(...) metamacro_head(__VA_ARGS__), metamacro_take10(metamacro_tail(__VA_ARGS__)) +#define metamacro_take12(...) metamacro_head(__VA_ARGS__), metamacro_take11(metamacro_tail(__VA_ARGS__)) +#define metamacro_take13(...) metamacro_head(__VA_ARGS__), metamacro_take12(metamacro_tail(__VA_ARGS__)) +#define metamacro_take14(...) metamacro_head(__VA_ARGS__), metamacro_take13(metamacro_tail(__VA_ARGS__)) +#define metamacro_take15(...) metamacro_head(__VA_ARGS__), metamacro_take14(metamacro_tail(__VA_ARGS__)) +#define metamacro_take16(...) metamacro_head(__VA_ARGS__), metamacro_take15(metamacro_tail(__VA_ARGS__)) +#define metamacro_take17(...) metamacro_head(__VA_ARGS__), metamacro_take16(metamacro_tail(__VA_ARGS__)) +#define metamacro_take18(...) metamacro_head(__VA_ARGS__), metamacro_take17(metamacro_tail(__VA_ARGS__)) +#define metamacro_take19(...) metamacro_head(__VA_ARGS__), metamacro_take18(metamacro_tail(__VA_ARGS__)) +#define metamacro_take20(...) metamacro_head(__VA_ARGS__), metamacro_take19(metamacro_tail(__VA_ARGS__)) + +// metamacro_drop expansions +#define metamacro_drop0(...) __VA_ARGS__ +#define metamacro_drop1(...) metamacro_tail(__VA_ARGS__) +#define metamacro_drop2(...) metamacro_drop1(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop3(...) metamacro_drop2(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop4(...) metamacro_drop3(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop5(...) metamacro_drop4(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop6(...) metamacro_drop5(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop7(...) metamacro_drop6(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop8(...) metamacro_drop7(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop9(...) metamacro_drop8(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop10(...) metamacro_drop9(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop11(...) metamacro_drop10(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop12(...) metamacro_drop11(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop13(...) metamacro_drop12(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop14(...) metamacro_drop13(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop15(...) metamacro_drop14(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop16(...) metamacro_drop15(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop17(...) metamacro_drop16(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop18(...) metamacro_drop17(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop19(...) metamacro_drop18(metamacro_tail(__VA_ARGS__)) +#define metamacro_drop20(...) metamacro_drop19(metamacro_tail(__VA_ARGS__)) + +#endif diff --git a/Apps/Wikipedia/WMF Framework/TimeInterval+Extensions.swift b/Apps/Wikipedia/WMF Framework/TimeInterval+Extensions.swift new file mode 100644 index 0000000..2794720 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/TimeInterval+Extensions.swift @@ -0,0 +1,19 @@ +import Foundation + +public extension TimeInterval { + static var oneMinute: TimeInterval { + return TimeInterval(60) + } + + static var tenMinutes: TimeInterval { + return oneMinute * 10 + } + + static var oneHour: TimeInterval { + return TimeInterval(oneMinute * 60) + } + + static var oneDay: TimeInterval { + return TimeInterval(oneHour * 24) + } +} diff --git a/Apps/Wikipedia/WMF Framework/TimelineView.swift b/Apps/Wikipedia/WMF Framework/TimelineView.swift new file mode 100644 index 0000000..7593e61 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/TimelineView.swift @@ -0,0 +1,301 @@ +import UIKit + +public class TimelineView: UIView { + public enum Decoration { + case doubleDot, singleDot, squiggle + } + + public override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + open func setup() { + } + + public var decoration: Decoration = .doubleDot { + didSet { + guard oldValue != decoration else { + return + } + + switch decoration { + case .squiggle: + innerDotShapeLayer.removeFromSuperlayer() + outerDotShapeLayer.removeFromSuperlayer() + layer.addSublayer(squiggleShapeLayer) + updateSquiggleCenterPoint() + case .doubleDot: + squiggleShapeLayer.removeFromSuperlayer() + layer.addSublayer(innerDotShapeLayer) + layer.addSublayer(outerDotShapeLayer) + case .singleDot: + squiggleShapeLayer.removeFromSuperlayer() + layer.addSublayer(innerDotShapeLayer) + } + + setNeedsDisplay() + } + } + public var shouldAnimateDots: Bool = false + public var minimizeUnanimatedDots: Bool = false + public var timelineColor: UIColor? = nil { + didSet { + refreshColors() + } + } + private var color: CGColor { + return timelineColor?.cgColor ?? tintColor.cgColor + } + + public var verticalLineWidth: CGFloat = 1.0 { + didSet { + squiggleShapeLayer.lineWidth = verticalLineWidth + setNeedsDisplay() + } + } + + public var pauseDotsAnimation: Bool = true { + didSet { + displayLink?.isPaused = pauseDotsAnimation + } + } + + private var dotRadius: CGFloat { + switch decoration { + case .singleDot: return 7.0 + default: return 9.0 + } + } + private let dotMinRadiusNormal: CGFloat = 0.4 + + // At a height of less than 30, (due to rounding) the squiggle's curves don't perfectly align with the straight lines. + private let squiggleHeight: CGFloat = 30.0 + + public var dotsY: CGFloat = 0 { + didSet { + guard shouldAnimateDots == false || decoration == .squiggle else { + return + } + + switch decoration { + case .doubleDot, .singleDot: updateDotsRadii(to: minimizeUnanimatedDots ? 0.0 : 1.0, at: CGPoint(x: bounds.midX, y: dotsY)) + case .squiggle: updateSquiggleCenterPoint() + } + setNeedsDisplay() + } + } + + override public func tintColorDidChange() { + super.tintColorDidChange() + refreshColors() + } + + override public var backgroundColor: UIColor? { + didSet { + outerDotShapeLayer.fillColor = backgroundColor?.cgColor + squiggleShapeLayer.fillColor = backgroundColor?.cgColor + } + } + + private lazy var outerDotShapeLayer: CAShapeLayer = { + let shape = CAShapeLayer() + shape.fillColor = backgroundColor?.cgColor ?? UIColor.white.cgColor + shape.strokeColor = color + shape.lineWidth = 1.0 + if decoration == .doubleDot { + self.layer.addSublayer(shape) + } + return shape + }() + + private lazy var innerDotShapeLayer: CAShapeLayer = { + let shape = CAShapeLayer() + shape.fillColor = color + shape.strokeColor = color + shape.lineWidth = 1.0 + if decoration == .doubleDot { + self.layer.addSublayer(shape) + } + return shape + }() + + private lazy var squiggleShapeLayer: CAShapeLayer = { + let shape = CAShapeLayer() + shape.updateSquiggleLocation(height: squiggleHeight, decorationMidY: dotsY, midX: bounds.midX) + shape.strokeColor = color + shape.fillColor = backgroundColor?.cgColor ?? UIColor.white.cgColor + shape.lineWidth = verticalLineWidth + if decoration == .squiggle { + self.layer.addSublayer(shape) + } + return shape + }() + + private lazy var displayLink: CADisplayLink? = { + guard decoration == .doubleDot, shouldAnimateDots == true else { + return nil + } + let link = CADisplayLink(target: self, selector: #selector(maybeUpdateDotsRadii)) + link.add(to: RunLoop.main, forMode: RunLoop.Mode.common) + return link + }() + + override public func removeFromSuperview() { + displayLink?.invalidate() + displayLink = nil + super.removeFromSuperview() + } + + override public func draw(_ rect: CGRect) { + super.draw(rect) + guard let context = UIGraphicsGetCurrentContext() else { + return + } + drawVerticalLine(in: context, rect: rect) + } + + public var extendTimelineAboveDot: Bool = true { + didSet { + if oldValue != extendTimelineAboveDot { + setNeedsDisplay() + } + } + } + + private func drawVerticalLine(in context: CGContext, rect: CGRect) { + context.setLineWidth(verticalLineWidth) + context.setStrokeColor(color) + let lineTopY = extendTimelineAboveDot ? rect.minY : dotsY + + switch decoration { + case .doubleDot, .singleDot: + context.move(to: CGPoint(x: rect.midX, y: lineTopY)) + context.addLine(to: CGPoint(x: rect.midX, y: rect.maxY)) + case .squiggle: + if extendTimelineAboveDot { + context.move(to: CGPoint(x: rect.midX, y: lineTopY)) + context.addLine(to: CGPoint(x: rect.midX, y: dotsY-squiggleHeight/2)) + } + context.move(to: CGPoint(x: rect.midX, y: dotsY+squiggleHeight/2)) + context.addLine(to: CGPoint(x: rect.midX, y: rect.maxY)) + } + + context.strokePath() + } + + private func refreshColors() { + outerDotShapeLayer.strokeColor = color + innerDotShapeLayer.fillColor = color + innerDotShapeLayer.strokeColor = color + squiggleShapeLayer.strokeColor = color + setNeedsDisplay() + } + + // Returns CGFloat in range from 0.0 to 1.0. 0.0 indicates dot should be minimized. + // 1.0 indicates dot should be maximized. Approaches 1.0 as timelineView.dotY + // approaches vertical center. Approaches 0.0 as timelineView.dotY approaches top + // or bottom. + private func dotRadiusNormal(with y:CGFloat, in container:UIView) -> CGFloat { + let yInContainer = convert(CGPoint(x:0, y:y), to: container).y + let halfContainerHeight = container.bounds.size.height * 0.5 + return max(0.0, 1.0 - (abs(yInContainer - halfContainerHeight) / halfContainerHeight)) + } + + private var lastDotRadiusNormal: CGFloat = -1.0 // -1.0 so dots with dotAnimationNormal of "0.0" are visible initially + + @objc private func maybeUpdateDotsRadii() { + guard let containerView = window else { + return + } + + // Shift the "full-width dot" point up a bit - otherwise it's in the vertical center of screen. + let yOffset = containerView.bounds.size.height * 0.15 + + var radiusNormal = dotRadiusNormal(with: dotsY + yOffset, in: containerView) + + // Reminder: can reduce precision to 1 (significant digit) to reduce how often dot radii are updated. + let precision: CGFloat = 2 + let roundingNumber = pow(10, precision) + radiusNormal = (radiusNormal * roundingNumber).rounded(.up) / roundingNumber + + guard radiusNormal != lastDotRadiusNormal else { + return + } + + updateDotsRadii(to: radiusNormal, at: CGPoint(x: bounds.midX, y: dotsY)) + + // Progressively fade the inner dot when it gets tiny. + innerDotShapeLayer.opacity = easeInOutQuart(number: Float(radiusNormal)) + + lastDotRadiusNormal = radiusNormal + } + + private func updateDotsRadii(to radiusNormal: CGFloat, at center: CGPoint) { + outerDotShapeLayer.updateDotRadius(dotRadius * max(radiusNormal, dotMinRadiusNormal), center: center) + innerDotShapeLayer.updateDotRadius(dotRadius * max((radiusNormal - dotMinRadiusNormal), 0.0), center: center) + } + + private func updateSquiggleCenterPoint() { + squiggleShapeLayer.updateSquiggleLocation(height: squiggleHeight, decorationMidY: dotsY, midX: bounds.midX) + } + + private func easeInOutQuart(number:Float) -> Float { + return number < 0.5 ? 8.0 * pow(number, 4) : 1.0 - 8.0 * (number - 1.0) * pow(number, 3) + } +} + +extension CAShapeLayer { + fileprivate func updateDotRadius(_ radius: CGFloat, center: CGPoint) { + path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0.0, endAngle:CGFloat.pi * 2.0, clockwise: true).cgPath + } + + fileprivate func updateSquiggleLocation(height: CGFloat, decorationMidY: CGFloat, midX: CGFloat) { + let startY = decorationMidY - height/2 // squiggle's middle (not top) should be startY + let topPoint = CGPoint(x: midX, y: startY) + let quarterOnePoint = CGPoint(x: midX, y: startY + (height*1/4)) + let midPoint = CGPoint(x: midX, y: startY + (height*2/4)) + let quarterThreePoint = CGPoint(x: midX, y: startY + (height*3/4)) + let bottomPoint = CGPoint(x: midX, y: startY + height) + + /// Math for curves shown/explained on Phab ticket: https://phabricator.wikimedia.org/T258209#6363389 + let eighthOfHeight = height/8 + let circleDiameter = sqrt(2*(eighthOfHeight*eighthOfHeight)) + let radius = circleDiameter/2 + + /// Without this adjustment, the `arcCenter`s are not the true center of circle and the squiggle has some jagged edges. + let centerAdjustedRadius = radius - 1 + + let arc1Start = CGPoint(x: midX - radius*3, y: topPoint.y + radius*3) + let arc1Center = CGPoint(x: arc1Start.x + centerAdjustedRadius, y: arc1Start.y + centerAdjustedRadius) + + let arc2Start = CGPoint(x: midX + radius*1, y: quarterOnePoint.y - radius*1) + let arc2Center = CGPoint(x: arc2Start.x + centerAdjustedRadius, y: arc2Start.y + centerAdjustedRadius) + + let arc3Start = CGPoint(x: midX - radius*3, y: midPoint.y + radius*3) + let arc3Center = CGPoint(x: arc3Start.x + centerAdjustedRadius, y: arc3Start.y + centerAdjustedRadius) + + let arc4Start = CGPoint(x: midX + radius*1, y: quarterThreePoint.y - radius*1) + let arc4Center = CGPoint(x: arc4Start.x + centerAdjustedRadius, y: arc4Start.y + centerAdjustedRadius) + + let squiggle = UIBezierPath() + let fullCircle = 2 * CGFloat.pi // addArc's angles are in radians, let's make it easier + squiggle.move(to: topPoint) + squiggle.addLine(to: arc1Start) + squiggle.addArc(withCenter: arc1Center, radius: radius, startAngle: fullCircle * 5/8, endAngle: fullCircle * 1/8, clockwise: false) + squiggle.addLine(to: arc2Start) + squiggle.addArc(withCenter: arc2Center, radius: radius, startAngle: fullCircle * 5/8, endAngle: fullCircle * 1/8, clockwise: true) + squiggle.addLine(to: arc3Start) + squiggle.addArc(withCenter: arc3Center, radius: radius, startAngle: fullCircle * 5/8, endAngle: fullCircle * 1/8, clockwise: false) + squiggle.addLine(to: arc4Start) + squiggle.addArc(withCenter: arc4Center, radius: radius, startAngle: fullCircle * 5/8, endAngle: fullCircle * 1/8, clockwise: true) + squiggle.addLine(to: bottomPoint) + + path = squiggle.cgPath + } +} diff --git a/Apps/Wikipedia/WMF Framework/UIFont+WMFDynamicType.swift b/Apps/Wikipedia/WMF Framework/UIFont+WMFDynamicType.swift new file mode 100644 index 0000000..771f1ba --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/UIFont+WMFDynamicType.swift @@ -0,0 +1,152 @@ +import UIKit + +@objc(WMFFontFamily) public enum FontFamily: Int { + case system + case georgia +} + +@objc (WMFDynamicTextStyle) public class DynamicTextStyle: NSObject { + @objc public static let subheadline = DynamicTextStyle(.system, .subheadline) + @objc public static let semiboldSubheadline = DynamicTextStyle(.system, .subheadline, .semibold) + public static let mediumSubheadline = DynamicTextStyle(.system, .subheadline, .medium) + public static let boldSubheadline = DynamicTextStyle(.system, .subheadline, .bold) + public static let italicSubheadline = DynamicTextStyle(.system, .subheadline, .regular, [UIFontDescriptor.SymbolicTraits.traitItalic]) + + public static let headline = DynamicTextStyle(.system, .headline) + public static let mediumHeadline = DynamicTextStyle(.system, .headline, .medium) + public static let semiboldHeadline = DynamicTextStyle(.system, .headline, .semibold) + public static let boldHeadline = DynamicTextStyle(.system, .headline, .bold) + public static let heavyHeadline = DynamicTextStyle(.system, .headline, .heavy) + + public static let footnote = DynamicTextStyle(.system, .footnote) + public static let mediumFootnote = DynamicTextStyle(.system, .footnote, .medium) + @objc public static let semiboldFootnote = DynamicTextStyle(.system, .footnote, .semibold) + public static let italicFootnote = DynamicTextStyle(.system, .footnote, .regular, [UIFontDescriptor.SymbolicTraits.traitItalic]) + public static let boldFootnote = DynamicTextStyle(.system, .footnote, .bold) + + public static let boldTitle1 = DynamicTextStyle(.system, .title1, .bold) + public static let mediumTitle1 = DynamicTextStyle(.system, .title1, .medium) + public static let heavyTitle1 = DynamicTextStyle(.system, .title1, .heavy) + + public static let boldTitle2 = DynamicTextStyle(.system, .title2, .bold) + public static let semiboldTitle3 = DynamicTextStyle(.system, .title3, .bold) + + public static let callout = DynamicTextStyle(.system, .callout) + public static let semiboldCallout = DynamicTextStyle(.system, .callout, .semibold) + public static let boldCallout = DynamicTextStyle(.system, .callout, .bold) + public static let italicCallout = DynamicTextStyle(.system, .callout, .regular, [UIFontDescriptor.SymbolicTraits.traitItalic]) + + public static let title2 = DynamicTextStyle(.system, .title2) + public static let title3 = DynamicTextStyle(.system, .title3) + + public static let body = DynamicTextStyle(.system, .body) + @objc public static let semiboldBody = DynamicTextStyle(.system, .body, .semibold) + public static let mediumBody = DynamicTextStyle(.system, .body, .medium) + public static let italicBody = DynamicTextStyle(.system, .body, .regular, [UIFontDescriptor.SymbolicTraits.traitItalic]) + + public static let caption1 = DynamicTextStyle(.system, .caption1) + public static let mediumCaption1 = DynamicTextStyle(.system, .caption1, .medium) + public static let caption2 = DynamicTextStyle(.system, .caption2) + public static let semiboldCaption2 = DynamicTextStyle(.system, .caption2, .semibold) + public static let mediumCaption2 = DynamicTextStyle(.system, .caption2, .medium) + public static let italicCaption2 = DynamicTextStyle(.system, .caption2, .regular, [UIFontDescriptor.SymbolicTraits.traitItalic]) + public static let italicCaption1 = DynamicTextStyle(.system, .caption1, .regular, [UIFontDescriptor.SymbolicTraits.traitItalic]) + + public static let georgiaTitle3 = DynamicTextStyle(.georgia, .title3) + + let family: FontFamily + let style: UIFont.TextStyle + let weight: UIFont.Weight + let traits: UIFontDescriptor.SymbolicTraits + + init(_ family: FontFamily = .system, _ style: UIFont.TextStyle, _ weight: UIFont.Weight = .regular, _ traits: UIFontDescriptor.SymbolicTraits = []) { + self.family = family + self.weight = weight + self.traits = traits + self.style = style + } + + func with(weight: UIFont.Weight) -> DynamicTextStyle { + return DynamicTextStyle(family, style, weight, traits) + } + + func with(traits: UIFontDescriptor.SymbolicTraits) -> DynamicTextStyle { + return DynamicTextStyle(family, style, weight, traits) + } + + func with(weight: UIFont.Weight, traits: UIFontDescriptor.SymbolicTraits) -> DynamicTextStyle { + return DynamicTextStyle(family, style, weight, traits) + } +} + +public extension UITraitCollection { + var wmf_preferredContentSizeCategory: UIContentSizeCategory { + return preferredContentSizeCategory + } +} + +fileprivate var fontCache: [String: UIFont] = [:] + +public extension UIFont { + + @objc(wmf_fontForDynamicTextStyle:) class func wmf_font(_ dynamicTextStyle: DynamicTextStyle) -> UIFont { + return UIFont.wmf_font(dynamicTextStyle, compatibleWithTraitCollection: UITraitCollection(preferredContentSizeCategory: .large)) + } + + class func wmf_scaledSystemFont(forTextStyle style: UIFont.TextStyle, weight: UIFont.Weight, size: CGFloat) -> UIFont { + return UIFontMetrics(forTextStyle: style).scaledFont(for: UIFont.systemFont(ofSize: size, weight: weight)) + } + + class func wmf_scaledSystemFont(forTextStyle style: UIFont.TextStyle, weight: UIFont.Weight, size: CGFloat, maximumPointSize: CGFloat) -> UIFont { + return UIFontMetrics(forTextStyle: style).scaledFont(for: UIFont.systemFont(ofSize: size, weight: weight), maximumPointSize: maximumPointSize) + } + + @objc(wmf_fontForDynamicTextStyle:compatibleWithTraitCollection:) class func wmf_font(_ dynamicTextStyle: DynamicTextStyle, compatibleWithTraitCollection traitCollection: UITraitCollection) -> UIFont { + let fontFamily = dynamicTextStyle.family + let weight = dynamicTextStyle.weight + let traits = dynamicTextStyle.traits + let style = dynamicTextStyle.style + guard fontFamily != .system || weight != .regular || traits != [] else { + return UIFont.preferredFont(forTextStyle: style, compatibleWith: traitCollection) + } + + let cacheKey = "\(fontFamily.rawValue)-\(weight.rawValue)-\(traits.rawValue)-\(style.rawValue)-\(traitCollection.preferredContentSizeCategory.rawValue)" + if let font = fontCache[cacheKey] { + return font + } + + let size: CGFloat = UIFont.preferredFont(forTextStyle: style, compatibleWith: traitCollection).pointSize + + var font: UIFont + switch fontFamily { + case .georgia: + // using the standard .with(traits: doesn't seem to work for georgia + let isBold = weight > UIFont.Weight.regular + let isItalic = traits.contains(.traitItalic) + if isBold && isItalic { + font = UIFont(descriptor: UIFontDescriptor(name: "Georgia-BoldItalic", size: size), size: 0) + } else if isBold { + font = UIFont(descriptor: UIFontDescriptor(name: "Georgia-Bold", size: size), size: 0) + } else if isItalic { + font = UIFont(descriptor: UIFontDescriptor(name: "Georgia-Italic", size: size), size: 0) + } else { + font = UIFont(descriptor: UIFontDescriptor(name: "Georgia", size: size), size: 0) + } + case .system: + font = weight != .regular ? UIFont.systemFont(ofSize: size, weight: weight) : UIFont.preferredFont(forTextStyle: style, compatibleWith: traitCollection) + if traits != [] { + font = font.with(traits: traits) + } + } + fontCache[cacheKey] = font + return font + } + + func with(traits: UIFontDescriptor.SymbolicTraits) -> UIFont { + guard let descriptor = self.fontDescriptor.withSymbolicTraits(traits) else { + return self + } + + return UIFont(descriptor: descriptor, size: 0) + } +} diff --git a/Apps/Wikipedia/WMF Framework/UIScrollView+Limits.swift b/Apps/Wikipedia/WMF Framework/UIScrollView+Limits.swift new file mode 100644 index 0000000..23d7824 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/UIScrollView+Limits.swift @@ -0,0 +1,74 @@ +import UIKit + +extension UIScrollView { + private var topOffsetY: CGFloat { + return 0 - adjustedContentInset.top + } + + public var bottomOffsetY: CGFloat { + return contentSize.height - bounds.size.height + adjustedContentInset.bottom + } + + private var topOffset: CGPoint { + return CGPoint(x: contentOffset.x, y: topOffsetY) + } + + private var bottomOffset: CGPoint { + return CGPoint(x: contentOffset.x, y: bottomOffsetY) + } + + public var isAtTop: Bool { + // Rounded: Sometimes when we expect them to be equal, these are less than .2 different (due to rounding in earleir calculation) - and with multiple layout passes, it caused a large scrolling bug on a VC's launch. + return contentOffset.y.rounded(.up) <= topOffsetY.rounded(.up) + } + + private var isAtBottom: Bool { + // Rounded: Sometimes when we expect them to be equal, these are less than .2 different (due to rounding in earleir calculation) - and with multiple layout passes, it caused a large scrolling bug on a VC's launch. + return contentOffset.y.rounded(.up) >= bottomOffsetY.rounded(.up) + } + + public var verticalOffsetPercentage: CGFloat { + get { + let height = contentSize.height + guard height > 0 else { + return 0 + } + return contentOffset.y / height + } + set { + let newOffsetY = contentSize.height * newValue + setContentOffset(CGPoint(x: contentOffset.x, y: newOffsetY), animated: false) + } + } + + @objc(wmf_setContentInset:verticalScrollIndicatorInsets:preserveContentOffset:preserveAnimation:) + public func setContentInset(_ updatedContentInset: UIEdgeInsets, verticalScrollIndicatorInsets updatedVerticalScrollIndicatorInsets: UIEdgeInsets, preserveContentOffset: Bool = true, preserveAnimation: Bool = false) -> Bool { + guard updatedContentInset != contentInset || updatedVerticalScrollIndicatorInsets != verticalScrollIndicatorInsets else { + return false + } + let wasAtTop = isAtTop + let wasAtBottom = isAtBottom + verticalScrollIndicatorInsets = updatedVerticalScrollIndicatorInsets + + if preserveAnimation { + contentInset = updatedContentInset + } else { + let wereAnimationsEnabled = UIView.areAnimationsEnabled + UIView.setAnimationsEnabled(false) + contentInset = updatedContentInset + UIView.setAnimationsEnabled(wereAnimationsEnabled) + } + + guard preserveContentOffset else { + return true + } + + if wasAtTop { + contentOffset = topOffset + } else if contentSize.height > bounds.inset(by: adjustedContentInset).height && wasAtBottom { + contentOffset = bottomOffset + } + + return true + } +} diff --git a/Apps/Wikipedia/WMF Framework/UIStackView+SubviewVerification.swift b/Apps/Wikipedia/WMF Framework/UIStackView+SubviewVerification.swift new file mode 100644 index 0000000..46615ac --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/UIStackView+SubviewVerification.swift @@ -0,0 +1,27 @@ +public extension UIView { + func wmf_hasRequiredNonZeroHeightConstraint() -> Bool { + let requiredHeightConstraint = constraints.first(where: {constraint in + guard + type(of: constraint) == NSLayoutConstraint.self, + constraint.firstAttribute == .height, + constraint.priority == UILayoutPriority.required, + constraint.constant != 0 + else { + return false + } + return true + }) + return (requiredHeightConstraint != nil) + } +} + +public extension UIStackView { + func wmf_firstArrangedSubviewWithRequiredNonZeroHeightConstraint() -> UIView? { + return arrangedSubviews.first(where: { arrangedSubview in + return arrangedSubview.wmf_hasRequiredNonZeroHeightConstraint() + }) + } + func wmf_anArrangedSubviewHasRequiredNonZeroHeightConstraintAssertString() -> String { + return "\n\nAll stackview arrangedSubview height constraints need to have a priority of < 1000 so the stackview can collapse the 'cell' if the arrangedSubview's isHidden property is set to true. This arrangedSubview was determined to have a required height: \(String(describing: wmf_firstArrangedSubviewWithRequiredNonZeroHeightConstraint())). To fix reduce the priority of its height constraint to < 1000.\n\n" + } +} diff --git a/Apps/Wikipedia/WMF Framework/UIView+Constraints.swift b/Apps/Wikipedia/WMF Framework/UIView+Constraints.swift new file mode 100644 index 0000000..831a7d9 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/UIView+Constraints.swift @@ -0,0 +1,39 @@ +import UIKit + +@objc public extension UIView { + func addCenteredSubview(_ subview: UIView) { + addSubview(subview) + subview.translatesAutoresizingMaskIntoConstraints = false + let centerXConstraint = centerXAnchor.constraint(equalTo: subview.centerXAnchor) + let centerYConstraint = centerYAnchor.constraint(equalTo: subview.centerYAnchor) + NSLayoutConstraint.activate([centerXConstraint, centerYConstraint]) + } + + @objc func wmf_addSubviewWithConstraintsToEdges(_ subview: UIView) { + wmf_addSubview(subview, withConstraintsToEdgesWithInsets: .zero) + } + + @objc func wmf_addSubview(_ subview: UIView, withConstraintsToEdgesWithInsets insets: UIEdgeInsets, priority: UILayoutPriority = .required, belowSubview: UIView? = nil) { + if let belowSubview = belowSubview { + insertSubview(subview, belowSubview: belowSubview) + } else { + addSubview(subview) + } + wmf_addConstraintsToEdgesOfView(subview, withInsets: insets, priority: priority) + } + + // Until we drop iOS 10 and can use NSDirectionalEdgeInsets, assume insets.left == leading & insets.right == trailing + @objc func wmf_addConstraintsToEdgesOfView(_ subview: UIView, withInsets insets: UIEdgeInsets = .zero, priority: UILayoutPriority = .required) { + subview.translatesAutoresizingMaskIntoConstraints = false + subview.frame = bounds.inset(by: insets) + let topConstraint = subview.topAnchor.constraint(equalTo: topAnchor, constant: insets.top) + topConstraint.priority = priority + let bottomConstraint = bottomAnchor.constraint(equalTo: subview.bottomAnchor, constant: insets.bottom) + bottomConstraint.priority = priority + let leftConstraint = subview.leadingAnchor.constraint(equalTo: leadingAnchor, constant: insets.left) + leftConstraint.priority = priority + let rightConstraint = trailingAnchor.constraint(equalTo: subview.trailingAnchor, constant: insets.right) + rightConstraint.priority = priority + addConstraints([topConstraint, bottomConstraint, leftConstraint, rightConstraint]) + } +} diff --git a/Apps/Wikipedia/WMF Framework/UIView+SemanticContent.swift b/Apps/Wikipedia/WMF Framework/UIView+SemanticContent.swift new file mode 100644 index 0000000..bc18527 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/UIView+SemanticContent.swift @@ -0,0 +1,162 @@ +@objc public enum HorizontalAlignment : Int { + case center + case left + case right +} + +@objc public enum VerticalAlignment: Int { + case center + case top + case bottom +} + +public let NoIntrinsicSize = CGSize(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric) + +extension UIView { + @objc public func wmf_sizeThatFits(_ size: CGSize) -> CGSize { + return sizeThatFits(size) + } + + @objc @discardableResult public func wmf_preferredFrame(at point: CGPoint, maximumSize: CGSize = NoIntrinsicSize, minimumSize: CGSize = NoIntrinsicSize, horizontalAlignment: HorizontalAlignment = .center, verticalAlignment: VerticalAlignment = .center, apply: Bool = false) -> CGRect { + let viewSize: CGSize = wmf_sizeThatFits(maximumSize) + + var x: CGFloat = point.x + var y: CGFloat = point.y + + let viewWidth: CGFloat + let widthToFit: CGFloat + + if minimumSize.width != UIView.noIntrinsicMetric && maximumSize.width != UIView.noIntrinsicMetric { // max and min defined + viewWidth = max(min(maximumSize.width, viewSize.width), minimumSize.width) + widthToFit = maximumSize.width + } else if minimumSize.width != UIView.noIntrinsicMetric && maximumSize.width == UIView.noIntrinsicMetric { // only min defined + viewWidth = max(minimumSize.width, viewSize.width) + widthToFit = viewWidth + } else if minimumSize.width == UIView.noIntrinsicMetric && maximumSize.width != UIView.noIntrinsicMetric { // only max defined + viewWidth = min(maximumSize.width, viewSize.width) + widthToFit = maximumSize.width + } else { // neither defined + viewWidth = viewSize.width + widthToFit = viewWidth + } + + let viewHeight: CGFloat + let heightToFit: CGFloat + + if minimumSize.height != UIView.noIntrinsicMetric && maximumSize.height != UIView.noIntrinsicMetric { // max and min defined + viewHeight = max(min(maximumSize.height, viewSize.height), minimumSize.height) + heightToFit = maximumSize.height + } else if minimumSize.height != UIView.noIntrinsicMetric && maximumSize.height == UIView.noIntrinsicMetric { // only min defined + viewHeight = max(minimumSize.height, viewSize.height) + heightToFit = viewHeight + } else if minimumSize.height == UIView.noIntrinsicMetric && maximumSize.height != UIView.noIntrinsicMetric { // only max defined + viewHeight = min(maximumSize.height, viewSize.height) + heightToFit = maximumSize.height + } else { // neither defined + viewHeight = viewSize.height + heightToFit = viewHeight + } + + switch verticalAlignment { + case .center: + y += floor(0.5*heightToFit - 0.5*viewHeight) + case .bottom: + y += (heightToFit - viewHeight) + case .top: + break + } + + switch horizontalAlignment { + case .center: + x += floor(0.5*widthToFit - 0.5*viewWidth) + case .right: + x += (widthToFit - viewWidth) + case .left: + break + } + + let fitFrame = CGRect(x: round(x), y: round(y), width: ceil(viewWidth), height: ceil(viewHeight)) + if apply { + frame = fitFrame + } + return fitFrame + } + + @discardableResult public func wmf_preferredFrame(at point: CGPoint, maximumSize: CGSize = NoIntrinsicSize, minimumSize: CGSize = NoIntrinsicSize, horizontalAlignment: HorizontalAlignment, apply: Bool) -> CGRect { + return wmf_preferredFrame(at: point, maximumSize: maximumSize, minimumSize: minimumSize, horizontalAlignment: horizontalAlignment, verticalAlignment: .top, apply: apply) + } + + @discardableResult public func wmf_preferredFrame(at point: CGPoint, maximumSize: CGSize, minimumSize: CGSize = NoIntrinsicSize, alignedBy semanticContentAttribute: UISemanticContentAttribute, apply: Bool) -> CGRect { + let horizontalAlignment: HorizontalAlignment = semanticContentAttribute == .forceRightToLeft ? .right : .left + return wmf_preferredFrame(at: point, maximumSize: maximumSize, minimumSize: minimumSize, horizontalAlignment: horizontalAlignment, apply: apply) + } + + @discardableResult public func wmf_preferredFrame(at point: CGPoint, maximumWidth: CGFloat, minimumWidth: CGFloat = UIView.noIntrinsicMetric, horizontalAlignment: HorizontalAlignment, apply: Bool) -> CGRect { + let minimumSize = CGSize(width: minimumWidth, height: UIView.noIntrinsicMetric) + let maximumSize = CGSize(width: maximumWidth, height: UIView.noIntrinsicMetric) + return wmf_preferredFrame(at: point, maximumSize: maximumSize, minimumSize: minimumSize, horizontalAlignment: horizontalAlignment, apply: apply) + } + + @discardableResult public func wmf_preferredFrame(at point: CGPoint, maximumWidth: CGFloat, minimumWidth: CGFloat = UIView.noIntrinsicMetric, alignedBy semanticContentAttribute: UISemanticContentAttribute, apply: Bool) -> CGRect { + let horizontalAlignment: HorizontalAlignment = semanticContentAttribute == .forceRightToLeft ? .right : .left + return wmf_preferredFrame(at: point, maximumWidth: maximumWidth, minimumWidth: minimumWidth, horizontalAlignment: horizontalAlignment, apply: apply) + } + + @discardableResult public func wmf_preferredHeight(at point: CGPoint, maximumWidth: CGFloat, minimumWidth: CGFloat = UIView.noIntrinsicMetric, alignedBy semanticContentAttribute: UISemanticContentAttribute, spacing: CGFloat, apply: Bool) -> CGFloat { + return wmf_preferredFrame(at: point, maximumWidth: maximumWidth, minimumWidth: minimumWidth, alignedBy: semanticContentAttribute, apply: apply).layoutHeight(with: spacing) + } + + @discardableResult public func wmf_preferredHeight(at point: CGPoint, maximumWidth: CGFloat, minimumWidth: CGFloat = UIView.noIntrinsicMetric, horizontalAlignment: HorizontalAlignment, spacing: CGFloat, apply: Bool) -> CGFloat { + return wmf_preferredFrame(at: point, maximumWidth: maximumWidth, minimumWidth: minimumWidth, horizontalAlignment: horizontalAlignment, apply: apply).layoutHeight(with: spacing) + } +} + +extension UIButton { + public override func wmf_sizeThatFits(_ maximumSize: CGSize) -> CGSize { + var buttonAdjustedSize = maximumSize + var heightAdjustment = contentEdgeInsets.top + contentEdgeInsets.bottom + var widthAdjustment = contentEdgeInsets.left + contentEdgeInsets.right + + var imageHeight: CGFloat = 0 + if let image = image(for: .normal) { + widthAdjustment += imageEdgeInsets.left + imageEdgeInsets.right + image.size.width + imageHeight = image.size.height + imageEdgeInsets.top + imageEdgeInsets.bottom + contentEdgeInsets.top + contentEdgeInsets.bottom + } + + heightAdjustment += titleEdgeInsets.top + titleEdgeInsets.bottom + widthAdjustment += titleEdgeInsets.left + titleEdgeInsets.right + + if buttonAdjustedSize.width != UIView.noIntrinsicMetric { + buttonAdjustedSize.width = buttonAdjustedSize.width - widthAdjustment + } + + if buttonAdjustedSize.height != UIView.noIntrinsicMetric { + buttonAdjustedSize.height = buttonAdjustedSize.height - heightAdjustment + } + + let buttonLabelSize: CGSize + if let titleLabel = titleLabel { + buttonLabelSize = titleLabel.sizeThatFits(buttonAdjustedSize) + } else { + buttonLabelSize = .zero + } + + let maxHeight = max(imageHeight, buttonLabelSize.height + heightAdjustment) + return CGSize(width: buttonLabelSize.width + widthAdjustment, height: maxHeight) + } +} + +extension AlignedImageButton { + @discardableResult override public func wmf_preferredFrame(at point: CGPoint, maximumSize: CGSize = NoIntrinsicSize, minimumSize: CGSize = NoIntrinsicSize, horizontalAlignment: HorizontalAlignment = .center, verticalAlignment: VerticalAlignment = .center, apply: Bool = false) -> CGRect { + let adjustedPoint = CGPoint(x: point.x - leftPadding, y: point.y - verticalPadding) + var adjustedSize = maximumSize + if adjustedSize.width != UIView.noIntrinsicMetric { + adjustedSize.width = adjustedSize.width + leftPadding + rightPadding + } + if adjustedSize.height != UIView.noIntrinsicMetric { + adjustedSize.height = adjustedSize.height + 2 * verticalPadding + } + return super.wmf_preferredFrame(at: adjustedPoint, maximumSize: adjustedSize, minimumSize: minimumSize, horizontalAlignment: horizontalAlignment, verticalAlignment: verticalAlignment, apply: apply) + } +} + diff --git a/Apps/Wikipedia/WMF Framework/URLComponents+Extensions.swift b/Apps/Wikipedia/WMF Framework/URLComponents+Extensions.swift new file mode 100644 index 0000000..4613fdc --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/URLComponents+Extensions.swift @@ -0,0 +1,87 @@ +import Foundation + +extension URLComponents { + static func with(host: String, scheme: String = "https", path: String = "/", queryParameters: [String: Any]? = nil) -> URLComponents { + var components = URLComponents() + components.host = host + components.scheme = scheme + components.path = path + components.replacePercentEncodedQueryWithQueryParameters(queryParameters) + return components + } + + public static func percentEncodedQueryStringFrom(_ queryParameters: [String: Any]) -> String { + var query = "" + + // sort query parameters by key, this allows for consistency when itemKeys are generated for the persistent cache. + struct KeyValue { + let key: String + let value: Any + } + + var unorderedKeyValues: [KeyValue] = [] + + for (name, value) in queryParameters { + + unorderedKeyValues.append(KeyValue(key: name, value: value)) + } + + let orderedKeyValues = unorderedKeyValues.sorted { (lhs, rhs) -> Bool in + return lhs.key < rhs.key + } + + for keyValue in orderedKeyValues { + guard + let encodedName = keyValue.key.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryComponentAllowed), + let encodedValue = String(describing: keyValue.value).addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryComponentAllowed) else { + continue + } + if query != "" { + query.append("&") + } + + query.append("\(encodedName)=\(encodedValue)") + } + + return query + } + + mutating func appendQueryParametersToPercentEncodedQuery(_ queryParameters: [String: Any]?) { + guard let queryParameters = queryParameters else { + return + } + var newPEQ = "" + if let existing = percentEncodedQuery { + newPEQ = existing + "&" + } + newPEQ = newPEQ + URLComponents.percentEncodedQueryStringFrom(queryParameters) + percentEncodedQuery = newPEQ + } + + mutating func replacePercentEncodedQueryWithQueryParameters(_ queryParameters: [String: Any]?) { + guard let queryParameters = queryParameters else { + percentEncodedQuery = nil + return + } + percentEncodedQuery = URLComponents.percentEncodedQueryStringFrom(queryParameters) + } + + mutating func replacePercentEncodedPathWithPathComponents(_ pathComponents: [String]?) { + guard let pathComponents = pathComponents else { + percentEncodedPath = "/" + return + } + let fullComponents = [""] + pathComponents + #if DEBUG + for component in fullComponents { + assert(!component.contains("/")) + } + #endif + percentEncodedPath = fullComponents.joined(separator: "/") // NSString.path(with: components) removes the trailing slash that the reading list API needs + } + + public func wmf_URLWithLanguageVariantCode(_ code: String?) -> URL? { + return (self as NSURLComponents).wmf_URL(withLanguageVariantCode: code) + } + +} diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/Contents.json b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/saved.imageset/Contents.json b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/saved.imageset/Contents.json new file mode 100644 index 0000000..f3558b4 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/saved.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "save (saved).pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/saved.imageset/save (saved).pdf b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/saved.imageset/save (saved).pdf new file mode 100644 index 0000000..6049323 Binary files /dev/null and b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/saved.imageset/save (saved).pdf differ diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-delete.imageset/Contents.json b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-delete.imageset/Contents.json new file mode 100644 index 0000000..8978e38 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-delete.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "group.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-delete.imageset/group.pdf b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-delete.imageset/group.pdf new file mode 100644 index 0000000..bc04832 Binary files /dev/null and b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-delete.imageset/group.pdf differ diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-save.imageset/Contents.json b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-save.imageset/Contents.json new file mode 100644 index 0000000..38d7cfe --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-save.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "saveIcon.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-save.imageset/saveIcon.pdf b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-save.imageset/saveIcon.pdf new file mode 100644 index 0000000..dae7c2c Binary files /dev/null and b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-save.imageset/saveIcon.pdf differ diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-share.imageset/Contents.json b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-share.imageset/Contents.json new file mode 100644 index 0000000..152203c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-share.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "share.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-share.imageset/share.pdf b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-share.imageset/share.pdf new file mode 100644 index 0000000..6b4b414 Binary files /dev/null and b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-share.imageset/share.pdf differ diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-unsave.imageset/Contents.json b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-unsave.imageset/Contents.json new file mode 100644 index 0000000..b6f7861 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-unsave.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "saveIcon-2.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-unsave.imageset/saveIcon-2.pdf b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-unsave.imageset/saveIcon-2.pdf new file mode 100644 index 0000000..b5ceb1b Binary files /dev/null and b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/swipe-action-unsave.imageset/saveIcon-2.pdf differ diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/unsaved.imageset/Contents.json b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/unsaved.imageset/Contents.json new file mode 100644 index 0000000..4241980 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/unsaved.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "save (unselected).pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/unsaved.imageset/save (unselected).pdf b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/unsaved.imageset/save (unselected).pdf new file mode 100644 index 0000000..8b8f7d7 Binary files /dev/null and b/Apps/Wikipedia/WMF Framework/WMF Framework.xcassets/unsaved.imageset/save (unselected).pdf differ diff --git a/Apps/Wikipedia/WMF Framework/WMF.h b/Apps/Wikipedia/WMF Framework/WMF.h new file mode 100644 index 0000000..d86c694 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMF.h @@ -0,0 +1,133 @@ +@import Foundation; + +//! Project version number for WMF. +FOUNDATION_EXPORT double WMFVersionNumber; + +//! Project version string for WMF. +FOUNDATION_EXPORT const unsigned char WMFVersionString[]; + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import + +#import +#import + +#import +#import + +#import + +#import +#import + +#import +#import +#import + +#import +#import +#import +#import +#import +#import + +#import +#import + +#import + +#import + +#import +#import + +#import +#import + +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import + +#import +#import +#import +#import + +#import +#import + +#import +#import +#import +#import +#import +#import +#import + +//UI +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +//Deprecated +#import +#import diff --git a/Apps/Wikipedia/WMF Framework/WMFAnnouncement.h b/Apps/Wikipedia/WMF Framework/WMFAnnouncement.h new file mode 100644 index 0000000..9988d84 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFAnnouncement.h @@ -0,0 +1,40 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface WMFAnnouncement : WMFMTLModel + +@property (nonatomic, copy, readonly, nullable) NSString *identifier; +@property (nonatomic, copy, readonly, nullable) NSString *type; +@property (nonatomic, copy, readonly, nullable) NSDate *startTime; +@property (nonatomic, copy, readonly, nullable) NSDate *endTime; +@property (nonatomic, copy, readonly, nullable) NSArray *platforms; +@property (nonatomic, copy, readonly, nullable) NSArray *countries; +@property (nonatomic, copy, readonly, nullable) NSString *placement; + +@property (nonatomic, copy, readonly, nullable) NSURL *imageURL; +@property (nonatomic, copy, readonly, nullable) NSNumber *imageHeight; + +@property (nonatomic, copy, readonly, nullable) NSString *text; + +@property (nonatomic, copy, readonly, nullable) NSString *actionTitle; +@property (nonatomic, copy, readonly, nullable) NSURL *actionURL; +@property (nonatomic, copy, readonly, nullable) NSString *actionURLString; + +@property (nonatomic, copy, readonly, nullable) NSString *captionHTML; +@property (nonatomic, copy, readonly, nullable) NSString *negativeText; + +@property (nonatomic, copy, readonly, nullable) NSNumber *readingListSyncEnabled; +@property (nonatomic, copy, readonly, nullable) NSNumber *loggedIn; +@property (nonatomic, copy, readonly, nullable) NSNumber *beta; + +@property (nonatomic, copy, readonly, nullable) NSString *domain; + +//only applies to survey types +@property (nonatomic, copy, readonly, nullable) NSArray *articleTitles; +@property(nonatomic, copy, readonly, nullable) NSNumber *percentReceivingExperiment; +@property (nonatomic, strong, readonly, nullable) NSNumber *displayDelay; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/WMFAnnouncement.m b/Apps/Wikipedia/WMF Framework/WMFAnnouncement.m new file mode 100644 index 0000000..5fc8288 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFAnnouncement.m @@ -0,0 +1,110 @@ +#import "WMFAnnouncement.h" +#import +#import +#import +#import + +@implementation WMFAnnouncement + +@synthesize actionURL = _actionURL; + ++ (NSDictionary *)JSONKeyPathsByPropertyKey { + return @{ + WMF_SAFE_KEYPATH(WMFAnnouncement.new, identifier): @"id", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, type): @"type", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, startTime): @"start_time", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, endTime): @"end_time", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, platforms): @"platforms", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, countries): @"countries", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, placement): @"placement", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, text): @"text", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, actionTitle): @"action.title", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, actionURLString): @"action.url", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, captionHTML): @"caption_HTML", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, imageURL): @[@"image", @"image_url"], + WMF_SAFE_KEYPATH(WMFAnnouncement.new, imageHeight): @"image_height", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, negativeText): @"negative_text", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, loggedIn): @"logged_in", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, readingListSyncEnabled): @"reading_list_sync_enabled", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, beta): @"beta", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, domain): @"domain", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, articleTitles): @"articleTitles", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, displayDelay): @"displayDelay", + WMF_SAFE_KEYPATH(WMFAnnouncement.new, percentReceivingExperiment): @"percent_receiving_experiment" + }; +} + ++ (NSInteger)version { + return 4; +} + +- (NSURL *)actionURL { + if (!_actionURL) { + _actionURL = [NSURL wmf_optionalURLWithString: self.actionURLString]; + } + + return _actionURL; +} + ++ (NSValueTransformer *)imageURLJSONTransformer { + return [MTLValueTransformer + transformerUsingForwardBlock:^NSURL *(NSDictionary *value, + BOOL *success, + NSError *__autoreleasing *error) { + NSString *urlString = value[@"image"] ?: value[@"image_url"]; + return [NSURL wmf_optionalURLWithString:urlString]; + } + reverseBlock:^NSDictionary *(NSURL *URL, + BOOL *success, + NSError *__autoreleasing *error) { + NSString *urlString = [URL absoluteString]; + if (!urlString) { + return @{}; + } + return @{@"image_url": urlString}; + }]; +} + ++ (NSValueTransformer *)startTimeJSONTransformer { + return [MTLValueTransformer transformerUsingForwardBlock:^id(NSString *value, BOOL *success, NSError *__autoreleasing *error) { + NSDate *date = [[NSDateFormatter wmf_iso8601Formatter] dateFromString:value]; + return date; + }]; +} + ++ (NSValueTransformer *)endTimeJSONTransformer { + return [MTLValueTransformer transformerUsingForwardBlock:^id(NSString *value, BOOL *success, NSError *__autoreleasing *error) { + NSDate *date = [[NSDateFormatter wmf_iso8601Formatter] dateFromString:value]; + return date; + }]; +} + ++ (NSDictionary *)allowedSecureCodingClassesByPropertyKey { + + //Add NSString to list of allowed classes for NSArray properties. + //This fixes the "[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type 'NSString' for key 'NS.objects', even though it was not explicitly included in the client allowed classes set" console error in iOS 15 + NSDictionary *superAllowedClassesDict = [super allowedSecureCodingClassesByPropertyKey]; + NSMutableDictionary *allowedClassesDict = [[NSMutableDictionary alloc] initWithDictionary:superAllowedClassesDict]; + + NSArray *keysToCheck = @[@"countries", @"platforms", @"articleTitles"]; + + for (NSString *key in keysToCheck) { + NSObject *object = [allowedClassesDict objectForKey:key]; + if ([object isKindOfClass:[NSArray class]]) { + NSMutableArray *allowedClasses = [NSMutableArray arrayWithArray:(NSArray *)object]; + [allowedClasses addObject:[NSString class]]; + [allowedClassesDict setObject:[NSArray arrayWithArray:allowedClasses] forKey:key]; + } + } + + return [NSDictionary dictionaryWithDictionary:allowedClassesDict]; +} + +// No languageVariantCodePropagationSubelementKeys + ++ (NSArray *)languageVariantCodePropagationURLKeys { + return @[@"imageURL", + @"actionURL"]; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFAnnouncementsFetcher.h b/Apps/Wikipedia/WMF Framework/WMFAnnouncementsFetcher.h new file mode 100644 index 0000000..1e1993d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFAnnouncementsFetcher.h @@ -0,0 +1,11 @@ +@import Foundation; +#import +#import + +@class WMFAnnouncement; + +@interface WMFAnnouncementsFetcher : WMFLegacyFetcher + +- (void)fetchAnnouncementsForURL:(NSURL *)siteURL force:(BOOL)force failure:(WMFErrorHandler)failure success:(void (^)(NSArray *announcements))success; + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFAnnouncementsFetcher.m b/Apps/Wikipedia/WMF Framework/WMFAnnouncementsFetcher.m new file mode 100644 index 0000000..67feb8d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFAnnouncementsFetcher.m @@ -0,0 +1,100 @@ +#import "WMFAnnouncementsFetcher.h" +#import "WMFAnnouncement.h" +#import +#import + +@implementation WMFAnnouncementsFetcher + +- (void)fetchAnnouncementsForURL:(NSURL *)siteURL force:(BOOL)force failure:(WMFErrorHandler)failure success:(void (^)(NSArray *announcements))success { + NSParameterAssert(siteURL); + if (siteURL == nil) { + NSError *error = [WMFFetcher invalidParametersError]; + failure(error); + return; + } + + NSURL *url = [self.configuration announcementsAPIURLForURL:siteURL appendingPathComponents:@[@"feed", @"announcements"]]; + [self.session getJSONDictionaryFromURL:url ignoreCache:YES completionHandler:^(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error) { + if (error) { + failure(error); + return; + } + + if (response.statusCode == 304) { + failure([WMFFetcher noNewDataError]); + return; + } + + NSError *serializerError = nil; + NSArray *announcements = [WMFLegacySerializer modelsOfClass:[WMFAnnouncement class] fromArrayForKeyPath:@"announce" inJSONDictionary:result languageVariantCode:url.wmf_languageVariantCode error:&serializerError]; + if (serializerError) { + failure(serializerError); + return; + } + + NSString *geoIPCookie = [self geoIPCookieString]; + NSString *setCookieHeader = nil; + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + setCookieHeader = [(NSHTTPURLResponse *)response allHeaderFields][@"Set-Cookie"]; + } + NSArray *announcementsFilteredByCountry = [self filterAnnouncements:announcements withCurrentCountryInIPHeader:setCookieHeader geoIPCookieValue:geoIPCookie]; + NSArray *filteredAnnouncements = [self filterAnnouncementsForiOSPlatform:announcementsFilteredByCountry]; + success(filteredAnnouncements); + }]; +} + +- (NSArray *)filterAnnouncements:(NSArray *)announcements withCurrentCountryInIPHeader:(NSString *)header geoIPCookieValue:(NSString *)cookieValue { + + NSArray *validAnnouncements = [announcements wmf_select:^BOOL(WMFAnnouncement *obj) { + if (![obj isKindOfClass:[WMFAnnouncement class]]) { + return NO; + } + NSArray *countries = [obj countries]; + if (countries.count == 0) { + return YES; + } + __block BOOL valid = NO; + [countries enumerateObjectsUsingBlock:^(NSString *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { + if ([header containsString:[NSString stringWithFormat:@"GeoIP=%@", obj]]) { + valid = YES; + *stop = YES; + } + if ([header length] < 1 && [cookieValue hasPrefix:obj]) { + valid = YES; + *stop = YES; + } + }]; + return valid; + }]; + return validAnnouncements; +} + +- (NSArray *)filterAnnouncementsForiOSPlatform:(NSArray *)announcements { + + NSArray *validAnnouncements = [announcements wmf_select:^BOOL(WMFAnnouncement *obj) { + if (![obj isKindOfClass:[WMFAnnouncement class]]) { + return NO; + } + if ([obj.platforms containsObject:@"iOSAppV5"]) { + return YES; + } else { + return NO; + } + }]; + return validAnnouncements; +} + +- (NSString *)geoIPCookieString { + NSArray *cookies = [[WMFSession sharedCookieStorage] cookies];; + NSHTTPCookie *cookie = [cookies wmf_match:^BOOL(NSHTTPCookie *obj) { + if ([[obj name] containsString:@"GeoIP"]) { + return YES; + } else { + return NO; + } + }]; + + return [cookie value]; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataClass.h b/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataClass.h new file mode 100644 index 0000000..db65302 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataClass.h @@ -0,0 +1,14 @@ +#import + +NS_ASSUME_NONNULL_BEGIN +@class ReadingList; + +@interface WMFArticle : NSManagedObject + +@property (atomic, readonly) NSArray *sortedNonDefaultReadingLists; + +@end + +NS_ASSUME_NONNULL_END + +#import diff --git a/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataClass.m b/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataClass.m new file mode 100644 index 0000000..d6dfa46 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataClass.m @@ -0,0 +1,39 @@ +#import "WMFArticle+CoreDataClass.h" +#import + +@interface WMFArticle () { + NSArray * _Nullable _sortedNonDefaultReadingLists; +} +@end + +@implementation WMFArticle + +- (NSArray *)sortedNonDefaultReadingLists { + @synchronized (self) { + if (_sortedNonDefaultReadingLists != nil) { + return _sortedNonDefaultReadingLists; + } + _sortedNonDefaultReadingLists = [[self.readingLists filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"isDefault == NO"]] sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"canonicalName" ascending:YES comparator:^NSComparisonResult(NSString *a, NSString *b) { + if (a == nil) { + return NSOrderedAscending; + } + if (b == nil) { + return NSOrderedDescending; + } + return [a localizedStandardCompare:b]; + }]]]; + return _sortedNonDefaultReadingLists ?: @[]; + } +} + +- (void)didChangeValueForKey:(NSString *)inKey withSetMutation:(NSKeyValueSetMutationKind)inMutationKind usingObjects:(NSSet *)inObjects { + [super didChangeValueForKey:inKey withSetMutation:inMutationKind usingObjects:inObjects]; + if (![inKey isEqualToString:@"readingLists"]) { + return; + } + @synchronized (self) { + _sortedNonDefaultReadingLists = nil; + } +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataProperties.h b/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataProperties.h new file mode 100644 index 0000000..73cba02 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataProperties.h @@ -0,0 +1,79 @@ +#import + +@class ReadingList; + +NS_ASSUME_NONNULL_BEGIN + +@interface WMFArticle (CoreDataProperties) + ++ (NSFetchRequest *)fetchRequest; + +@property (nullable, nonatomic, copy) NSString *key; // Key is a standardized version of the article URL generated by the wmf_databaseKey extension on NSURL. It's the desktop domain (for example, en.wikipedia.org instead of en.m.wikipedia.org) with /wiki/{{title}} as the path and no percent encoding. The scheme is https and there should be no fragment or query. The string is standardized with the `precomposedStringWithCanonicalMapping` method to prevent differences in encoding certain characters. + +@property (nullable, nonatomic, copy) NSString *variant; // Variant is the langugage variant associated with the key + +#pragma mark - User interaction +#pragma mark Reading Lists and Saving for Offline +@property (nullable, nonatomic, copy) NSDate *savedDate; // The date the user saved the article or nil if the article was never saved by the user +@property (nullable, nonatomic, retain) NSSet *readingLists; +@property (nullable, nonatomic, retain) NSSet *previewReadingLists; +@property (nullable, nonatomic, copy) NSNumber *errorCodeNumber; // error with article download - use error extension +@property (nonatomic) BOOL isDownloaded; // is fully downloaded for offline viewing +@property (nullable, nonatomic, copy) NSDate *downloadRetryDate; // The date when it's OK to retry the download +@property (nonatomic) int16_t downloadAttemptCount; // The number of attempts that have been made to download + +@property (nonatomic) BOOL isConversionFromMobileViewNeeded; +#pragma mark Viewing +@property (nullable, nonatomic, copy) NSDate *viewedDate; // The date the user viewed the article in the article view or nil if the article was never viewed by the user +@property (nullable, nonatomic, copy) NSDate *viewedDateWithoutTime; // Viewed date without time for grouping by day in history +@property (nullable, nonatomic, copy) NSDate *lastModifiedDate; // Last date the article was modified + +@property (nullable, nonatomic, copy) NSString *viewedFragment; // Last section fragment viewed by the user +@property (nonatomic) double viewedScrollPosition; // Last scroll position viewed by the user +@property (nonatomic) BOOL wasSignificantlyViewed; // Was viewed for a significant amount of time by the user +#pragma mark Other +@property (nonatomic) BOOL isExcludedFromFeed; // set to true if the user hid this article from the feed +@property (nullable, nonatomic, copy) NSDate *newsNotificationDate; +@property (nullable, nonatomic, copy) NSNumber *placesSortOrder; + +#pragma mark - Metadata + +@property (nullable, nonatomic, copy) NSString *displayTitle; // Don't use this property, use displayTitleHTML. It will set the plain text version to displayTitle. +@property (nullable, nonatomic, copy) NSString *imageURLString; // original image URL +@property (nonatomic, copy) NSNumber *imageHeight; // original image height +@property (nonatomic, copy) NSNumber *imageWidth; // original image width +@property (nullable, nonatomic, retain) NSDictionary *pageViews; +@property (nullable, nonatomic, copy) NSString *snippet; // TODO: consider making naming consistent (probably use 'extract' instead of 'snippet' here and 'summary' elsewhere) +@property (nullable, nonatomic, copy) NSString *wikidataDescription; +@property (nullable, nonatomic, copy) NSString *wikidataID; +@property (nullable, nonatomic, copy) NSNumber *pageID; // The pageID from MediaWiki + +#pragma mark - Coordinates and Geography +@property (nullable, nonatomic, copy) NSNumber *signedQuadKey; // representation of the latitude and longitude of the article. Use the `coordinate` extension to access this value +@property (nonatomic, copy) NSNumber *geoDimensionNumber; // geoDimension from search API +@property (nonatomic, copy) NSNumber *geoTypeNumber; // geoType from search API + +#pragma mark - Deprecated +@property (nullable, nonatomic, copy) NSString *displayTitleHTMLString __attribute__((deprecated)); // This is used for storage of the displayTitleHTML but is "deprecated" in the sense that you shouldn't use this property, you should use displayTitleHTML which will set this and the display title +@property (nonatomic) double latitude; //__deprecated; // Use coordinate instead (not using actual __deprecated tag due to inability to ignore the warning when these are used in Swift) +@property (nonatomic) double longitude; //__deprecated; // Use coordinate instead (not using actual __deprecated tag due to inability to ignore the warning when these are used in Swift) +@property (nullable, nonatomic, copy) NSNumber *ns __attribute__((deprecated)); // Use namespace extension instead +@property (nullable, nonatomic, copy) NSString *thumbnailURLString; // deprecated + +@end + +@interface WMFArticle (CoreDataGeneratedAccessors) + +- (void)addReadingListsObject:(ReadingList *)value; +- (void)removeReadingListsObject:(ReadingList *)value; +- (void)addReadingLists:(NSSet *)values; +- (void)removeReadingLists:(NSSet *)values; + +- (void)addPreviewReadingListsObject:(ReadingList *)value; +- (void)removePreviewReadingListsObject:(ReadingList *)value; +- (void)addPreviewReadingLists:(NSSet *)values; +- (void)removePreviewReadingLists:(NSSet *)values; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataProperties.m b/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataProperties.m new file mode 100644 index 0000000..34f314d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFArticle+CoreDataProperties.m @@ -0,0 +1,46 @@ +#import "WMFArticle+CoreDataProperties.h" + +@implementation WMFArticle (CoreDataProperties) + ++ (NSFetchRequest *)fetchRequest { + return [[NSFetchRequest alloc] initWithEntityName:@"WMFArticle"]; +} + +@dynamic displayTitle; +@dynamic displayTitleHTMLString; +@dynamic geoDimensionNumber; +@dynamic geoTypeNumber; +@dynamic imageHeight; +@dynamic imageURLString; +@dynamic imageWidth; +@dynamic isDownloaded; +@dynamic isConversionFromMobileViewNeeded; +@dynamic isExcludedFromFeed; +@dynamic key; +@dynamic latitude; +@dynamic longitude; +@dynamic newsNotificationDate; +@dynamic pageViews; +@dynamic placesSortOrder; +@dynamic savedDate; +@dynamic signedQuadKey; +@dynamic snippet; +@dynamic thumbnailURLString; +@dynamic variant; +@dynamic viewedDate; +@dynamic viewedDateWithoutTime; +@dynamic viewedFragment; +@dynamic viewedScrollPosition; +@dynamic wasSignificantlyViewed; +@dynamic wikidataDescription; +@dynamic wikidataID; +@dynamic readingLists; +@dynamic previewReadingLists; +@dynamic errorCodeNumber; +@dynamic ns; +@dynamic pageID; +@dynamic lastModifiedDate; +@dynamic downloadRetryDate; +@dynamic downloadAttemptCount; + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFArticle+Errors.swift b/Apps/Wikipedia/WMF Framework/WMFArticle+Errors.swift new file mode 100644 index 0000000..de4e7b0 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFArticle+Errors.swift @@ -0,0 +1,83 @@ +import Foundation + + +public enum ArticleError: Int32, Error { + case none = 0 + case saveToDiskFailed = 1 + case apiFailed = 2 +} + +extension ArticleError: LocalizedError { + public var errorDescription: String? { + switch self { + case .none: + return nil + case .saveToDiskFailed: + return WMFLocalizedString("reading-lists-article-save-to-disk-failed", value: "Device limited exceeded, unable to sync article", comment: "Text of the alert label informing the user that article couldn't be saved due to insufficient storage available") + case .apiFailed: + return WMFLocalizedString("reading-lists-article-api-failure", value: "Unable to sync article", comment: "Text of the alert label informing the user that article couldn't be saved due to a server-side error") + } + } + public var failureReason: String? { + switch self { + case .none: + return nil + case .saveToDiskFailed: + return nil + case .apiFailed: + return nil + } + } + public var recoverySuggestion: String? { + switch self { + case .none: + return nil + case .saveToDiskFailed: + return WMFLocalizedString("reading-lists-article-save-to-disk-recovery-suggestion", value: "Clear some space on your device and try again", comment: "Recovery suggestion to clear space on the user's device to allow articles to download") + case .apiFailed: + return nil + } + } +} + + +extension WMFArticle { + public var error: ArticleError { + get { + return ArticleError(rawValue: errorCodeNumber?.int32Value ?? 0) ?? .none + } + set { + guard newValue != .none else { + errorCodeNumber = nil + return + } + errorCodeNumber = NSNumber(value: newValue.rawValue) + } + } + + public func retryDownload() { + guard savedDate != nil else { + return + } + isDownloaded = false + errorCodeNumber = nil + downloadAttemptCount = 0 + downloadRetryDate = nil + } +} + +extension NSManagedObjectContext { + public func retryFailedArticleDownloads(with keys: [String]) throws { + let batches = keys.chunked(into: 500) + for batch in batches { + let articleFetch = WMFArticle.fetchRequest() + articleFetch.predicate = NSPredicate(format: "errorCodeNumber != NULL && key IN %@", batch) + let articles = try fetch(articleFetch) + for article in articles { + article.retryDownload() + } + try save() + } + } +} + diff --git a/Apps/Wikipedia/WMF Framework/WMFArticle+Extensions.h b/Apps/Wikipedia/WMF Framework/WMFArticle+Extensions.h new file mode 100644 index 0000000..8e19d48 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFArticle+Extensions.h @@ -0,0 +1,93 @@ +#import + +@class MWKSearchResult; +@class WMFFeedArticlePreview; +@class WMFInMemoryURLKey; + +typedef NS_ENUM(NSUInteger, WMFGeoType) { + WMFGeoTypeUnknown = 0, + WMFGeoTypeCountry, + WMFGeoTypeSatellite, + WMFGeoTypeAdm1st, + WMFGeoTypeAdm2nd, + WMFGeoTypeAdm3rd, + WMFGeoTypeCity, + WMFGeoTypeAirport, + WMFGeoTypeMountain, + WMFGeoTypeIsle, + WMFGeoTypeWaterBody, + WMFGeoTypeForest, + WMFGeoTypeRiver, + WMFGeoTypeGlacier, + WMFGeoTypeEvent, + WMFGeoTypeEdu, + WMFGeoTypePass, + WMFGeoTypeRailwayStation, + WMFGeoTypeLandmark +}; + +typedef NS_ENUM(NSUInteger, WMFArticleAction) { + WMFArticleActionNone = 0, + WMFArticleActionRead, + WMFArticleActionSave, + WMFArticleActionShare, +}; + +NS_ASSUME_NONNULL_BEGIN + +@interface WMFArticle (WMFExtensions) + +@property (nonatomic, readonly, nullable) NSURL *URL; + +@property (nonatomic, readonly, nullable) WMFInMemoryURLKey *inMemoryKey; + +@property (nonatomic, copy, nonnull) NSString *displayTitleHTML; + +@property (nonatomic, readonly, nullable) NSString *capitalizedWikidataDescription; + +@property (nonatomic, readonly) BOOL isAnyVariantSaved; // An article should appear as saved in the UI if any of its language variants are saved +@property (nonatomic, copy, readonly, nullable) WMFArticle *savedVariant; // The article variant that is saved + +@property (nonatomic, nullable) NSURL *thumbnailURL; // Deprecated. Use imageURLForWidth: + ++ (nullable NSURL *)imageURLForTargetImageWidth:(NSInteger)width fromImageSource:(NSString *)imageSource withOriginalWidth:(NSInteger)originalWidth; +- (nullable NSURL *)imageURLForWidth:(NSInteger)width; + +@property (nonatomic, readonly, nullable) NSArray *pageViewsSortedByDate; + +@property (nonatomic, readonly) WMFGeoType geoType; + +@property (nonatomic, readonly) int64_t geoDimension; + +- (void)updateViewedDateWithoutTime; // call after setting viewedDate + +- (void)updateWithSearchResult:(nullable MWKSearchResult *)searchResult; + +@end + +@interface NSManagedObjectContext (WMFArticle) + +- (nullable WMFArticle *)fetchArticleWithURL:(nullable NSURL *)articleURL; + +- (nullable WMFArticle *)fetchArticleWithKey:(nullable NSString *)key variant:(nullable NSString *)variant; + +- (nullable NSArray *)fetchArticlesWithURL:(nullable NSURL *)url error:(NSError **)error; +- (nullable NSArray *)fetchArticlesWithKey:(nullable NSString *)key variant:(nullable NSString *)variant error:(NSError **)error; +- (nullable NSArray *)fetchArticlesWithInMemoryURLKeys:(NSArray *)urlKeys error:(NSError **)error NS_SWIFT_NAME(fetchArticlesWithInMemoryURLKeys(_:)); + +- (nullable WMFArticle *)createArticleWithURL:(nullable NSURL *)url; +- (nullable WMFArticle *)createArticleWithKey:(nullable NSString *)key variant:(nullable NSString *)variant; + +- (nullable WMFArticle *)fetchOrCreateArticleWithKey:(nullable NSString *)key variant:(nullable NSString *)variant; + +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleURL; + +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleURL updatedWithSearchResult:(nullable MWKSearchResult *)searchResult; + +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleURL updatedWithFeedPreview:(nullable WMFFeedArticlePreview *)feedPreview pageViews:(nullable NSDictionary *)pageViews isFeatured:(BOOL)isFeatured; + +- (nullable WMFArticle *)fetchArticleWithWikidataID:(nullable NSString *)wikidataID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/WMFArticle+Extensions.m b/Apps/Wikipedia/WMF Framework/WMFArticle+Extensions.m new file mode 100644 index 0000000..4233ea4 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFArticle+Extensions.m @@ -0,0 +1,323 @@ +#import +#import +#import + +@interface NSManagedObjectContext (WMFArticle_Private) +- (NSUInteger)countOfSavedArticleVariantsWithKey:(nullable NSString *)key error:(NSError **)error; +- (nullable WMFArticle *)savedArticleVariantWithKey:(nullable NSString *)key error:(NSError **)error; +@end + +@implementation WMFArticle (Extensions) + +- (NSString *)capitalizedWikidataDescription { + return [self.wikidataDescription wmf_stringByCapitalizingFirstCharacterUsingWikipediaLanguageCode:self.URL.wmf_languageCode]; +} + +- (nullable NSURL *)URL { + NSString *key = self.key; + if (!key) { + return nil; + } + NSURL *value = [NSURL URLWithString:key]; + value.wmf_languageVariantCode = self.variant; + return value; +} + +- (nullable WMFInMemoryURLKey *)inMemoryKey { + return self.URL.wmf_inMemoryKey; +} + +- (BOOL)isAnyVariantSaved { + NSUInteger savedCount = [self.managedObjectContext countOfSavedArticleVariantsWithKey:self.key error:nil]; + NSAssert(savedCount < 2, @"More than one article variant marked as saved for key '%@'", self.key); + return savedCount > 0; +} + +- (nullable WMFArticle *)savedVariant { + // If the article has a savedDate, it is the saved variant + if (self.savedDate != nil) { + return self; + // If the article does not have a variant, no further checking is needed. Return nil. + } else if (!self.variant) { + return nil; + } else { + return [self.managedObjectContext savedArticleVariantWithKey:self.key error:nil]; + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" // this is the only section of code where the "deprecated" but not really deprecated displayTitleHTMLString should be used + +- (void)setDisplayTitleHTML:(NSString *)displayTitleHTML { + self.displayTitleHTMLString = displayTitleHTML; + self.displayTitle = displayTitleHTML.wmf_stringByRemovingHTML; +} + +- (NSString *)displayTitleHTML { + return self.displayTitleHTMLString ?: self.displayTitle ?: self.URL.wmf_title ?: @""; +} + +#pragma clang diagnostic pop + +- (nullable NSURL *)thumbnailURL { + NSString *thumbnailURLString = self.thumbnailURLString; + if (!thumbnailURLString) { + return [self imageURLForWidth:240]; //hardcoded to not rely on UIScreen in a model object + } + return [NSURL URLWithString:thumbnailURLString]; +} + ++ (nullable NSURL *)imageURLForTargetImageWidth:(NSInteger)width fromImageSource:(NSString *)imageSource withOriginalWidth:(NSInteger)originalWidth { + NSAssert(width > 0, @"Width must be > 0"); + if (width <= 0) { + return nil; + } + NSString *lowercasePathExtension = [[imageSource pathExtension] lowercaseString]; + if (width >= originalWidth && ![lowercasePathExtension isEqualToString:@"svg"] && ![lowercasePathExtension isEqualToString:@"pdf"]) { + return [NSURL URLWithString:imageSource]; + } + return [NSURL URLWithString:WMFChangeImageSourceURLSizePrefix(imageSource, width)]; +} + +- (nullable NSURL *)imageURLForWidth:(NSInteger)width { + NSAssert(width > 0, @"Width must be > 0"); + if (width <= 0) { + return nil; + } + NSString *imageURLString = self.imageURLString; + NSNumber *imageWidth = self.imageWidth; + if (!imageURLString || !imageWidth) { + NSString *thumbnailURLString = self.thumbnailURLString; + if (!thumbnailURLString) { + return nil; + } + NSInteger sizePrefix = WMFParseSizePrefixFromSourceURL(thumbnailURLString); + if (sizePrefix == NSNotFound || width >= sizePrefix) { + return [NSURL URLWithString:thumbnailURLString]; + } + return [NSURL URLWithString:WMFChangeImageSourceURLSizePrefix(thumbnailURLString, width)]; + } + return [WMFArticle imageURLForTargetImageWidth:width fromImageSource:imageURLString withOriginalWidth:[imageWidth integerValue]]; +} + +- (void)setThumbnailURL:(NSURL *)thumbnailURL { + self.thumbnailURLString = thumbnailURL.absoluteString; +} + +- (NSArray *)pageViewsSortedByDate { + return self.pageViews.wmf_pageViewsSortedByDate; +} + +- (void)updateViewedDateWithoutTime { + NSDate *viewedDate = self.viewedDate; + if (viewedDate) { + NSCalendar *calendar = [NSCalendar wmf_gregorianCalendar]; + NSDateComponents *components = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:viewedDate]; + self.viewedDateWithoutTime = [calendar dateFromComponents:components]; + } else { + self.viewedDateWithoutTime = nil; + } +} + +- (void)updateWithSearchResult:(nullable MWKSearchResult *)searchResult { + if ([searchResult.displayTitleHTML length] > 0) { + self.displayTitleHTML = searchResult.displayTitleHTML; + } else if ([searchResult.displayTitle length] > 0) { + self.displayTitleHTML = searchResult.displayTitle; + } + + if ([searchResult.wikidataDescription length] > 0) { + self.wikidataDescription = searchResult.wikidataDescription; + } + if ([searchResult.extract length] > 0) { + self.snippet = searchResult.extract; + } + if (searchResult.thumbnailURL != nil) { + self.thumbnailURL = searchResult.thumbnailURL; + } + if (searchResult.location != nil) { + self.location = searchResult.location; + } + if (searchResult.geoDimension != nil) { + self.geoDimensionNumber = searchResult.geoDimension; + } + if (searchResult.geoType != nil) { + self.geoTypeNumber = searchResult.geoType; + } +} + +@end + +#pragma mark - NSManagedObjectContext Extensions + +@implementation NSManagedObjectContext (WMFArticle) + +- (nullable WMFArticle *)fetchArticleWithURL:(nullable NSURL *)articleURL { + return [self fetchArticleWithKey:articleURL.wmf_databaseKey variant:articleURL.wmf_languageVariantCode]; +} + +- (nullable NSArray *)fetchArticlesWithURL:(nullable NSURL *)url error:(NSError **)error { + return [self fetchArticlesWithKey:url.wmf_databaseKey variant:url.wmf_languageVariantCode error:error]; +} + +- (nullable NSArray *)fetchArticlesWithKey:(nullable NSString *)key variant:(nullable NSString *)variant error:(NSError **)error { + if (!key) { + return @[]; + } + NSFetchRequest *request = [WMFArticle fetchRequest]; + request.predicate = [NSPredicate predicateWithFormat:@"key == %@ && variant == %@", key, variant]; + return [self executeFetchRequest:request error:error]; +} + +- (nullable NSArray *)fetchArticlesWithInMemoryURLKeys:(NSArray *)urlKeys error:(NSError **)error { + if (urlKeys.count == 0) { + return @[]; + } + NSFetchRequest *request = [WMFArticle fetchRequest]; + request.predicate = [self articlePredicateForInMemoryURLKeys:urlKeys]; + return [self executeFetchRequest:request error:error]; +} + +- (NSPredicate *)articlePredicateForInMemoryURLKeys:(NSArray *)urlKeys { + NSMutableArray *subpredicates = [[NSMutableArray alloc] init]; + for (WMFInMemoryURLKey *urlKey in urlKeys) { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"key == %@ && variant == %@", urlKey.databaseKey, urlKey.languageVariantCode]; + [subpredicates addObject:predicate]; + } + return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; +} + +- (nullable WMFArticle *)fetchArticleWithKey:(nullable NSString *)key variant:(nullable NSString *)variant { + if (!key) { + return nil; + } + WMFArticle *article = nil; + NSFetchRequest *request = [WMFArticle fetchRequest]; + request.fetchLimit = 1; + request.predicate = [NSPredicate predicateWithFormat:@"key == %@ && variant == %@", key, variant]; + article = [[self executeFetchRequest:request error:nil] firstObject]; + return article; +} + +- (nullable WMFArticle *)fetchArticleWithWikidataID:(nullable NSString *)wikidataID { + if (!wikidataID) { + return nil; + } + WMFArticle *article = nil; + NSFetchRequest *request = [WMFArticle fetchRequest]; + request.fetchLimit = 1; + request.predicate = [NSPredicate predicateWithFormat:@"wikidataID == %@", wikidataID]; + article = [[self executeFetchRequest:request error:nil] firstObject]; + return article; +} + +- (nullable WMFArticle *)createArticleWithURL:(nullable NSURL *)url { + return [self createArticleWithKey:url.wmf_databaseKey variant:url.wmf_languageVariantCode]; +} + +- (nullable WMFArticle *)createArticleWithKey:(nullable NSString *)key variant:(nullable NSString *)variant { + WMFArticle *article = [[WMFArticle alloc] initWithContext:self]; + article.key = key; + article.variant = variant; + return article; +} + +- (nullable WMFArticle *)fetchOrCreateArticleWithKey:(nullable NSString *)key variant:(nullable NSString *)variant { + if (!key) { + return nil; + } + WMFArticle *article = [self fetchArticleWithKey:key variant:variant]; + if (!article) { + article = [self createArticleWithKey:key variant:variant]; + } + return article; +} + +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleURL { + return [self fetchOrCreateArticleWithKey:articleURL.wmf_databaseKey variant:articleURL.wmf_languageVariantCode]; +} + +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleURL updatedWithSearchResult:(nullable MWKSearchResult *)searchResult { + + NSParameterAssert(articleURL); + WMFArticle *article = [self fetchOrCreateArticleWithURL:articleURL]; + [article updateWithSearchResult:searchResult]; + return article; +} + +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(nullable NSURL *)articleURL updatedWithFeedPreview:(nullable WMFFeedArticlePreview *)feedPreview pageViews:(nullable NSDictionary *)pageViews isFeatured:(BOOL)isFeatured { + NSParameterAssert(articleURL); + if (!articleURL) { + return nil; + } + + WMFArticle *article = [self fetchOrCreateArticleWithURL:articleURL]; + + if (isFeatured) { + WMFFeedArticlePreview *oldFeedPreview = [article feedArticlePreview]; + if (![oldFeedPreview isEqual:feedPreview]) { + [SharedContainerCacheClearFeaturedArticleWrapper clearOutFeaturedArticleWidgetCache]; + } + } + + if ([feedPreview.displayTitleHTML length] > 0) { + article.displayTitleHTML = feedPreview.displayTitleHTML; + } else if ([feedPreview.displayTitle length] > 0) { + article.displayTitleHTML = feedPreview.displayTitle; + } + + if ([feedPreview.wikidataDescription length] > 0) { + article.wikidataDescription = feedPreview.wikidataDescription; + } + if ([feedPreview.snippet length] > 0) { + article.snippet = feedPreview.snippet; + } + if (feedPreview.thumbnailURL != nil) { + + article.thumbnailURL = feedPreview.thumbnailURL; + } + if (pageViews != nil) { + if (article.pageViews == nil) { + article.pageViews = pageViews; + } else { + article.pageViews = [article.pageViews mtl_dictionaryByAddingEntriesFromDictionary:pageViews]; + } + } + if (feedPreview.imageURLString != nil) { + article.imageURLString = feedPreview.imageURLString; + } + if (feedPreview.imageWidth != nil) { + article.imageWidth = feedPreview.imageWidth; + } + if (feedPreview.imageHeight != nil) { + article.imageHeight = feedPreview.imageHeight; + } + + return article; +} + +@end + + +@implementation NSManagedObjectContext (WMFArticle_Private) + +- (NSUInteger)countOfSavedArticleVariantsWithKey:(nullable NSString *)key error:(NSError **)error { + if (!key) { + return 0; + } + NSFetchRequest *request = [WMFArticle fetchRequest]; + request.predicate = [NSPredicate predicateWithFormat:@"key == %@ && savedDate != NULL", key]; + NSUInteger count = [self countForFetchRequest:request error:error]; + return count == NSNotFound ? 0 : count; +} + +- (nullable WMFArticle *)savedArticleVariantWithKey:(nullable NSString *)key error:(NSError **)error { + if (!key) { + return nil; + } + NSFetchRequest *request = [WMFArticle fetchRequest]; + request.predicate = [NSPredicate predicateWithFormat:@"key == %@ && savedDate != NULL", key]; + return (WMFArticle *)[[self executeFetchRequest:request error:error] firstObject]; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFArticle+Extensions.swift b/Apps/Wikipedia/WMF Framework/WMFArticle+Extensions.swift new file mode 100644 index 0000000..18b16ec --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFArticle+Extensions.swift @@ -0,0 +1,74 @@ +import Foundation + +extension WMFArticle { + @objc public var capitalizedWikidataDescriptionOrSnippet: String? { + guard let wikidataDescription = capitalizedWikidataDescription, !wikidataDescription.isEmpty else { + return snippet + } + return wikidataDescription + } + + public var hasChangedValuesForCurrentEventThatAffectPreviews: Bool { + let previewKeys: Set = ["wikidataDescription", "snippet", "imageURLString"] + return hasChangedValuesForCurrentEventForKeys(previewKeys) + } + + @objc public var hasChangedValuesForCurrentEventThatAffectSavedArticlesFetch: Bool { + let previewKeys: Set = ["savedDate", "isDownloaded"] + return hasChangedValuesForCurrentEventForKeys(previewKeys) + } + + @objc public var hasChangedValuesForCurrentEventThatAffectSavedState: Bool { + let previewKeys: Set = ["savedDate"] + return hasChangedValuesForCurrentEventForKeys(previewKeys) + } + + public var hasChangedValuesForCurrentEventThatAffectSavedArticlePreviews: Bool { + let previewKeys: Set = ["wikidataDescription", "snippet", "imageURLString", "isDownloaded", "readingLists", "errorCodeNumber"] + return hasChangedValuesForCurrentEventForKeys(previewKeys) + } + + public var namespace: PageNamespace? { + return url?.namespace + } + + public var namespaceAndTitle: (namespace: PageNamespace, title: String)? { + return url?.namespaceAndTitle + } + + @objc public var namespaceNumber: NSNumber? { + guard let namespace = namespace else { + return nil + } + return NSNumber(integerLiteral: namespace.rawValue) + } + + public var isSaved: Bool { + get { + return savedDate != nil + } + set { + savedDate = newValue ? Date() : nil + } + } + + @objc public func feedArticlePreview() -> WMFFeedArticlePreview? { + + var dictionary: [AnyHashable: Any] = [ + "displayTitle": displayTitle as Any, + "displayTitleHTML": displayTitleHTML, + "thumbnailURL": thumbnailURL as Any, + "imageURLString": imageURLString as Any, + "wikidataDescription": wikidataDescription as Any, + "snippet": snippet as Any, + "imageWidth": imageWidth as Any, + "imageHeight": imageHeight as Any + ] + + if let articleURLString = key?.decomposedStringWithCanonicalMapping { + dictionary["articleURL"] = URL(string: articleURLString) + } + + return try? WMFFeedArticlePreview(dictionary: dictionary) + } +} diff --git a/Apps/Wikipedia/WMF Framework/WMFArticle+QuadKey.swift b/Apps/Wikipedia/WMF Framework/WMFArticle+QuadKey.swift new file mode 100644 index 0000000..314aded --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFArticle+QuadKey.swift @@ -0,0 +1,74 @@ +import Foundation +import MapKit + +extension WMFArticle { + public var quadKey: QuadKey? { + get { + guard let signedQuadKey = signedQuadKey else { + return nil + } + let signedQuadKeyInteger = signedQuadKey.int64Value + let quadKey = QuadKey(int64: signedQuadKeyInteger) + return quadKey + } + + set { + guard let newValue = newValue else { + signedQuadKey = nil + return + } + let signedQuadKeyInteger = Int64(quadKey: newValue) + signedQuadKey = NSNumber(value: signedQuadKeyInteger) + } + } + + public var coordinate: CLLocationCoordinate2D? { + get { + guard let quadKey = quadKey else { + return nil + } + let coordinate = QuadKeyCoordinate(quadKey: quadKey) + let latitude = coordinate.latitudePart.latitude + let longitude = coordinate.longitudePart.longitude + return CLLocationCoordinate2DMake(latitude, longitude) + } + + set { + guard let newValue = newValue else { + quadKey = nil + return + } + quadKey = QuadKey(latitude: newValue.latitude, longitude: newValue.longitude) + } + } + + // allows us to keep coordinate optional above + @objc public func update(scalarCoordinate: CLLocationCoordinate2D) { + coordinate = CLLocationCoordinate2DIsValid(scalarCoordinate) ? scalarCoordinate : nil + } + + @objc public var location: CLLocation? { + get { + guard let coordinate = coordinate else { + return nil + } + return CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude) + } + + set { + coordinate = newValue?.coordinate + } + } + + @objc public var mapItem: MKMapItem? { + guard let coord = self.coordinate, + CLLocationCoordinate2DIsValid(coord) else { + return nil + } + + let placemark = MKPlacemark(coordinate: coord, addressDictionary: nil ) + let mapItem = MKMapItem(placemark: placemark) + mapItem.name = self.displayTitle + return mapItem + } +} diff --git a/Apps/Wikipedia/WMF Framework/WMFBlocksKit.swift b/Apps/Wikipedia/WMF Framework/WMFBlocksKit.swift new file mode 100644 index 0000000..8fef19a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFBlocksKit.swift @@ -0,0 +1,169 @@ +import Foundation + +extension NSArray { + @objc public func wmf_map(_ transform: @escaping (Any) -> Any?) -> NSArray { + return map(transform) as NSArray + } + + @objc public func wmf_select(_ transform: @escaping (Any) -> Bool) -> NSArray { + return filter(transform) as NSArray + } + + @objc public func wmf_reject(_ transform: @escaping (Any) -> Bool) -> NSArray { + return filter({ (obj) -> Bool in + return !transform(obj) + }) as NSArray + } + + @objc public func wmf_match(_ matcher: @escaping (Any) -> Bool) -> Any? { + for obj in self { + guard !matcher(obj) else { + return obj + } + } + return nil + } + + @objc public func wmf_reduce(_ initialResult: Any?, withBlock: @escaping (Any?, Any) -> Any?) -> Any? { + return reduce(initialResult, withBlock) + } + + @objc public func wmf_arrayByRecursivelyRemovingNullObjects() -> NSArray { + let mutableSelf = NSMutableArray(capacity: self.count) + for value in self { + if let dict = value as? NSDictionary { + mutableSelf.add(dict.wmf_dictionaryByRecursivelyRemovingNullObjects()) + } else if let array = value as? NSArray { + mutableSelf.add(array.wmf_arrayByRecursivelyRemovingNullObjects()) + } else if let set = value as? NSSet { + mutableSelf.add(set.wmf_setByRecursivelyRemovingNullObjects()) + } else if value as? NSNull != nil { + + } else { + mutableSelf.add(value) + } + } + return mutableSelf + } +} + +extension NSSet { + @objc public func wmf_map(_ transform: @escaping (Any) -> Any?) -> NSSet { + let array = map { (obj) -> Any in + return transform(obj) ?? NSNull() + } + return NSSet(array: array) + } + + @objc public func wmf_select(_ transform: @escaping (Any) -> Bool) -> NSSet { + let array = filter(transform) + return NSSet(array: array) + } + + @objc public func wmf_reject(_ transform: @escaping (Any) -> Bool) -> NSSet { + let array = filter({ (obj) -> Bool in + return !transform(obj) + }) + return NSSet(array: array) + } + + @objc public func wmf_match(_ matcher: @escaping (Any) -> Bool) -> Any? { + for obj in self { + guard !matcher(obj) else { + return obj + } + } + return nil + } + + @objc public func wmf_reduce(_ initialResult: Any?, withBlock: @escaping (Any?, Any) -> Any?) -> Any? { + return reduce(initialResult, withBlock) + } + + @objc public func wmf_setByRecursivelyRemovingNullObjects() -> NSSet { + let mutableSelf = NSMutableSet(capacity: self.count) + for value in self { + if let dict = value as? NSDictionary { + mutableSelf.add(dict.wmf_dictionaryByRecursivelyRemovingNullObjects()) + } else if let array = value as? NSArray { + mutableSelf.add(array.wmf_arrayByRecursivelyRemovingNullObjects()) + } else if let set = value as? NSSet { + mutableSelf.add(set.wmf_setByRecursivelyRemovingNullObjects()) + } else if value as? NSNull != nil { + + } else { + mutableSelf.add(value) + } + } + return mutableSelf + } + +} + +extension NSDictionary { + @objc public func wmf_map(_ transform: @escaping (Any, Any) -> Any?) -> NSDictionary { + guard count > 0 else { + return self + } + let result = NSMutableDictionary(capacity: count) + for (key, value) in self { + result[key] = transform(key, value) ?? NSNull() + } + return result + } + + @objc public func wmf_select(_ transform: @escaping (Any, Any) -> Bool) -> NSDictionary { + guard count > 0 else { + return self + } + let result = NSMutableDictionary(capacity: count) + for (key, value) in self { + guard transform(key, value) else { + continue + } + result[key] = value + } + return result + } + + @objc public func wmf_reject(_ transform: @escaping (Any, Any) -> Bool) -> NSDictionary { + return wmf_select({ (key, value) -> Bool in + return !transform(key, value) + }) + } + + @objc public func wmf_match(_ matcher: @escaping (Any, Any) -> Bool) -> Any? { + for (key, value) in self { + guard !matcher(key, value) else { + return value + } + } + return nil + } + + @objc public func wmf_reduce(_ initialResult: Any?, withBlock: @escaping (Any?, Any, Any) -> Any?) -> Any? { + return reduce(initialResult) { (result, kv) -> Any? in + return withBlock(result, kv.0, kv.1) + } + } + + @objc public func wmf_dictionaryByRecursivelyRemovingNullObjects() -> NSDictionary { + let mutableSelf = NSMutableDictionary(capacity: self.count) + for (key, value) in self { + if let dict = value as? NSDictionary { + mutableSelf[key] = dict.wmf_dictionaryByRecursivelyRemovingNullObjects() + } else if let array = value as? NSArray { + mutableSelf[key] = array.wmf_arrayByRecursivelyRemovingNullObjects() + } else if let set = value as? NSSet { + mutableSelf[key] = set.wmf_setByRecursivelyRemovingNullObjects() + } else if value as? NSNull != nil { + + } else if key as? NSNull != nil { + + } else { + mutableSelf[key] = value + } + } + return mutableSelf + } +} diff --git a/Apps/Wikipedia/WMF Framework/WMFCaptcha.swift b/Apps/Wikipedia/WMF Framework/WMFCaptcha.swift new file mode 100644 index 0000000..3020d77 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFCaptcha.swift @@ -0,0 +1,22 @@ +public class WMFCaptcha: NSObject { + @objc public let captchaID: String + @objc public let captchaURL: URL + @objc public init(captchaID:String, captchaURL:URL) { + self.captchaID = captchaID + self.captchaURL = captchaURL + } + static public func captcha(from requests: [[String : AnyObject]]) -> WMFCaptcha? { + guard + let captchaAuthenticationRequest = requests.first(where: {$0["id"]! as! String == "CaptchaAuthenticationRequest"}), + let fields = captchaAuthenticationRequest["fields"] as? [String : AnyObject], + let captchaId = fields["captchaId"] as? [String : AnyObject], + let captchaInfo = fields["captchaInfo"] as? [String : AnyObject], + let captchaIdValue = captchaId["value"] as? String, + let captchaInfoValue = captchaInfo["value"] as? String, + let captchaURL = URL(string: captchaInfoValue) + else { + return nil + } + return WMFCaptcha(captchaID: captchaIdValue, captchaURL: captchaURL) + } +} diff --git a/Apps/Wikipedia/WMF Framework/WMFCaptchaViewController.swift b/Apps/Wikipedia/WMF Framework/WMFCaptchaViewController.swift new file mode 100644 index 0000000..f5bc9bf --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFCaptchaViewController.swift @@ -0,0 +1,209 @@ +import UIKit + +// Presently it is assumed this view controller will be used only as a +// child view controller of another view controller. + +extension UIStackView { + fileprivate var wmf_isCollapsed: Bool { + get { + for subview in arrangedSubviews { + if !subview.isHidden { + return false + } + } + return true + } + set { + for subview in arrangedSubviews { + subview.isHidden = newValue + } + } + } +} + +@objc public protocol WMFCaptchaViewControllerDelegate { + func captchaReloadPushed(_ sender: AnyObject) + func captchaSolutionChanged(_ sender: AnyObject, solutionText: String?) + func captchaSiteURL() -> URL + func captchaKeyboardReturnKeyTapped() + func captchaHideSubtitle() -> Bool +} + +class WMFCaptchaViewController: UIViewController, UITextFieldDelegate, Themeable { + + @IBOutlet fileprivate var captchaImageView: UIImageView! + @IBOutlet fileprivate var captchaTextFieldTitleLabel: UILabel! + @IBOutlet fileprivate var captchaTextField: ThemeableTextField! + @IBOutlet fileprivate var stackView: UIStackView! + @IBOutlet fileprivate var titleLabel: UILabel! + @IBOutlet fileprivate var subTitleLabel: WMFAuthLinkLabel! + @IBOutlet fileprivate var infoButton: UIButton! + @IBOutlet fileprivate var refreshButton: UIButton! + + @objc public weak var captchaDelegate: WMFCaptchaViewControllerDelegate? + fileprivate let captchaResetter = WMFCaptchaResetter() + + fileprivate var theme = Theme.standard + + @objc var captcha: WMFCaptcha? { + didSet { + guard let captcha = captcha else { + captchaTextField.text = nil + stackView.wmf_isCollapsed = true + return + } + stackView.wmf_isCollapsed = false + captchaTextField.text = "" + refreshImage(for: captcha) + if let captchaDelegate = captchaDelegate { + subTitleLabel.isHidden = captchaDelegate.captchaHideSubtitle() + } + } + } + + @objc var solution:String? { + guard + let captchaSolution = captchaTextField.text, + !captchaSolution.isEmpty + else { + return nil + } + return captchaTextField.text + } + + public func textFieldShouldReturn(_ textField: UITextField) -> Bool { + if textField == captchaTextField { + guard let captchaDelegate = captchaDelegate else { + assertionFailure("Expected delegate not set") + return true + } + captchaDelegate.captchaKeyboardReturnKeyTapped() + } + return true + } + + fileprivate func refreshImage(for captcha: WMFCaptcha) { + guard let fullCaptchaImageURL = fullCaptchaImageURL(from: captcha.captchaURL) else { + assertionFailure("Unable to determine fullCaptchaImageURL") + return + } + captchaImageView.wmf_setImage(with: fullCaptchaImageURL, detectFaces: false, onGPU: false, failure: { (error) in + + }) { + + } + } + + public func captchaTextFieldBecomeFirstResponder() { + // Reminder: captchaTextField is private so this vc maintains control over the captcha solution. + captchaTextField.becomeFirstResponder() + } + + fileprivate func fullCaptchaImageURL(from captchaURL: URL) -> URL? { + guard let components = URLComponents(url: captchaURL, resolvingAgainstBaseURL: false) else { + assertionFailure("Could not extract url components") + return nil + } + return components.url(relativeTo: captchaBaseURL()) + } + + fileprivate func captchaBaseURL() -> URL? { + guard let captchaDelegate = captchaDelegate else { + assertionFailure("Expected delegate not set") + return nil + } + let captchaSiteURL = captchaDelegate.captchaSiteURL() + guard var components = URLComponents(url: captchaSiteURL, resolvingAgainstBaseURL: false) else { + assertionFailure("Could not extract url components") + return nil + } + components.queryItems = nil + guard let url = components.url else { + assertionFailure("Could not extract url") + return nil + } + return url + } + + @IBAction func textFieldDidChange(_ sender: UITextField) { + captchaDelegate?.captchaSolutionChanged(self, solutionText:sender.text) + } + + override var prefersStatusBarHidden: Bool { + return true + } + + override func viewDidLoad() { + super.viewDidLoad() + guard let captchaDelegate = captchaDelegate else { + assertionFailure("Required delegate is unset") + return + } + + assert(stackView.wmf_firstArrangedSubviewWithRequiredNonZeroHeightConstraint() == nil, stackView.wmf_anArrangedSubviewHasRequiredNonZeroHeightConstraintAssertString()) + + captcha = nil + captchaTextFieldTitleLabel.text = WMFLocalizedString("field-captcha-title", value:"Enter the text you see above", comment: "Title for captcha field") + captchaTextField.placeholder = WMFLocalizedString("field-captcha-placeholder", value:"CAPTCHA text", comment: "Placeholder text shown inside captcha field until user taps on it") + titleLabel.text = WMFLocalizedString("account-creation-captcha-title", value:"CAPTCHA security check", comment: "Title for account creation CAPTCHA interface") + + // Reminder: used a label instead of a button for subtitle because of multi-line string issues with UIButton. + subTitleLabel.strings = WMFAuthLinkLabelStrings(dollarSignString: WMFLocalizedString("account-creation-captcha-cannot-see-image", value:"Can't see the image? %1$@", comment: "Text asking the user if they cannot see the captcha image. %1$@ is the message {{msg-wm|Wikipedia-ios-account-creation-captcha-request-account}}"), substitutionString: WMFLocalizedString("account-creation-captcha-request-account", value:"Request account.", comment: "Text for link to 'Request an account' page.")) + subTitleLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(requestAnAccountTapped(_:)))) + + subTitleLabel.isHidden = (captcha == nil) || captchaDelegate.captchaHideSubtitle() + + view.wmf_configureSubviewsForDynamicType() + + apply(theme: theme) + } + + @objc func requestAnAccountTapped(_ recognizer: UITapGestureRecognizer) { + navigate(to: URL(string: "https://en.wikipedia.org/wiki/Wikipedia:Request_an_account")) + } + + @IBAction fileprivate func infoButtonTapped(withSender sender: UIButton) { + navigate(to: URL(string: "https://en.wikipedia.org/wiki/Special:Captcha/help")) + } + + @IBAction fileprivate func refreshButtonTapped(withSender sender: UIButton) { + captchaDelegate?.captchaReloadPushed(self) + + let failure: WMFErrorHandler = {error in } + + WMFAlertManager.sharedInstance.showAlert(WMFLocalizedString("account-creation-captcha-obtaining", value:"Obtaining a new CAPTCHA...", comment: "Alert shown when user wants a new captcha when creating account"), sticky: false, dismissPreviousAlerts: true, tapCallBack: nil) + + self.captchaResetter.resetCaptcha(siteURL: captchaBaseURL()!, success: { result in + DispatchQueue.main.async { + guard let previousCaptcha = self.captcha else { + assertionFailure("If resetting a captcha, expect to have a previous one here") + return + } + + let previousCaptchaURL = previousCaptcha.captchaURL + let previousCaptchaNSURL = previousCaptchaURL as NSURL + + let newCaptchaID = result.index + + // Resetter only fetches captchaID, so use previous captchaURL changing its wpCaptchaId. + let newCaptchaURL = previousCaptchaNSURL.wmf_url(withValue: newCaptchaID, forQueryKey:"wpCaptchaId") + let newCaptcha = WMFCaptcha.init(captchaID: newCaptchaID, captchaURL: newCaptchaURL) + self.captcha = newCaptcha + } + }, failure:failure) + } + + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + + titleLabel.textColor = theme.colors.primaryText + captchaTextFieldTitleLabel.textColor = theme.colors.secondaryText + subTitleLabel.apply(theme: theme) + view.backgroundColor = theme.colors.paperBackground + captchaTextField.apply(theme: theme) + view.tintColor = theme.colors.link + } +} diff --git a/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataClass.h b/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataClass.h new file mode 100644 index 0000000..c52c161 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataClass.h @@ -0,0 +1,14 @@ +#import +#import + +@class NSArray, WMFContentGroup; + +NS_ASSUME_NONNULL_BEGIN + +@interface WMFContent : NSManagedObject + +@end + +NS_ASSUME_NONNULL_END + +#import diff --git a/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataClass.m b/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataClass.m new file mode 100644 index 0000000..d5409de --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataClass.m @@ -0,0 +1,5 @@ +#import "WMFContent+CoreDataClass.h" + +@implementation WMFContent + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataProperties.h b/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataProperties.h new file mode 100644 index 0000000..1c422da --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataProperties.h @@ -0,0 +1,14 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface WMFContent (CoreDataProperties) + ++ (NSFetchRequest *)fetchRequest; + +@property (nullable, nonatomic, retain) id object; +@property (nullable, nonatomic, retain) WMFContentGroup *contentGroup; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataProperties.m b/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataProperties.m new file mode 100644 index 0000000..351b555 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContent+CoreDataProperties.m @@ -0,0 +1,12 @@ +#import + +@implementation WMFContent (CoreDataProperties) + ++ (NSFetchRequest *)fetchRequest { + return [[NSFetchRequest alloc] initWithEntityName:@"WMFContent"]; +} + +@dynamic object; +@dynamic contentGroup; + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataClass.h b/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataClass.h new file mode 100644 index 0000000..9fbac88 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataClass.h @@ -0,0 +1,12 @@ +#import +@class WMFContent; + +NS_ASSUME_NONNULL_BEGIN + +@interface WMFContentGroup : NSManagedObject + +@end + +NS_ASSUME_NONNULL_END + +#import diff --git a/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataClass.m b/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataClass.m new file mode 100644 index 0000000..ebc9b27 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataClass.m @@ -0,0 +1,5 @@ +#import "WMFContentGroup+CoreDataClass.h" + +@implementation WMFContentGroup + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataProperties.h b/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataProperties.h new file mode 100644 index 0000000..efee923 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataProperties.h @@ -0,0 +1,44 @@ +#import +@import CoreLocation; + +NS_ASSUME_NONNULL_BEGIN + +@interface WMFContentGroup (CoreDataProperties) + ++ (NSFetchRequest *)fetchRequest; + +@property (nullable, nonatomic, copy) NSString *key; +@property (nullable, nonatomic, copy) NSDate *date; +@property (nullable, nonatomic, copy) NSDate *midnightUTCDate; +@property (nullable, nonatomic, copy) NSDate *contentMidnightUTCDate; +@property (nullable, nonatomic, copy) NSDate *contentDate; + +@property (nullable, nonatomic, copy) NSString *siteURLString; +@property (nullable, nonatomic, copy) NSString *variant; + +@property (nonatomic) int32_t contentGroupKindInteger; +@property (nonatomic) int16_t contentTypeInteger; + +@property (nonatomic) BOOL isVisible; +@property (nonatomic) BOOL wasDismissed; + +@property (nullable, nonatomic, copy) id contentPreview; + +@property (nonatomic) int32_t dailySortPriority; + +@property (nullable, nonatomic, copy) NSString *articleURLString; +@property (nullable, nonatomic, copy) NSString *featuredContentIdentifier; + +@property (nullable, nonatomic, retain) CLLocation *location; +@property (nullable, nonatomic, retain) CLPlacemark *placemark; + +@property (nullable, nonatomic, retain) NSNumber *countOfFullContent; +@property (nullable, nonatomic, retain) WMFContent *fullContent; + +@property (nonatomic) int16_t undoTypeInteger; + +@property (nullable, nonatomic, copy) NSString *placement; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataProperties.m b/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataProperties.m new file mode 100644 index 0000000..a5e378d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContentGroup+CoreDataProperties.m @@ -0,0 +1,31 @@ +#import "WMFContentGroup+CoreDataProperties.h" + +@implementation WMFContentGroup (CoreDataProperties) + ++ (NSFetchRequest *)fetchRequest { + return [[NSFetchRequest alloc] initWithEntityName:@"WMFContentGroup"]; +} + +@dynamic articleURLString; +@dynamic contentTypeInteger; +@dynamic contentGroupKindInteger; +@dynamic dailySortPriority; +@dynamic date; +@dynamic midnightUTCDate; +@dynamic contentMidnightUTCDate; +@dynamic key; +@dynamic location; +@dynamic placemark; +@dynamic siteURLString; +@dynamic variant; +@dynamic isVisible; +@dynamic wasDismissed; +@dynamic fullContent; +@dynamic contentPreview; +@dynamic featuredContentIdentifier; +@dynamic contentDate; +@dynamic countOfFullContent; +@dynamic undoTypeInteger; +@dynamic placement; + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFContentGroup+Display.swift b/Apps/Wikipedia/WMF Framework/WMFContentGroup+Display.swift new file mode 100644 index 0000000..914a377 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContentGroup+Display.swift @@ -0,0 +1,157 @@ +extension WMFFeedDisplayType { + public func imageWidthCompatibleWithTraitCollection(_ traitCollection: UITraitCollection) -> Int { + switch self { + case .pageWithPreview, .relatedPagesSourceArticle, .random, .continueReading: + return traitCollection.wmf_leadImageWidth + default: + return traitCollection.wmf_nearbyThumbnailWidth + } + } +} + +extension WMFContentGroup { + public func imageURLsCompatibleWithTraitCollection(_ traitCollection: UITraitCollection, dataStore: MWKDataStore, viewSize: CGSize? = nil) -> Set? { + switch contentGroupKind { + case .pictureOfTheDay: + guard let imageInfo = contentPreview as? WMFFeedImage else { + return nil + } + + let fallback: (WMFFeedImage, UITraitCollection) -> URL = { imageInfo, traitCollection in + let imageURL = URL(string: WMFChangeImageSourceURLSizePrefix(imageInfo.imageThumbURL.absoluteString, traitCollection.wmf_leadImageWidth)) ?? imageInfo.imageThumbURL + return imageURL + } + + guard let viewSize = viewSize else { + return [fallback(imageInfo, traitCollection)] + } + + let scaledViewSize = CGSize(width: UIScreen.main.scale * viewSize.width, height: UIScreen.main.scale * viewSize.height) + let imageURL = imageInfo.getImageURL(forWidth: Double(scaledViewSize.width), height: Double(scaledViewSize.height)) ?? + fallback(imageInfo, traitCollection) + return [imageURL] + + case .announcement: + guard let announcement = contentPreview as? WMFAnnouncement else { + return nil + } + guard let imageURL = announcement.imageURL else { + return nil + } + return [imageURL] + default: + let count = countOfPreviewItems + guard count > 0 else { + return nil + } + var imageURLs: Set = [] + imageURLs.reserveCapacity(count) + for index in 0.. URL? { + let displayType = displayTypeForItem(at: index) + var index = index + switch displayType { + case .relatedPagesSourceArticle: + return articleURL + case .relatedPages: + index -= 1 + case .ranked: + guard let content = contentPreview as? [WMFFeedTopReadArticlePreview], content.count > index else { + return nil + } + return content[index].articleURL + default: + break + } + + if let contentURL = contentPreview as? URL { + return contentURL + } + + guard let content = contentPreview as? [URL], content.count > index else { + return nil + } + + return content[index] + } + + public var isSelectable: Bool { + guard undoType == .none else { + return false + } + switch contentGroupKind { + case .announcement, .notification, .theme, .readingList: + return false + default: + return true + } + } + + public var previewArticleKeys: Set { + guard countOfPreviewItems > 0 else { + return [] + } + var articleKeys: Set = [] + articleKeys.reserveCapacity(countOfPreviewItems) + for i in 0...countOfPreviewItems { + guard let key = previewArticleURLForItemAtIndex(i)?.wmf_inMemoryKey else { + continue + } + articleKeys.insert(key) + } + return articleKeys + } +} diff --git a/Apps/Wikipedia/WMF Framework/WMFContentGroup+EventLogging.swift b/Apps/Wikipedia/WMF Framework/WMFContentGroup+EventLogging.swift new file mode 100644 index 0000000..18cac20 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContentGroup+EventLogging.swift @@ -0,0 +1,38 @@ +import Foundation + +@objc public extension WMFContentGroup { + var eventLoggingLabel: EventLoggingLabel? { + switch contentGroupKind { + case .featuredArticle: + return .featuredArticle + case .topRead: + return .topRead + case .onThisDay: + return .onThisDay + case .random: + return .random + case .news: + return .news + case .relatedPages: + return .relatedPages + case .continueReading: + return .continueReading + case .locationPlaceholder: + fallthrough + case .location: + return .location + case .mainPage: + return .mainPage + case .pictureOfTheDay: + return .pictureOfTheDay + case .announcement: + guard let announcement = contentPreview as? WMFAnnouncement else { + return .announcement + } + return announcement.placement == "article" ? .articleAnnouncement : .announcement + default: + return nil + } + } +} + diff --git a/Apps/Wikipedia/WMF Framework/WMFContentGroup+Extensions.h b/Apps/Wikipedia/WMF Framework/WMFContentGroup+Extensions.h new file mode 100644 index 0000000..1199ce7 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContentGroup+Extensions.h @@ -0,0 +1,139 @@ +#import + +@import CoreLocation; + +@class WMFInMemoryURLKey; + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(int16_t, WMFContentType) { + WMFContentTypeUnknown = 0, + WMFContentTypeURL = 1, + WMFContentTypeImage = 2, + WMFContentTypeTopReadPreview = 3, + WMFContentTypeStory = 4, + WMFContentTypeAnnouncement = 5, + WMFContentTypeOnThisDayEvent = 6, + WMFContentTypeNotification = 7, + WMFContentTypeTheme = 8, + WMFContentTypeReadingList = 9 +}; + +typedef NS_ENUM(int32_t, WMFContentGroupKind) { + WMFContentGroupKindUnknown = 0, + WMFContentGroupKindContinueReading = 1, + WMFContentGroupKindMainPage = 2, + WMFContentGroupKindRelatedPages = 3, + WMFContentGroupKindLocation = 4, + WMFContentGroupKindPictureOfTheDay = 5, + WMFContentGroupKindRandom = 6, + WMFContentGroupKindFeaturedArticle = 7, + WMFContentGroupKindTopRead = 8, + WMFContentGroupKindNews = 9, + WMFContentGroupKindNotification = 10, + WMFContentGroupKindAnnouncement = 11, + WMFContentGroupKindLocationPlaceholder = 12, + WMFContentGroupKindOnThisDay = 13, + WMFContentGroupKindTheme = 14, + WMFContentGroupKindReadingList = 15 +}; + +typedef NS_ENUM(int16_t, WMFContentGroupUndoType) { + WMFContentGroupUndoTypeNone = 0, + WMFContentGroupUndoTypeContentGroupKind = 1, + WMFContentGroupUndoTypeContentGroup = 2 +}; + +@interface WMFContentGroup (Extensions) + ++ (nullable NSString *)databaseKeyForURL:(nullable NSURL *)URL; + +@property (nonatomic, assign) WMFContentType contentType; + +@property (nonatomic, assign) WMFContentGroupKind contentGroupKind; + +@property (nonatomic, assign) WMFContentGroupUndoType undoType; + +@property (nonatomic, strong, nullable) NSURL *URL; +@property (nonatomic, strong, nullable) NSURL *articleURL; +@property (nonatomic, strong, nullable) NSURL *siteURL; + +@property (nonatomic, readonly, nullable) WMFInMemoryURLKey *inMemoryKey; + +- (void)updateKey; //Sets key property based on content group kind +- (void)updateContentType; +- (void)updateDailySortPriorityWithSortOrderByContentLanguageCode:(nullable NSDictionary *)sortOrderByContentLanguageCode; + ++ (nullable NSURL *)mainPageURLForSiteURL:(NSURL *)URL; ++ (nullable NSURL *)continueReadingContentGroupURLForArticleURL:(NSURL *)articleURL; ++ (nullable NSURL *)relatedPagesContentGroupURLForArticleURL:(NSURL *)articleURL; ++ (nullable NSURL *)articleURLForRelatedPagesContentGroupURL:(nullable NSURL *)url; ++ (nullable NSURL *)announcementURLForSiteURL:(NSURL *)siteURL identifier:(NSString *)identifier; ++ (nullable NSURL *)randomContentGroupURLForSiteURL:(NSURL *)url midnightUTCDate:(NSDate *)midnightUTCDate; ++ (nullable NSURL *)onThisDayContentGroupURLForSiteURL:(NSURL *)url midnightUTCDate:(NSDate *)midnightUTCDate; ++ (nullable NSURL *)locationContentGroupURLForLocation:(CLLocation *)location languageVariantCode:(NSString *)languageVariantCode; ++ (nullable NSURL *)locationPlaceholderContentGroupURLWithLanguageVariantCode:(NSString *)languageVariantCode; ++ (nullable NSURL *)notificationContentGroupURLWithLanguageVariantCode:(NSString *)languageVariantCode; ++ (nullable NSURL *)themeContentGroupURLWithLanguageVariantCode:(NSString *)languageVariantCode; ++ (nullable NSURL *)readingListContentGroupURLWithLanguageVariantCode:(NSString *)languageVariantCode; + +- (BOOL)isForLocalDate:(NSDate *)date; //date is a date in the user's time zone +@property (nonatomic, readonly) BOOL isForToday; //is for today in the user's time zone +@property (nonatomic, readonly) BOOL isRTL; //content is in an RTL language + +// Utilizes featuredContentIdentifier for storage so can't be set along with featuredContentIdentifier +@property (nonatomic) NSInteger featuredContentIndex; + +- (void)setFullContentObject:(id)fullContentObject; // will automatically create or update fullContent relationship +- (void)updateContentPreviewWithContent:(id)content; + +- (void)updateVisibilityForUserIsLoggedIn:(BOOL)isLoggedIn; +- (void)markDismissed; + +@end + +@interface NSManagedObjectContext (WMFContentGroup) + +- (nullable WMFContentGroup *)createGroupOfKind:(WMFContentGroupKind)kind forDate:(NSDate *)date withSiteURL:(nullable NSURL *)siteURL associatedContent:(nullable id)associatedContent; + +- (nullable WMFContentGroup *)fetchOrCreateGroupForURL:(NSURL *)URL ofKind:(WMFContentGroupKind)kind forDate:(NSDate *)date withSiteURL:(nullable NSURL *)siteURL associatedContent:(nullable id)associatedContent customizationBlock:(nullable void (^)(WMFContentGroup *group))customizationBlock; + +- (nullable WMFContentGroup *)createGroupOfKind:(WMFContentGroupKind)kind forDate:(NSDate *)date withSiteURL:(nullable NSURL *)siteURL associatedContent:(nullable id)associatedContent customizationBlock:(nullable void (^)(WMFContentGroup *group))customizationBlock; + +- (NSArray *)contentGroupsOfKind:(WMFContentGroupKind)kind; + +- (void)removeContentGroup:(WMFContentGroup *)group; + +- (void)removeAllContentGroupsOfKind:(WMFContentGroupKind)kind; + +- (nullable WMFContentGroup *)contentGroupForURL:(NSURL *)url; + +- (void)enumerateContentGroupsWithBlock:(void (^)(WMFContentGroup *_Nonnull group, BOOL *stop))block; + +- (void)enumerateContentGroupsOfKind:(WMFContentGroupKind)kind withBlock:(void (^)(WMFContentGroup *_Nonnull group, BOOL *stop))block; + +- (void)enumerateContentGroupsOfKind:(WMFContentGroupKind)kind sortedByKey:(NSString *)key ascending:(BOOL)ascending withBlock:(void (^)(WMFContentGroup *_Nonnull group, BOOL *stop))block; + +- (nullable WMFContentGroup *)newestVisibleGroupOfKind:(WMFContentGroupKind)kind; + +- (nullable WMFContentGroup *)newestVisibleGroupOfKind:(WMFContentGroupKind)kind forSiteURL:(nullable NSURL *)siteURL; + +- (nullable WMFContentGroup *)newestVisibleGroupOfKind:(WMFContentGroupKind)kind withPredicate:(nullable NSPredicate *)predicate; + +- (nullable WMFContentGroup *)newestGroupOfKind:(WMFContentGroupKind)kind; + +- (nullable WMFContentGroup *)newestGroupOfKind:(WMFContentGroupKind)kind forSiteURL:(nullable NSURL *)siteURL; + +- (nullable WMFContentGroup *)groupOfKind:(WMFContentGroupKind)kind forDate:(NSDate *)date; + +- (nullable WMFContentGroup *)groupOfKind:(WMFContentGroupKind)kind forDate:(NSDate *)date siteURL:(NSURL *)siteURL; + +- (nullable NSArray *)groupsOfKind:(WMFContentGroupKind)kind forDate:(NSDate *)date; + +- (nullable NSArray *)orderedGroupsOfKind:(WMFContentGroupKind)kind withPredicate:(nullable NSPredicate *)predicate; + +- (nullable WMFContentGroup *)locationContentGroupWithSiteURL:(nullable NSURL *)siteURL withinMeters:(CLLocationDistance)meters ofLocation:(CLLocation *)location; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/WMFContentGroup+Extensions.m b/Apps/Wikipedia/WMF Framework/WMFContentGroup+Extensions.m new file mode 100644 index 0000000..27ee308 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFContentGroup+Extensions.m @@ -0,0 +1,948 @@ +#import +#import +#import "WMFAnnouncement.h" +@import UIKit; +#import +#import +#import +#import +#import +#import +#import + +@implementation WMFContentGroup (Extensions) + +- (nullable NSURL *)URL { + NSString *key = self.key; + if (!key) { + return nil; + } + NSURL *URL = [NSURL URLWithString:key]; + URL.wmf_languageVariantCode = self.variant; + return URL; +} + +- (void)setURL:(NSURL *)URL { + // See comment for -createGroupForURL:ofKind:forDate:withSiteURL:associatedContent: for details about this assertion + // Assert that URL is nil OR (both variant values are nil OR are equal) + NSAssert(!URL || ((URL.wmf_languageVariantCode == nil && self.variant == nil) || [URL.wmf_languageVariantCode isEqualToString:self.variant]), @"Incoming URL value should have same language variant as WMFContentGroup instance"); + self.key = URL.absoluteString.precomposedStringWithCanonicalMapping; +} + ++ (nullable NSString *)databaseKeyForURL:(nullable NSURL *)URL { + NSParameterAssert(URL); + return [[URL absoluteString] precomposedStringWithCanonicalMapping]; +} + +- (void)updateKey { + NSURL *URL = nil; + switch (self.contentGroupKind) { + case WMFContentGroupKindUnknown: + assert(false); + break; + case WMFContentGroupKindContinueReading: + URL = [WMFContentGroup continueReadingContentGroupURLForArticleURL:self.articleURL]; + break; + case WMFContentGroupKindMainPage: + URL = [WMFContentGroup mainPageURLForSiteURL:self.siteURL]; + break; + case WMFContentGroupKindRelatedPages: + URL = [WMFContentGroup relatedPagesContentGroupURLForArticleURL:self.articleURL]; + break; + case WMFContentGroupKindLocation: + URL = [WMFContentGroup locationContentGroupURLForLocation:self.location languageVariantCode:self.siteURL.wmf_languageVariantCode]; + break; + case WMFContentGroupKindLocationPlaceholder: + URL = [WMFContentGroup locationPlaceholderContentGroupURLWithLanguageVariantCode:self.siteURL.wmf_languageVariantCode]; + break; + case WMFContentGroupKindPictureOfTheDay: + URL = [WMFContentGroup pictureOfTheDayContentGroupURLForSiteURL:self.siteURL midnightUTCDate:self.midnightUTCDate]; + break; + case WMFContentGroupKindRandom: + URL = [WMFContentGroup randomContentGroupURLForSiteURL:self.siteURL midnightUTCDate:self.midnightUTCDate]; + break; + case WMFContentGroupKindFeaturedArticle: + URL = [WMFContentGroup featuredArticleContentGroupURLForSiteURL:self.siteURL midnightUTCDate:self.midnightUTCDate]; + break; + case WMFContentGroupKindTopRead: + URL = [WMFContentGroup topReadContentGroupURLForSiteURL:self.siteURL midnightUTCDate:self.midnightUTCDate]; + break; + case WMFContentGroupKindNews: + URL = [WMFContentGroup newsContentGroupURLForSiteURL:self.siteURL midnightUTCDate:self.midnightUTCDate]; + break; + case WMFContentGroupKindOnThisDay: + URL = [WMFContentGroup onThisDayContentGroupURLForSiteURL:self.siteURL midnightUTCDate:self.midnightUTCDate]; + break; + case WMFContentGroupKindNotification: + URL = [WMFContentGroup notificationContentGroupURLWithLanguageVariantCode:self.siteURL.wmf_languageVariantCode]; + break; + case WMFContentGroupKindTheme: + URL = [WMFContentGroup themeContentGroupURLWithLanguageVariantCode:self.siteURL.wmf_languageVariantCode]; + break; + case WMFContentGroupKindReadingList: + URL = [WMFContentGroup readingListContentGroupURLWithLanguageVariantCode:self.siteURL.wmf_languageVariantCode]; + break; + case WMFContentGroupKindAnnouncement: + URL = [WMFContentGroup announcementURLForSiteURL:self.siteURL identifier:[(WMFAnnouncement *)self.contentPreview identifier]]; + default: + break; + } + assert(URL); + self.key = [WMFContentGroup databaseKeyForURL:URL]; +} + +- (void)updateContentType { + switch (self.contentGroupKind) { + case WMFContentGroupKindPictureOfTheDay: + self.contentType = WMFContentTypeImage; + break; + case WMFContentGroupKindTopRead: + self.contentType = WMFContentTypeTopReadPreview; + break; + case WMFContentGroupKindNews: + self.contentType = WMFContentTypeStory; + break; + case WMFContentGroupKindOnThisDay: + self.contentType = WMFContentTypeOnThisDayEvent; + break; + case WMFContentGroupKindUnknown: + assert(false); + self.contentType = WMFContentTypeUnknown; + break; + case WMFContentGroupKindAnnouncement: + self.contentType = WMFContentTypeAnnouncement; + break; + case WMFContentGroupKindNotification: + self.contentType = WMFContentTypeNotification; + break; + case WMFContentGroupKindTheme: + self.contentType = WMFContentTypeTheme; + break; + case WMFContentGroupKindReadingList: + self.contentType = WMFContentTypeReadingList; + break; + case WMFContentGroupKindContinueReading: + case WMFContentGroupKindMainPage: + case WMFContentGroupKindRelatedPages: + case WMFContentGroupKindLocation: + case WMFContentGroupKindLocationPlaceholder: + case WMFContentGroupKindRandom: + case WMFContentGroupKindFeaturedArticle: + default: + self.contentType = WMFContentTypeURL; + break; + } +} + + + +- (void)updateDailySortPriorityWithSortOrderByContentLanguageCode:(nullable NSDictionary *)sortOrderByContentLanguageCode { + + NSNumber *contentLanguageSortOrderNumber = nil; + NSString *contentLanguageCode = self.siteURL.wmf_contentLanguageCode; + + if (contentLanguageCode) { + contentLanguageSortOrderNumber = sortOrderByContentLanguageCode[contentLanguageCode]; + } + + NSInteger maxSortOrderByKind = 14; + int32_t contentLanguageSortOrder = (int32_t)(maxSortOrderByKind * [contentLanguageSortOrderNumber integerValue]); + int32_t updatedDailySortPriority = 0; + + switch (self.contentGroupKind) { + case WMFContentGroupKindUnknown: + break; + case WMFContentGroupKindAnnouncement: + updatedDailySortPriority = -2; + break; + case WMFContentGroupKindContinueReading: + updatedDailySortPriority = 0; + break; + case WMFContentGroupKindRelatedPages: + updatedDailySortPriority = 1; + break; + case WMFContentGroupKindReadingList: + updatedDailySortPriority = contentLanguageSortOrder + 2; + break; + case WMFContentGroupKindTheme: + updatedDailySortPriority = contentLanguageSortOrder + 3; + break; + case WMFContentGroupKindFeaturedArticle: + updatedDailySortPriority = contentLanguageSortOrder + 4; + break; + case WMFContentGroupKindTopRead: + updatedDailySortPriority = contentLanguageSortOrder + 5; + break; + case WMFContentGroupKindNews: + updatedDailySortPriority = contentLanguageSortOrder + 6; + break; + case WMFContentGroupKindNotification: + updatedDailySortPriority = -1; + break; + case WMFContentGroupKindPictureOfTheDay: + updatedDailySortPriority = 8; + break; + case WMFContentGroupKindOnThisDay: + updatedDailySortPriority = contentLanguageSortOrder + 9; + break; + case WMFContentGroupKindLocationPlaceholder: + updatedDailySortPriority = contentLanguageSortOrder + 10; + break; + case WMFContentGroupKindLocation: + updatedDailySortPriority = contentLanguageSortOrder + 11; + break; + case WMFContentGroupKindRandom: + updatedDailySortPriority = contentLanguageSortOrder + 12; + break; + case WMFContentGroupKindMainPage: + updatedDailySortPriority = contentLanguageSortOrder + 13; + break; + default: + break; + } + + if (self.dailySortPriority == updatedDailySortPriority) { + return; + } + + self.dailySortPriority = updatedDailySortPriority; +} + +- (WMFContentType)contentType { + return (WMFContentType)self.contentTypeInteger; +} + +- (void)setContentType:(WMFContentType)contentType { + self.contentTypeInteger = contentType; +} + +- (WMFContentGroupKind)contentGroupKind { + return (WMFContentGroupKind)self.contentGroupKindInteger; +} + +- (void)setContentGroupKind:(WMFContentGroupKind)contentGroupKind { + self.contentGroupKindInteger = contentGroupKind; +} + +- (WMFContentGroupUndoType)undoType { + return (WMFContentGroupUndoType)self.undoTypeInteger; +} + +- (void)setUndoType:(WMFContentGroupUndoType)undoType { + self.undoTypeInteger = undoType; +} + +- (nullable NSURL *)articleURL { + NSURL *articleURL = [NSURL URLWithString:self.articleURLString]; + articleURL.wmf_languageVariantCode = self.variant; + return articleURL; +} + +- (void)setArticleURL:(nullable NSURL *)articleURL { + // See comment for -createGroupForURL:ofKind:forDate:withSiteURL:associatedContent: for details about this assertion + // Assert that articleURL is nil OR (both variant values are nil OR are equal) + NSAssert(!articleURL || ((articleURL.wmf_languageVariantCode == nil && self.variant == nil) || [articleURL.wmf_languageVariantCode isEqualToString:self.variant]), @"Incoming articleURL value should have same language variant as WMFContentGroup instance"); + self.articleURLString = articleURL.absoluteString; +} + +- (nullable NSURL *)siteURL { + NSURL *siteURL = [NSURL URLWithString:self.siteURLString]; + siteURL.wmf_languageVariantCode = self.variant; + return siteURL; +} + +- (void)setSiteURL:(nullable NSURL *)siteURL { + // See comment for -createGroupForURL:ofKind:forDate:withSiteURL:associatedContent: for details about this assertion + // Assert that siteURL is nil OR (both variant values are nil OR are equal) + NSAssert(!siteURL || ((siteURL.wmf_languageVariantCode == nil && self.variant == nil) || [siteURL.wmf_languageVariantCode isEqualToString:self.variant]), @"Incoming siteURL value should have same language variant as WMFContentGroup instance"); + self.siteURLString = siteURL.wmf_databaseKey; +} + +- (nullable WMFInMemoryURLKey *)inMemoryKey { + return self.URL.wmf_inMemoryKey; +} + +- (void)setFullContentObject:(NSObject *)fullContentObject { + NSManagedObjectContext *moc = self.managedObjectContext; + NSAssert(moc != nil, @"nil moc"); + if (!moc) { + return; + } + if ([fullContentObject isKindOfClass:[NSArray class]] || [fullContentObject isKindOfClass:[NSSet class]]) { + self.countOfFullContent = @([(id)fullContentObject count]); + } + WMFContent *fullContent = self.fullContent; + if (!fullContentObject) { + if (fullContent) { + [moc deleteObject:fullContent]; + self.fullContent = nil; + return; + } + return; + } + + if (!fullContent) { + fullContent = (WMFContent *)[NSEntityDescription insertNewObjectForEntityForName:@"WMFContent" inManagedObjectContext:moc]; + self.fullContent = fullContent; + } + fullContent.object = fullContentObject; +} + +- (NSInteger)featuredContentIndex { + if (self.featuredContentIdentifier == nil) { + return NSNotFound; + } + return self.featuredContentIdentifier.integerValue; +} + +- (void)setFeaturedContentIndex:(NSInteger)index { + if (index == NSNotFound) { + self.featuredContentIdentifier = nil; + } else { + self.featuredContentIdentifier = [NSString stringWithFormat:@"%lli", (long long)index]; + } +} + +- (void)updateContentPreviewWithContent:(id)content { + if (!content) { + return; + } + NSInteger contentLimit = 3; + NSArray *contentArray = nil; + if ([content isKindOfClass:[NSArray class]]) { + contentArray = (NSArray *)content; + } + switch (self.contentGroupKind) { + case WMFContentGroupKindOnThisDay: { + NSInteger featuredEventIndex = self.featuredContentIndex; + if (featuredEventIndex >= 0 && featuredEventIndex < contentArray.count) { + NSInteger startIndex = featuredEventIndex; + NSInteger length = startIndex + 1 < contentArray.count ? 2 : 1; + NSRange range = NSMakeRange(startIndex, length); + // If we have only the last item but there's more than one, move back one so we get 2 total. + // Only do so in this specific case so we usually have the `featuredContent` first (it's chosen based on a score weighing its various properties). + BOOL shouldBackUpByOne = range.length == 1 && startIndex > 0 && contentArray.count > 1; + if (shouldBackUpByOne) { + range = NSMakeRange(startIndex - 1, 2); + } + self.contentPreview = [contentArray subarrayWithRange:range]; + } + } break; + case WMFContentGroupKindTopRead: + contentLimit = 5; + case WMFContentGroupKindRelatedPages: + case WMFContentGroupKindLocation: { + if (contentArray.count > contentLimit) { + self.contentPreview = [contentArray subarrayWithRange:NSMakeRange(0, contentLimit)]; + } else if (contentArray.count > 0) { + self.contentPreview = contentArray; + } + + } break; + case WMFContentGroupKindMainPage: + case WMFContentGroupKindNotification: + case WMFContentGroupKindLocationPlaceholder: + case WMFContentGroupKindPictureOfTheDay: + case WMFContentGroupKindRandom: + case WMFContentGroupKindFeaturedArticle: + case WMFContentGroupKindTheme: + case WMFContentGroupKindReadingList: + case WMFContentGroupKindAnnouncement: + case WMFContentGroupKindContinueReading: + case WMFContentGroupKindNews: + case WMFContentGroupKindUnknown: + default: { + id firstObject = contentArray.firstObject ?: content; + if (firstObject) { + self.contentPreview = firstObject; + } + } break; + } +} + ++ (NSURL *)baseURL { + NSURL *URL = [NSURL URLWithString:@"wikipedia://content/"]; + return URL; +} + ++ (nullable NSURL *)mainPageURLForSiteURL:(NSURL *)URL { + NSString *language = URL.wmf_languageCode; + NSString *domain = URL.wmf_domain; + NSParameterAssert(domain); + NSParameterAssert(language); + if (!domain || !language) { + return nil; + } + + NSURL *theURL = [[self baseURL] URLByAppendingPathComponent:@"main-page"]; + theURL = [theURL URLByAppendingPathComponent:domain]; + theURL = [theURL URLByAppendingPathComponent:language]; + theURL.wmf_languageVariantCode = URL.wmf_languageVariantCode; + return theURL; +} + ++ (nullable NSURL *)continueReadingContentGroupURLForArticleURL:(NSURL *)url { + NSParameterAssert(url); + NSString *title = url.wmf_title; + NSString *domain = url.wmf_domain; + NSString *language = url.wmf_languageCode; + NSParameterAssert(title); + NSParameterAssert(domain); + NSParameterAssert(language); + if (!title || !domain || !language) { + return nil; + } + NSURLComponents *components = [NSURLComponents componentsWithURL:[self baseURL] resolvingAgainstBaseURL:NO]; + NSString *encodedTitle = [title stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet wmf_encodeURIComponentAllowedCharacterSet]]; + NSString *path = [NSString pathWithComponents:@[@"/continue-reading", domain, language, encodedTitle]]; + components.percentEncodedPath = path; + return [components wmf_URLWithLanguageVariantCode:url.wmf_languageVariantCode]; +} + ++ (nullable NSURL *)relatedPagesContentGroupURLForArticleURL:(NSURL *)url { + NSParameterAssert(url); + NSString *title = url.wmf_title; + NSString *domain = url.wmf_domain; + NSString *language = url.wmf_languageCode; + NSParameterAssert(title); + NSParameterAssert(domain); + NSParameterAssert(language); + if (!title || !domain || !language) { + return nil; + } + NSURLComponents *components = [NSURLComponents componentsWithURL:[self baseURL] resolvingAgainstBaseURL:NO]; + NSString *encodedTitle = [title stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet wmf_encodeURIComponentAllowedCharacterSet]]; + NSString *path = [NSString pathWithComponents:@[@"/related-pages", domain, language, encodedTitle]]; + components.percentEncodedPath = path; + return [components wmf_URLWithLanguageVariantCode:url.wmf_languageVariantCode]; +} + ++ (nullable NSURL *)articleURLForRelatedPagesContentGroupURL:(nullable NSURL *)url { + if (!url) { + return nil; + } + NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + NSArray *pathComponents = components.percentEncodedPath.pathComponents; + if (pathComponents.count < 5) { + return nil; + } + NSString *domain = pathComponents[2]; + NSString *language = pathComponents[3]; + NSString *title = [pathComponents[4] stringByRemovingPercentEncoding]; + NSURL *theURL = [NSURL wmf_URLWithDomain:domain languageCode:language title:title fragment:nil]; + theURL.wmf_languageVariantCode = url.wmf_languageVariantCode; + return theURL; +} + ++ (nullable NSURL *)locationContentGroupURLForLocation:(CLLocation *)location languageVariantCode:(NSString *)languageVariantCode { + NSURL *url = [[self baseURL] URLByAppendingPathComponent:@"nearby"]; + url = [url URLByAppendingPathComponent:[NSString stringWithFormat:@"%.6f/%.6f", location.coordinate.latitude, location.coordinate.longitude]]; + url.wmf_languageVariantCode = languageVariantCode; + return url; +} + ++ (nullable NSURL *)locationPlaceholderContentGroupURLWithLanguageVariantCode:(NSString *)languageVariantCode { + NSURL *url = [[self baseURL] URLByAppendingPathComponent:@"nearby-placeholder"]; + url.wmf_languageVariantCode = languageVariantCode; + return url; +} + ++ (nullable NSURL *)contentGroupURLForSiteURL:(NSURL *)siteURL groupKindString:(NSString *)groupKindString { + NSString *language = siteURL.wmf_languageCode; + NSString *domain = siteURL.wmf_domain; + NSParameterAssert(domain); + NSParameterAssert(language); + if (!domain || !language) { + return nil; + } + + NSURL *urlKey = [[self baseURL] URLByAppendingPathComponent:groupKindString]; + urlKey = [urlKey URLByAppendingPathComponent:domain]; + urlKey = [urlKey URLByAppendingPathComponent:language]; + urlKey.wmf_languageVariantCode = siteURL.wmf_languageVariantCode; + return urlKey; +} + ++ (nullable NSURL *)contentGroupURLForSiteURL:(NSURL *)siteURL midnightUTCDate:(NSDate *)midnightUTCDate groupKindString:(NSString *)groupKindString { + NSURL *urlKey = [self contentGroupURLForSiteURL:siteURL groupKindString:groupKindString]; + urlKey = [urlKey URLByAppendingPathComponent:[[NSDateFormatter wmf_englishUTCSlashDelimitedYearMonthDayFormatter] stringFromDate:midnightUTCDate]]; + urlKey.wmf_languageVariantCode = siteURL.wmf_languageVariantCode; + return urlKey; +} + ++ (nullable NSURL *)pictureOfTheDayContentGroupURLForSiteURL:(NSURL *)url midnightUTCDate:(NSDate *)midnightUTCDate { + return [self contentGroupURLForSiteURL:url midnightUTCDate:midnightUTCDate groupKindString:@"picture-of-the-day"]; +} + ++ (nullable NSURL *)randomContentGroupURLForSiteURL:(NSURL *)url midnightUTCDate:(NSDate *)midnightUTCDate { + return [self contentGroupURLForSiteURL:url midnightUTCDate:midnightUTCDate groupKindString:@"random"]; +} + ++ (nullable NSURL *)featuredArticleContentGroupURLForSiteURL:(NSURL *)url midnightUTCDate:(NSDate *)midnightUTCDate { + return [self contentGroupURLForSiteURL:url midnightUTCDate:midnightUTCDate groupKindString:@"featured-article"]; +} + ++ (nullable NSURL *)topReadContentGroupURLForSiteURL:(NSURL *)url midnightUTCDate:(NSDate *)midnightUTCDate { + return [self contentGroupURLForSiteURL:url midnightUTCDate:midnightUTCDate groupKindString:@"top-read"]; +} + ++ (nullable NSURL *)newsContentGroupURLForSiteURL:(NSURL *)url midnightUTCDate:(NSDate *)midnightUTCDate { + return [self contentGroupURLForSiteURL:url midnightUTCDate:midnightUTCDate groupKindString:@"news"]; +} + ++ (nullable NSURL *)onThisDayContentGroupURLForSiteURL:(NSURL *)url midnightUTCDate:(NSDate *)midnightUTCDate { + return [self contentGroupURLForSiteURL:url midnightUTCDate:midnightUTCDate groupKindString:@"on-this-day"]; +} + ++ (nullable NSURL *)notificationContentGroupURLWithLanguageVariantCode:(NSString *)languageVariantCode { + NSURL *URL = [[self baseURL] URLByAppendingPathComponent:@"notification"]; + URL.wmf_languageVariantCode = languageVariantCode; + return URL; +} + ++ (nullable NSURL *)themeContentGroupURLWithLanguageVariantCode:(NSString *)languageVariantCode { + NSURL *URL = [[self baseURL] URLByAppendingPathComponent:@"theme"]; + URL.wmf_languageVariantCode = languageVariantCode; + return URL; +} + ++ (nullable NSURL *)readingListContentGroupURLWithLanguageVariantCode:(NSString *)languageVariantCode { + NSURL *URL = [[self baseURL] URLByAppendingPathComponent:@"reading-list"]; + URL.wmf_languageVariantCode = languageVariantCode; + return URL; +} + ++ (nullable NSURL *)announcementURLForSiteURL:(NSURL *)siteURL identifier:(NSString *)identifier { + NSURL *URL = [[self contentGroupURLForSiteURL:siteURL groupKindString:@"announcement"] URLByAppendingPathComponent:identifier]; + URL.wmf_languageVariantCode = siteURL.wmf_languageVariantCode; + return URL; +} + +- (BOOL)isForLocalDate:(NSDate *)date { + return [self.midnightUTCDate wmf_UTCDateIsSameDateAsLocalDate:date]; +} + +- (BOOL)isForToday { + return [self.midnightUTCDate wmf_UTCDateIsTodayLocal]; +} + +- (BOOL)isRTL { + return [MWKLanguageLinkController isLanguageRTLForContentLanguageCode:self.siteURL.wmf_contentLanguageCode]; +} + +- (void)markDismissed { + self.wasDismissed = YES; +} + +- (void)updateVisibilityForUserIsLoggedIn:(BOOL)isLoggedIn { + if (self.wasDismissed) { + if (self.isVisible) { + self.isVisible = NO; + } + return; + } + + if (self.contentType != WMFContentTypeAnnouncement) { + return; + } + + dispatch_block_t markInvisible = ^{ + if (self.isVisible) { + self.isVisible = NO; + } + }; + + WMFAnnouncement *announcement = (WMFAnnouncement *)self.contentPreview; + if (![announcement isKindOfClass:[WMFAnnouncement class]]) { + markInvisible(); + return; + } + + if (announcement.beta.boolValue) { // ignore beta announcements + markInvisible(); + return; + } + + if (announcement.loggedIn && announcement.loggedIn.boolValue != isLoggedIn) { + markInvisible(); + return; + } + + if (announcement.readingListSyncEnabled) { // ignore reading list announcements, regardless of true or false + markInvisible(); + return; + } + + if (!announcement.startTime || !announcement.endTime) { + if (self.isVisible) { + self.isVisible = NO; + } + return; + } + + NSDate *now = [NSDate date]; + if ([now timeIntervalSinceDate:announcement.startTime] > 0 && [announcement.endTime timeIntervalSinceDate:now] > 0) { + if (!self.isVisible) { + self.isVisible = YES; + } + } else { + if (self.isVisible) { + self.isVisible = NO; + } + } +} +@end + +@implementation NSManagedObjectContext (WMFContentGroup) + +- (void)enumerateContentGroupsWithBlock:(void (^)(WMFContentGroup *_Nonnull section, BOOL *stop))block { + if (!block) { + return; + } + NSFetchRequest *fetchRequest = [WMFContentGroup fetchRequest]; + NSError *fetchError = nil; + NSArray *contentGroups = [self executeFetchRequest:fetchRequest error:&fetchError]; + if (fetchError) { + DDLogError(@"Error fetching content groups: %@", fetchError); + return; + } + [contentGroups enumerateObjectsUsingBlock:^(WMFContentGroup *_Nonnull section, NSUInteger idx, BOOL *_Nonnull stop) { + block(section, stop); + }]; +} + +- (NSArray *)contentGroupsOfKind:(WMFContentGroupKind)kind sortedByDescriptors:(nullable NSArray *)sortDescriptors { + NSFetchRequest *fetchRequest = [WMFContentGroup fetchRequest]; + fetchRequest.predicate = [NSPredicate predicateWithFormat:@"contentGroupKindInteger == %@", @(kind)]; + fetchRequest.sortDescriptors = sortDescriptors; + NSError *fetchError = nil; + NSArray *contentGroups = [self executeFetchRequest:fetchRequest error:&fetchError]; + if (fetchError || !contentGroups) { + DDLogError(@"Error fetching content groups: %@", fetchError); + return @[]; + } + return contentGroups; +} + +- (NSArray *)contentGroupsOfKind:(WMFContentGroupKind)kind sortedByKey:(NSString *)sortKey ascending:(BOOL)ascending { + return [self contentGroupsOfKind:kind sortedByDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:sortKey ascending:ascending]]]; +} + +- (NSArray *)contentGroupsOfKind:(WMFContentGroupKind)kind { + return [self contentGroupsOfKind:kind sortedByDescriptors:nil]; +} + +- (void)enumerateContentGroupsOfKind:(WMFContentGroupKind)kind sortedByKey:(NSString *)key ascending:(BOOL)ascending withBlock:(void (^)(WMFContentGroup *_Nonnull group, BOOL *stop))block { + if (!block) { + return; + } + NSArray *contentGroups = [self contentGroupsOfKind:kind sortedByKey:key ascending:ascending]; + [contentGroups enumerateObjectsUsingBlock:^(WMFContentGroup *_Nonnull section, NSUInteger idx, BOOL *_Nonnull stop) { + block(section, stop); + }]; +} + +- (void)enumerateContentGroupsOfKind:(WMFContentGroupKind)kind withBlock:(void (^)(WMFContentGroup *_Nonnull group, BOOL *stop))block { + if (!block) { + return; + } + NSArray *contentGroups = [self contentGroupsOfKind:kind]; + [contentGroups enumerateObjectsUsingBlock:^(WMFContentGroup *_Nonnull section, NSUInteger idx, BOOL *_Nonnull stop) { + block(section, stop); + }]; +} + +- (nullable WMFContentGroup *)contentGroupForURL:(NSURL *)URL { + NSParameterAssert(URL); + if (!URL) { + return nil; + } + + NSString *key = [WMFContentGroup databaseKeyForURL:URL]; + if (!key) { + return nil; + } + + NSString *variant = URL.wmf_languageVariantCode; + + NSFetchRequest *fetchRequest = [WMFContentGroup fetchRequest]; + fetchRequest.predicate = [NSPredicate predicateWithFormat:@"key == %@ && variant == %@", key, variant]; + fetchRequest.fetchLimit = 1; + NSError *fetchError = nil; + NSArray *contentGroups = [self executeFetchRequest:fetchRequest error:&fetchError]; + if (fetchError) { + DDLogError(@"Error fetching content groups: %@", fetchError); + return nil; + } + return [contentGroups firstObject]; +} + +- (nullable WMFContentGroup *)newestGroupOfKind:(WMFContentGroupKind)kind requireIsVisible:(BOOL)isVisibleRequired { + return [self newestGroupOfKind:kind withPredicate:nil requireIsVisible:isVisibleRequired]; +} + +- (nullable WMFContentGroup *)newestGroupWithPredicate:(nullable NSPredicate *)predicate { + NSFetchRequest *fetchRequest = [WMFContentGroup fetchRequest]; + fetchRequest.predicate = predicate; + fetchRequest.fetchLimit = 1; + fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"midnightUTCDate" ascending:NO], [NSSortDescriptor sortDescriptorWithKey:@"dailySortPriority" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:@"date" ascending:NO]]; + NSError *fetchError = nil; + NSArray *contentGroups = [self executeFetchRequest:fetchRequest error:&fetchError]; + if (fetchError) { + DDLogError(@"Error fetching content groups: %@", fetchError); + return nil; + } + return [contentGroups firstObject]; +} + +- (nullable WMFContentGroup *)newestGroupOfKind:(WMFContentGroupKind)kind withPredicate:(nullable NSPredicate *)additionalPredicate requireIsVisible:(BOOL)isVisibleRequired { + NSPredicate *predicate = nil; + if (isVisibleRequired) { + predicate = [NSPredicate predicateWithFormat:@"contentGroupKindInteger == %@ && isVisible == YES", @(kind)]; + } else { + predicate = [NSPredicate predicateWithFormat:@"contentGroupKindInteger == %@", @(kind)]; + } + NSCompoundPredicate *compoundPredicate = nil; + if (additionalPredicate) { + compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[predicate, additionalPredicate]]; + } + return [self newestGroupWithPredicate:compoundPredicate ?: predicate]; +} + +- (nullable WMFContentGroup *)newestVisibleGroupOfKind:(WMFContentGroupKind)kind forSiteURL:(nullable NSURL *)siteURL { + if (!siteURL) { + return [self newestGroupOfKind:kind requireIsVisible:YES]; + } + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"siteURLString == %@ && variant = %@", siteURL.wmf_databaseKey, siteURL.wmf_languageVariantCode]; + return [self newestVisibleGroupOfKind:kind withPredicate:predicate]; +} + +- (nullable WMFContentGroup *)newestVisibleGroupOfKind:(WMFContentGroupKind)kind withPredicate:(nullable NSPredicate *)predicate { + return [self newestGroupOfKind:kind withPredicate:predicate requireIsVisible:YES]; +} + +- (nullable WMFContentGroup *)newestVisibleGroupOfKind:(WMFContentGroupKind)kind { + return [self newestGroupOfKind:kind requireIsVisible:YES]; +} + +- (nullable WMFContentGroup *)newestGroupOfKind:(WMFContentGroupKind)kind { + return [self newestGroupOfKind:kind requireIsVisible:NO]; +} + +- (nullable WMFContentGroup *)newestGroupOfKind:(WMFContentGroupKind)kind forSiteURL:(nullable NSURL *)siteURL { + if (!siteURL) { + return [self newestGroupOfKind:kind requireIsVisible:NO]; + } + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"siteURLString == %@ && variant = %@", siteURL.wmf_databaseKey, siteURL.wmf_languageVariantCode]; + return [self newestGroupOfKind:kind withPredicate:predicate requireIsVisible:NO]; +} + +- (nullable WMFContentGroup *)groupOfKind:(WMFContentGroupKind)kind forDate:(NSDate *)date { + NSFetchRequest *fetchRequest = [WMFContentGroup fetchRequest]; + fetchRequest.predicate = [NSPredicate predicateWithFormat:@"contentGroupKindInteger == %@ && midnightUTCDate == %@", @(kind), date.wmf_midnightUTCDateFromLocalDate]; + fetchRequest.fetchLimit = 1; + NSError *fetchError = nil; + NSArray *contentGroups = [self executeFetchRequest:fetchRequest error:&fetchError]; + if (fetchError) { + DDLogError(@"Error fetching content groups: %@", fetchError); + return nil; + } + return [contentGroups firstObject]; +} + +- (nullable WMFContentGroup *)groupOfKind:(WMFContentGroupKind)kind forDate:(NSDate *)date siteURL:(NSURL *)siteURL { + NSFetchRequest *fetchRequest = [WMFContentGroup fetchRequest]; + fetchRequest.predicate = [NSPredicate predicateWithFormat:@"contentGroupKindInteger == %@ && midnightUTCDate == %@ && siteURLString == %@ && variant == %@", @(kind), date.wmf_midnightUTCDateFromLocalDate, siteURL.wmf_databaseKey, siteURL.wmf_languageVariantCode]; + fetchRequest.fetchLimit = 1; + NSError *fetchError = nil; + NSArray *contentGroups = [self executeFetchRequest:fetchRequest error:&fetchError]; + if (fetchError) { + DDLogError(@"Error fetching content groups: %@", fetchError); + return nil; + } + return [contentGroups firstObject]; +} + +- (nullable NSArray *)orderedGroupsOfKind:(WMFContentGroupKind)kind withPredicate:(nullable NSPredicate *)predicate { + NSPredicate *basePredicate = [NSPredicate predicateWithFormat:@"contentGroupKindInteger == %@", @(kind)]; + NSPredicate *finalPredicate = basePredicate; + if (predicate) { + NSCompoundPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[basePredicate, predicate]]; + finalPredicate = compoundPredicate; + } + + NSArray *sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"midnightUTCDate" ascending:NO], [NSSortDescriptor sortDescriptorWithKey:@"dailySortPriority" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:@"date" ascending:NO]]; + return [self groupsWithPredicate:finalPredicate sortDescriptors:sortDescriptors]; +} + +- (nullable NSArray *)groupsOfKind:(WMFContentGroupKind)kind forDate:(NSDate *)date { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"contentGroupKindInteger == %@ && midnightUTCDate == %@", @(kind), date.wmf_midnightUTCDateFromLocalDate]; + return [self groupsWithPredicate:predicate sortDescriptors:nil]; +} + +- (nullable NSArray *)groupsWithPredicate: (nonnull NSPredicate *)predicate sortDescriptors: (nullable NSArray *)sortDescriptors { + NSFetchRequest *fetchRequest = [WMFContentGroup fetchRequest]; + fetchRequest.predicate = predicate; + if (sortDescriptors) { + fetchRequest.sortDescriptors = sortDescriptors; + } + NSError *fetchError = nil; + NSArray *contentGroups = [self executeFetchRequest:fetchRequest error:&fetchError]; + if (fetchError) { + DDLogError(@"Error fetching content groups: %@", fetchError); + return nil; + } + return contentGroups; +} + +/* There is an important dependency between the langauge variant property and the computed properties siteURL, articleURL, and URL. Each returned URL uses the value of the siteURLString, articleURLString, or key properties, respectively. Each also sets the value of the variant property as the wmf_languageVariantCode of the created URL. The langauge variant should remain consistent for the lifetime of a WMFContentGroup object. When created, the variant comes from either the passed-in URL if present, or the siteURL. Note that this property is set *before* the siteURL and URL properties in this method. + + The setter methods for siteURL, articleURL, and URL assert that the variant on those incoming URLs equals the variant property of the group. This should always be true. The assertions ensure that these assumptions are true in all uses and that future changes do not unexpectedly violate that assumption. + */ +- (nullable WMFContentGroup *)createGroupForURL:(nullable NSURL *)URL ofKind:(WMFContentGroupKind)kind forDate:(NSDate *)date withSiteURL:(nullable NSURL *)siteURL associatedContent:(nullable id)associatedContent customizationBlock:(nullable void (^)(WMFContentGroup *group))customizationBlock { + WMFContentGroup *group = [NSEntityDescription insertNewObjectForEntityForName:@"WMFContentGroup" inManagedObjectContext:self]; + group.date = date; + group.midnightUTCDate = date.wmf_midnightUTCDateFromLocalDate; + group.contentGroupKind = kind; + // Important to set variant before siteURL or URL properties. See comment above for details. + group.variant = URL ? URL.wmf_languageVariantCode : (siteURL ? siteURL.wmf_languageVariantCode : nil); + group.siteURL = siteURL; + group.fullContentObject = associatedContent; + [group updateContentPreviewWithContent:associatedContent]; + + if (customizationBlock) { + customizationBlock(group); + } + + if (URL) { + group.URL = URL; + } else { + [group updateKey]; + } + [group updateContentType]; + + return group; +} + +- (nullable WMFContentGroup *)createGroupOfKind:(WMFContentGroupKind)kind forDate:(NSDate *)date withSiteURL:(nullable NSURL *)siteURL associatedContent:(nullable id)associatedContent { + return [self createGroupForURL:nil ofKind:kind forDate:date withSiteURL:siteURL associatedContent:associatedContent customizationBlock:nil]; +} + +- (nullable WMFContentGroup *)createGroupOfKind:(WMFContentGroupKind)kind forDate:(NSDate *)date withSiteURL:(nullable NSURL *)siteURL associatedContent:(nullable id)associatedContent customizationBlock:(nullable void (^)(WMFContentGroup *group))customizationBlock { + return [self createGroupForURL:nil ofKind:kind forDate:date withSiteURL:siteURL associatedContent:associatedContent customizationBlock:customizationBlock]; +} + +- (nullable WMFContentGroup *)fetchOrCreateGroupForURL:(NSURL *)URL ofKind:(WMFContentGroupKind)kind forDate:(NSDate *)date withSiteURL:(nullable NSURL *)siteURL associatedContent:(nullable id)associatedContent customizationBlock:(nullable void (^)(WMFContentGroup *group))customizationBlock { + + WMFContentGroup *group = [self contentGroupForURL:URL]; + if (group) { + group.date = date; + group.midnightUTCDate = date.wmf_midnightUTCDateFromLocalDate; + group.contentGroupKind = kind; + group.fullContentObject = associatedContent; + group.siteURL = siteURL; + [group updateContentPreviewWithContent:associatedContent]; + if (customizationBlock) { + customizationBlock(group); + } + } else { + group = [self createGroupForURL:URL ofKind:kind forDate:date withSiteURL:siteURL associatedContent:associatedContent customizationBlock:customizationBlock]; + } + + return group; +} + +- (nullable WMFContentGroup *)createGroupOfKind:(WMFContentGroupKind)kind forDate:(NSDate *)date withSiteURL:(nullable NSURL *)siteURL associatedContent:(nullable id)associatedContent inManagedObjectContext:(nonnull NSManagedObjectContext *)moc { + return [self createGroupOfKind:kind forDate:date withSiteURL:siteURL associatedContent:associatedContent customizationBlock:NULL]; +} + +- (void)removeContentGroup:(WMFContentGroup *)group { + NSParameterAssert(group); + [self deleteObject:group]; +} + +- (void)removeContentGroups:(NSArray *)contentGroups { + for (WMFContentGroup *group in contentGroups) { + [self deleteObject:group]; + } +} + +- (void)removeAllContentGroupsOfKind:(WMFContentGroupKind)kind { + NSArray *groups = [self contentGroupsOfKind:kind]; + [self removeContentGroups:groups]; +} + +- (nullable WMFContentGroup *)locationContentGroupWithSiteURL:(nullable NSURL *)siteURL withinMeters:(CLLocationDistance)meters ofLocation:(CLLocation *)location { + __block WMFContentGroup *locationContentGroup = nil; + NSString *siteURLString = siteURL.wmf_databaseKey; + NSString *siteURLVariantCode = siteURL.wmf_languageVariantCode; + [self enumerateContentGroupsOfKind:WMFContentGroupKindLocation + withBlock:^(WMFContentGroup *_Nonnull group, BOOL *_Nonnull stop) { + CLLocation *groupLocation = group.location; + if (!groupLocation) { + return; + } + NSString *groupSiteURLString = group.siteURL.wmf_databaseKey; + if (siteURLString && groupSiteURLString && ![siteURLString isEqualToString:groupSiteURLString]) { + return; + } + NSString *groupVariantCode = group.variant; + if (siteURLVariantCode && groupVariantCode && ![siteURLVariantCode isEqualToString:groupVariantCode]) { + return; + } + CLLocationDistance distance = [groupLocation distanceFromLocation:location]; + if (distance <= meters) { + locationContentGroup = group; + *stop = YES; + } + }]; + return locationContentGroup; +} + +@end + +#pragma mark - Language Variant Propagation + +/* Since a serialized NSURL has no notion of a language variant, when the contentPreview property of a WMFContentGroup instance or the object property of a WMFContent instance is deserialized, the variant of the content group needs to be propagated to URL values in the deserialized object graph. + + By doing this in -awakeFromFetch, the propagation does not happen until the values for the instance is faulted in. It also ensures that propagation happens once per fetch. Note that values set when an object is initilized or a property is set are expected to already have the language variant correctly set on itself or subelements. + + The serialized objects are expected to be of type NSURL, WMFMTLModel subclasses, or collections of those types. + */ + +@implementation WMFContentGroup (LanguageVariantPropagation) + +- (void)awakeFromFetch { + [super awakeFromFetch]; + [WMFContentGroup propagateLanguageVariant: self.variant toPropertyValue: (NSObject *)self.contentPreview]; +} + ++ (void)propagateLanguageVariant:(nullable NSString *)variant toPropertyValue:(NSObject *)inObject { + if ([inObject isKindOfClass:[NSArray class]] || [inObject isKindOfClass:[NSSet class]]) { + idcollection = (id)inObject; + for (id element in collection) { + [self propagateVariant:variant ToElement:element]; + } + } + else { + [self propagateVariant:variant ToElement:inObject]; + } +} + ++ (void)propagateVariant:(nullable NSString *)variant ToElement:(id)element { + if ([element respondsToSelector:@selector(propagateLanguageVariantCode:)]) { + [element propagateLanguageVariantCode:variant]; + } else if ([element isKindOfClass:[NSURL class]]) { + ((NSURL *)element).wmf_languageVariantCode = variant; + } +} + +@end + +@implementation WMFContent (LanguageVariantPropagation) +- (void)awakeFromFetch { + [super awakeFromFetch]; + [WMFContentGroup propagateLanguageVariant: self.contentGroup.variant toPropertyValue: (NSObject *)self.object]; +} +@end + diff --git a/Apps/Wikipedia/WMF Framework/WMFCrossProcessCoreDataSynchronizer.h b/Apps/Wikipedia/WMF Framework/WMFCrossProcessCoreDataSynchronizer.h new file mode 100644 index 0000000..ace3dc3 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFCrossProcessCoreDataSynchronizer.h @@ -0,0 +1,21 @@ +#import + +@class NSManagedObjectContext; + +NS_ASSUME_NONNULL_BEGIN + +/// Keeps a shared persistent store in sync across different processes +/// Likely should be replaced with Persistent History Tracking introduced in iOS 13: +/// https://developer.apple.com/videos/play/wwdc2017/210/ +/// https://www.avanderlee.com/swift/persistent-history-tracking-core-data/ + +@interface WMFCrossProcessCoreDataSynchronizer : NSObject + +- (instancetype)initWithIdentifier:(NSString *)identifier storageDirectory:(NSURL *)directoryURL NS_DESIGNATED_INITIALIZER; + +- (void)startSynchronizingContexts:(NSArray *)contexts; +- (void)stop; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/WMFCrossProcessCoreDataSynchronizer.m b/Apps/Wikipedia/WMF Framework/WMFCrossProcessCoreDataSynchronizer.m new file mode 100644 index 0000000..6fbe7a1 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFCrossProcessCoreDataSynchronizer.m @@ -0,0 +1,162 @@ +#import +#include +#import +#import + +@interface WMFCrossProcessCoreDataSynchronizer () { + dispatch_semaphore_t _semaphore; + int _token; +} + +@property (nonatomic, copy) NSString *identifier; +@property (nonatomic, copy) NSURL *containerURL; + +@end + +static uint64_t bundleHash() { + static dispatch_once_t onceToken; + static uint64_t bundleHash; + dispatch_once(&onceToken, ^{ + bundleHash = (uint64_t)[[[NSBundle mainBundle] bundleIdentifier] hash]; + }); + return bundleHash; +} + +@implementation WMFCrossProcessCoreDataSynchronizer + +- (instancetype)initWithIdentifier:(NSString *)identifier storageDirectory:(NSURL *)directoryURL { + self = [super init]; + if (self) { + _semaphore = dispatch_semaphore_create(1); + self.containerURL = directoryURL; + self.identifier = identifier; + } + return self; +} + +- (void)dealloc { + [self stop]; +} + +- (void)startSynchronizingContexts:(NSArray *)contexts { + if (!self.identifier) { + DDLogError(@"missing channel name"); + return; + } + const char *name = [self.identifier UTF8String]; + for (NSManagedObjectContext *context in contexts) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:context]; + } + @weakify(self) + notify_register_dispatch(name, &_token, dispatch_get_main_queue(), ^(int token) { + @strongify(self) + uint64_t state; + notify_get_state(token, &state); + BOOL isExternal = state != bundleHash(); + if (isExternal) { + [self readCrossProcessCoreDataNotificationWithState:state intoContexts:contexts]; + } + }); +} + +- (void)stop { + if (_token != 0) { + notify_cancel(_token); + } + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - Writing Changes from this Process + +- (void)contextDidSave:(NSNotification *)note { + [self writeCrossProcessCoreDataNotification:note]; +} + +- (void)writeCrossProcessCoreDataNotification:(NSNotification *)note { + dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); + NSDictionary *userInfo = note.userInfo; + if (!userInfo) { + return; + } + + uint64_t state = bundleHash(); + + NSDictionary *archiveableUserInfo = [self archivableNotificationUserInfoForUserInfo:userInfo]; + + NSError *archiveError = nil; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:archiveableUserInfo requiringSecureCoding:NO error:&archiveError]; + if (archiveError) { + DDLogError(@"Error archiving cross process changes: %@", archiveError); + return; + } + + NSURL *fileURL = [self archivedChangesFileURLWithState:state]; + [data writeToURL:fileURL atomically:YES]; + + const char *name = [self.identifier UTF8String]; + notify_set_state(_token, state); + notify_post(name); + dispatch_semaphore_signal(_semaphore); +} + +#pragma mark - Reading changes from other processes + +- (void)readCrossProcessCoreDataNotificationWithState:(uint64_t)state intoContexts:(NSArray *)contexts { + NSURL *fileURL = [self archivedChangesFileURLWithState:state]; + NSError *unarchiveError = nil; + NSDictionary *userInfo = [self unarchivedDictionaryFromFileURL:fileURL error:&unarchiveError]; + if (unarchiveError) { + DDLogError(@"Error unarchiving cross process core data notification: %@", unarchiveError); + return; + } + [NSManagedObjectContext mergeChangesFromRemoteContextSave:userInfo intoContexts:contexts]; +} + +#pragma mark - Notification Archive Utitlities + +- (NSDictionary *)unarchivedDictionaryFromFileURL:(NSURL *)fileURL error:(NSError **)error { + NSData *data = [NSData dataWithContentsOfURL:fileURL]; + NSSet *allowedClasses = [NSSet setWithArray:[NSSecureUnarchiveFromDataTransformer allowedTopLevelClasses]]; + return [NSKeyedUnarchiver unarchivedObjectOfClasses:allowedClasses fromData:data error:error]; +} + +- (NSURL *)archivedChangesFileURLWithState:(uint64_t)state { + NSString *fileName = [NSString stringWithFormat:@"%llu.%@.changes", state, self.identifier]; + return [self.containerURL URLByAppendingPathComponent:fileName isDirectory:NO]; +} + +- (nullable id)archiveableNotificationValueForValue:(id)value { + if ([value isKindOfClass:[NSManagedObject class]]) { + return [[value objectID] URIRepresentation]; + } else if ([value isKindOfClass:[NSManagedObjectID class]]) { + return [value URIRepresentation]; + } else if ([value isKindOfClass:[NSSet class]] || [value isKindOfClass:[NSArray class]]) { + return [value wmf_map:^id(id obj) { + return [self archiveableNotificationValueForValue:obj]; + }]; + } else if ([value conformsToProtocol:@protocol(NSCoding)]) { + return value; + } else { + return nil; + } +} + +- (NSDictionary *)archivableNotificationUserInfoForUserInfo:(NSDictionary *)userInfo { + NSMutableDictionary *archiveableUserInfo = [NSMutableDictionary dictionaryWithCapacity:userInfo.count]; + NSArray *allKeys = userInfo.allKeys; + for (NSString *key in allKeys) { + id value = userInfo[key]; + if ([value isKindOfClass:[NSDictionary class]]) { + value = [self archivableNotificationUserInfoForUserInfo:value]; + } else { + value = [self archiveableNotificationValueForValue:value]; + } + if (value) { + archiveableUserInfo[key] = value; + } + } + return archiveableUserInfo; +} + + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFExploreFeedContentController.h b/Apps/Wikipedia/WMF Framework/WMFExploreFeedContentController.h new file mode 100644 index 0000000..2e15ff8 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFExploreFeedContentController.h @@ -0,0 +1,126 @@ +#import + +@import UIKit; + +@class MWKDataStore; + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const WMFExploreFeedPreferencesGlobalCardsKey; +extern NSString *const WMFExploreFeedContentControllerBusyStateDidChange; +extern NSString *const WMFExploreFeedPreferencesDidChangeNotification; +extern NSString *const WMFExploreFeedPreferencesDidSaveNotification; +extern NSString *const WMFNewExploreFeedPreferencesWereRejectedNotification; + +extern const NSInteger WMFExploreFeedMaximumNumberOfDays; + +@interface WMFExploreFeedContentController : NSObject + +- (instancetype)initWithDataStore:(MWKDataStore *)dataStore; +- (instancetype)init NS_UNAVAILABLE; + +@property (nonatomic, getter=isBusy) BOOL busy; +@property (nonatomic, readonly) NSInteger countOfVisibleContentGroupKinds; + +/// Stops all content sources, recreates them from current preferred languages, and starts them +- (void)updateContentSources; + +- (void)startContentSources; +- (void)stopContentSources; + +- (void)cancelAllFetches; + +- (void)updateFeedSourcesUserInitiated:(BOOL)wasUserInitiated completion:(nullable dispatch_block_t)completion; +- (void)updateFeedSourcesWithDate:(nullable NSDate *)date userInitiated:(BOOL)wasUserInitiated completion:(nullable dispatch_block_t)completion; + +- (void)performDeduplicatedFetch:(nullable dispatch_block_t)completion; + +- (void)updateContentSource:(Class)class force:(BOOL)force completion:(nullable dispatch_block_t)completion; + +// Preferences + +/** + Toggles all customizable content groups on or off for a given siteURL. + + @param siteURL A Wikipedia site url for which all customizable content groups will be visible or hidden in the feed. + @param isOn A flag that indicates whether all customizable content groups should be visible or hidden for a given siteURL in the feed. + @param updateFeed A flag that indicates whether feed should be updated after Explore feed preferences are updated. + */ +-(void)toggleContentForSiteURL:(NSURL *)siteURL isOn:(BOOL)isOn waitForCallbackFromCoordinator:(BOOL)waitForCallbackFromCoordinator updateFeed:(BOOL)updateFeed; + +/** + Toggles a content group of given kind on or off for all preferred languages. + + @param contentGroupKind The kind of the content group that is about to be toggled on or off in the feed. + @param isOn A flag indicating whether the group should be visible in the feed or not. + */ +- (void)toggleContentGroupOfKind:(WMFContentGroupKind)contentGroupKind isOn:(BOOL)isOn updateFeed:(BOOL)updateFeed; + +- (void)toggleContentGroupOfKind:(WMFContentGroupKind)contentGroupKind isOn:(BOOL)isOn waitForCallbackFromCoordinator:(BOOL)waitForCallbackFromCoordinator apply:(BOOL)apply updateFeed:(BOOL)updateFeed; + +/** + Toggles a content group of given kind on or off for a given siteURL. + + @param contentGroupKind The kind of the content group that is about to be toggled on or off in the feed. + @param isOn A flag indicating whether the group should be visible in the feed or not. + @param siteURL A Wikipedia site url for which a content group of given kind will be visible or hidden in the feed. + */ +- (void)toggleContentGroupOfKind:(WMFContentGroupKind)contentGroupKind isOn:(BOOL)isOn forSiteURL:(NSURL *)siteURL updateFeed:(BOOL)updateFeed; + +- (void)toggleAllContentGroupKinds:(BOOL)on updateFeed:(BOOL)updateFeed; + +/** + Toggles non-language specific content group kinds (Because you read, Continue reading and Picture of the day) + + @param on A flag indicating whether non-language specific groups should be visible in the feed. + */ +- (void)toggleGlobalContentGroupKinds:(BOOL)on updateFeed:(BOOL)updateFeed; + +/** + Returns a set of language codes representing languages in which a given content group kind is visible in the feed. + + @param contentGroupKind The kind of the content group whose language codes you want to get. + @return A set of language codes representing languages in which a given content group kind is visible in the feed. + If a given content group kind is not visible in any languages, the set will be empty. + */ +- (NSArray *_Nonnull)contentLanguageCodesForContentGroupKind:(WMFContentGroupKind)contentGroupKind; + +/** + Returns a flag indicating whether there are any customizable content groups visible in the feed for a given siteURL. + */ +- (BOOL)anyContentGroupsVisibleInTheFeedForSiteURL:(NSURL *)siteURL; + +/** + Returns a set of integers that represent customizable content group kinds. + */ ++ (NSSet *)customizableContentGroupKindNumbers; + +/** + Returns a set of integers that represent non-language specific content group kinds. + */ ++ (NSSet *)globalContentGroupKindNumbers; + +/** + Indicates whether non-language specific group kinds are visible in the feed. + */ +@property (nonatomic, readonly) BOOL areGlobalContentGroupKindsInFeed; + +- (BOOL)isGlobalContentGroupKindInFeed:(WMFContentGroupKind)contentGroupKind; + +- (void)saveNewExploreFeedPreferences:(NSDictionary *)newExploreFeedPreferences apply:(BOOL)apply updateFeed:(BOOL)updateFeed; +- (void)rejectNewExploreFeedPreferences; + +- (void)dismissCollapsedContentGroups; + +#if DEBUG +- (void)debugChaos; +#endif + +@end + +@interface WMFExploreFeedContentController (LanguageVariantMigration) +/// The expected dictionary uses language codes as the key with the value being the desired language variant code for that language. +- (void)migrateExploreFeedSettingsToLanguageVariants:(NSDictionary *)languageMapping inManagedObjectContext:(NSManagedObjectContext *)moc; +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/WMFExploreFeedContentController.m b/Apps/Wikipedia/WMF Framework/WMFExploreFeedContentController.m new file mode 100644 index 0000000..c86a2f9 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFExploreFeedContentController.m @@ -0,0 +1,864 @@ +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +NSString *const WMFExploreFeedContentControllerBusyStateDidChange = @"WMFExploreFeedContentControllerBusyStateDidChange"; +const NSInteger WMFExploreFeedMaximumNumberOfDays = 30; +static const NSTimeInterval WMFFeedRefreshTimeoutInterval = 60; +static NSTimeInterval WMFFeedRefreshBackgroundTimeout = 30; +static const NSString *kvo_WMFExploreFeedContentController_operationQueue_operationCount = @"kvo_WMFExploreFeedContentController_operationQueue_operationCount"; + +// Explore feed preferences dictionary keys +NSString *const WMFExploreFeedPreferencesKey = @"WMFExploreFeedPreferencesKey"; +NSString *const WMFExploreFeedPreferencesGlobalCardsKey = @"WMFExploreFeedPreferencesGlobalCardsKey"; +// Explore feed preferences notifications +NSString *const WMFExploreFeedPreferencesDidChangeNotification = @"WMFExploreFeedPreferencesDidChangeNotification"; +NSString *const WMFExploreFeedPreferencesDidSaveNotification = @"WMFExploreFeedPreferencesDidSaveNotification"; +NSString *const WMFNewExploreFeedPreferencesWereRejectedNotification = @"WMFNewExploreFeedPreferencesWereRejectedNotification"; + +@interface WMFExploreFeedContentController () + +@property (nonatomic, strong) NSArray> *contentSources; +@property (nonatomic, strong) NSOperationQueue *operationQueue; +@property (nonatomic, weak) MWKDataStore *dataStore; +@property (nonatomic, strong) NSDictionary *exploreFeedPreferences; +@property (nonatomic, copy, readonly) NSArray *preferredSiteURLs; +@property (nonatomic, strong) ExploreFeedPreferencesUpdateCoordinator *exploreFeedPreferencesUpdateCoordinator; +@property (nonatomic, nullable) NSNumber *cachedCountOfVisibleContentGroupKinds; +@property (nonatomic, strong) NSDictionary *sortOrderByContentLanguageCode; + +@end + +@implementation WMFExploreFeedContentController + +@synthesize exploreFeedPreferences = _exploreFeedPreferences; + +- (instancetype)initWithDataStore:(MWKDataStore *)dataStore { + self = [super init]; + if (self) { + self.operationQueue = [[NSOperationQueue alloc] init]; + self.operationQueue.maxConcurrentOperationCount = 1; + self.dataStore = dataStore; + } + return self; +} + +- (void)dealloc { + self.operationQueue = nil; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)setOperationQueue:(NSOperationQueue *)operationQueue { + if (_operationQueue == operationQueue) { + return; + } + + if (_operationQueue) { + [_operationQueue removeObserver:self forKeyPath:@"operationCount"]; + } + + _operationQueue = operationQueue; + + if (_operationQueue) { + [_operationQueue addObserver:self forKeyPath:@"operationCount" options:NSKeyValueObservingOptionNew context:&kvo_WMFExploreFeedContentController_operationQueue_operationCount]; + } +} + +- (void)setDataStore:(MWKDataStore *)dataStore { + _dataStore = dataStore; + self.exploreFeedPreferencesUpdateCoordinator = [[ExploreFeedPreferencesUpdateCoordinator alloc] initWithFeedContentController:self]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateExploreFeedPreferencesFromDidSaveNotification:) name:WMFViewContextDidSave object:nil]; +} + +- (NSDictionary *)exploreFeedPreferences { + assert([NSThread isMainThread]); + if (!_exploreFeedPreferences) { + _exploreFeedPreferences = [self exploreFeedPreferencesInManagedObjectContext:self.dataStore.viewContext]; + } + return _exploreFeedPreferences; +} + +- (void)setExploreFeedPreferences:(NSDictionary *)exploreFeedPreferences { + assert([NSThread isMainThread]); + _exploreFeedPreferences = exploreFeedPreferences; +} + +- (NSArray *)preferredSiteURLs { + return [self.dataStore.languageLinkController.preferredSiteURLs copy]; +} + +#pragma mark - Content Sources + +- (WMFFeedContentSource *)feedContentSource { + return [self.contentSources wmf_match:^BOOL(id obj) { + return [obj isKindOfClass:[WMFFeedContentSource class]]; + }]; +} + +- (WMFRandomContentSource *)randomContentSource { + return [self.contentSources wmf_match:^BOOL(id obj) { + return [obj isKindOfClass:[WMFRandomContentSource class]]; + }]; +} +- (WMFNearbyContentSource *)nearbyContentSource { + return [self.contentSources wmf_match:^BOOL(id obj) { + return [obj isKindOfClass:[WMFNearbyContentSource class]]; + }]; +} + +- (WMFFeedContentSource *)onThisDayContentSource { + return [self.contentSources wmf_match:^BOOL(id obj) { + return [obj isKindOfClass:[WMFOnThisDayContentSource class]]; + }]; +} + +- (NSArray> *)contentSources { + NSParameterAssert(self.dataStore); + WMFSession *session = self.dataStore.session; + WMFConfiguration *configuration = self.dataStore.configuration; + NSParameterAssert(session); + NSParameterAssert(configuration); + if (!_contentSources) { + NSArray*siteURLs = self.preferredSiteURLs; + NSParameterAssert(siteURLs); + NSMutableArray *mutableContentSources = [NSMutableArray arrayWithCapacity:2 + siteURLs.count * 7]; + [mutableContentSources addObject:[[WMFRelatedPagesContentSource alloc] init]]; + [mutableContentSources addObject:[[WMFContinueReadingContentSource alloc] initWithUserDataStore:self.dataStore]]; + for (NSURL *siteURL in siteURLs) { + WMFFeedContentSource *feedContentSource = [[WMFFeedContentSource alloc] initWithSiteURL:siteURL + userDataStore:self.dataStore]; + [mutableContentSources addObjectsFromArray: @[[[WMFNearbyContentSource alloc] initWithSiteURL:siteURL dataStore:self.dataStore], + feedContentSource, + [[WMFRandomContentSource alloc] initWithSiteURL:siteURL session:session configuration:configuration], + [[WMFAnnouncementsContentSource alloc] initWithSiteURL:siteURL userDataStore:self.dataStore], + [[WMFOnThisDayContentSource alloc] initWithSiteURL:siteURL session:session configuration:configuration]]]; + } + _contentSources = [mutableContentSources copy]; + } + return _contentSources; +} + +#pragma mark - Update / Start / Stop + +- (void)updateContentSources { + NSArray *siteURLs = self.preferredSiteURLs; + NSMutableDictionary *updatedSortOrder = [NSMutableDictionary dictionaryWithCapacity:siteURLs.count]; + NSInteger i = 0; + for (NSURL *siteURL in siteURLs) { + updatedSortOrder[siteURL.wmf_contentLanguageCode] = @(i); + i++; + } + self.sortOrderByContentLanguageCode = updatedSortOrder; + + if ([_contentSources count] == 0) { + return; + } + [self stopContentSources]; + self.contentSources = nil; + [self startContentSources]; + [self updateFeedSourcesUserInitiated:NO completion:NULL]; +} + +- (void)startContentSources { + [self.contentSources enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { + if ([obj conformsToProtocol:@protocol(WMFAutoUpdatingContentSource)]) { + [(id)obj startUpdating]; + } + }]; +} + +- (void)stopContentSources { + [self.contentSources enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { + if ([obj conformsToProtocol:@protocol(WMFAutoUpdatingContentSource)]) { + [(id)obj stopUpdating]; + } + }]; +} + +#pragma mark - Updating + +- (void)updateFeedSourcesUserInitiated:(BOOL)wasUserInitiated completion:(nullable dispatch_block_t)completion { + [self updateFeedSourcesWithDate:nil userInitiated:wasUserInitiated completion:completion]; +} + +- (void)updateFeedSourcesWithDate:(nullable NSDate *)date userInitiated:(BOOL)wasUserInitiated completion:(nullable dispatch_block_t)completion { + WMFAssertMainThread(@"updateFeedSources: must be called on the main thread"); + WMFAsyncBlockOperation *op = [[WMFAsyncBlockOperation alloc] initWithAsyncBlock:^(WMFAsyncBlockOperation *_Nonnull op) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSManagedObjectContext *moc = self.dataStore.feedImportContext; + WMFTaskGroup *group = [WMFTaskGroup new]; +#if DEBUG + NSMutableArray *entered = [NSMutableArray arrayWithCapacity:self.contentSources.count]; +#endif + [self.contentSources enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { + [group enter]; +#if DEBUG + NSString *classString = NSStringFromClass([obj class]); + @synchronized(self) { + [entered addObject:classString]; + } +#endif + dispatch_block_t contentSourceCompletion = ^{ +#if DEBUG + @synchronized(self) { + NSInteger index = [entered indexOfObject:classString]; + assert(index != NSNotFound); + [entered removeObjectAtIndex:index]; + } +#endif + [group leave]; + }; + + if ([obj conformsToProtocol:@protocol(WMFOptionalNewContentSource)]) { + NSDate *optionalDate = date ? date : [NSDate date]; + id optional = (id)obj; + [optional loadContentForDate:optionalDate inManagedObjectContext:moc force:NO addNewContent:wasUserInitiated completion:contentSourceCompletion]; + } else if (date && [obj conformsToProtocol:@protocol(WMFDateBasedContentSource)]) { + id dateBased = (id)obj; + [dateBased loadContentForDate:date inManagedObjectContext:moc force:NO completion:contentSourceCompletion]; + } else if (!date) { + [obj loadNewContentInManagedObjectContext:moc force:NO completion:contentSourceCompletion]; + } else { + contentSourceCompletion(); + } + }]; + + [group waitInBackgroundWithTimeout:WMFFeedRefreshTimeoutInterval + completion:^{ + [moc performBlock:^{ + NSError *saveError = nil; + if ([moc hasChanges]) { + [self applyExploreFeedPreferencesToAllObjectsInManagedObjectContext:moc]; + if (![moc save:&saveError]) { + DDLogError(@"Error saving: %@", saveError); + } + } + dispatch_async(dispatch_get_main_queue(), ^{ + [self.dataStore teardownFeedImportContext]; + [[NSUserDefaults standardUserDefaults] wmf_setFeedRefreshDate:[NSDate date]]; + [[WMFWidgetController shared] reloadAllWidgetsIfNecessary]; + if (completion) { + completion(); + } + [op finish]; + }); + }]; + +#if DEBUG + if ([entered count] > 0) { + DDLogError(@"Didn't leave: %@", entered); + } +#endif + }]; + }); + }]; + + [self.operationQueue addOperation:op]; +} + +- (void)updateContentSource:(Class)class force:(BOOL)force completion:(nullable dispatch_block_t)completion { + WMFAssertMainThread(@"updateContentSource: must be called on the main thread"); + NSManagedObjectContext *moc = self.dataStore.feedImportContext; + WMFTaskGroup *group = [WMFTaskGroup new]; + WMFAsyncBlockOperation *op = [[WMFAsyncBlockOperation alloc] initWithAsyncBlock:^(WMFAsyncBlockOperation *_Nonnull op) { + [self.contentSources enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { + if ([obj isKindOfClass:class]) { + [group enter]; + [obj loadNewContentInManagedObjectContext:moc + force:force + completion:^{ + [group leave]; + }]; + } + }]; + + [group waitInBackgroundWithTimeout:WMFFeedRefreshTimeoutInterval + completion:^{ + [moc performBlock:^{ + [self applyExploreFeedPreferencesToAllObjectsInManagedObjectContext:moc]; + NSError *saveError = nil; + if ([moc hasChanges] && ![moc save:&saveError]) { + DDLogError(@"Error saving: %@", saveError); + } + dispatch_async(dispatch_get_main_queue(), ^{ + if (completion) { + completion(); + } + [op finish]; + }); + }]; + }]; + + }]; + + [self.operationQueue addOperation:op]; +} + +- (void)updateBackgroundSourcesWithCompletion:(void (^_Nonnull)(UIBackgroundFetchResult))completionHandler { + WMFAssertMainThread(@"updateBackgroundSourcesWithCompletion: must be called on the main thread"); + + NSManagedObjectContext *moc = self.dataStore.viewContext; + NSFetchRequest *beforeFetchRequest = [WMFContentGroup fetchRequest]; + NSInteger beforeCount = [moc countForFetchRequest:beforeFetchRequest error:nil]; + + WMFAsyncBlockOperation *op = [[WMFAsyncBlockOperation alloc] initWithAsyncBlock:^(WMFAsyncBlockOperation *_Nonnull op) { + WMFTaskGroup *group = [WMFTaskGroup new]; + [group enter]; + [[self feedContentSource] loadNewContentInManagedObjectContext:moc + force:NO + completion:^{ + [group leave]; + }]; + + [group enter]; + [[self randomContentSource] loadNewContentInManagedObjectContext:moc + force:NO + completion:^{ + [group leave]; + }]; + + [group enter]; + [[self onThisDayContentSource] loadNewContentInManagedObjectContext:moc + force:NO + completion:^{ + [group leave]; + }]; + + [group waitInBackgroundWithTimeout:WMFFeedRefreshBackgroundTimeout + completion:^{ + [moc performBlock:^{ + BOOL didUpdate = NO; + if ([moc hasChanges]) { + [self applyExploreFeedPreferencesToAllObjectsInManagedObjectContext:moc]; + NSFetchRequest *afterFetchRequest = [WMFContentGroup fetchRequest]; + NSInteger afterCount = [moc countForFetchRequest:afterFetchRequest error:nil]; + didUpdate = afterCount != beforeCount; + NSError *saveError = nil; + if (![moc save:&saveError]) { + DDLogError(@"Error saving background source update: %@", saveError); + } + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[WMFWidgetController shared] reloadAllWidgetsIfNecessary]; + if (completionHandler) { + completionHandler(didUpdate ? UIBackgroundFetchResultNewData : UIBackgroundFetchResultNoData); + } + [op finish]; + }); + }]; + }]; + }]; + [self.operationQueue addOperation:op]; +} + +#pragma mark - Preferences + +- (void)updateExploreFeedPreferencesFromDidSaveNotification:(NSNotification *)note { + NSDictionary *userInfo = note.userInfo; + NSArray *keys = @[NSInsertedObjectsKey, NSUpdatedObjectsKey, NSDeletedObjectsKey, NSRefreshedObjectsKey, NSInvalidatedObjectsKey]; + for (NSString *key in keys) { + NSSet *savedObjects = userInfo[key]; + for (NSManagedObject *object in savedObjects) { + if (![object isKindOfClass:[WMFKeyValue class]]) { + continue; + } + WMFKeyValue *keyValue = (WMFKeyValue *)object; + if (![keyValue.key isEqualToString:WMFExploreFeedPreferencesKey]) { + continue; + } + NSDictionary *newExploreFeedPreferences = (NSDictionary *)keyValue.value; + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.exploreFeedPreferences == newExploreFeedPreferences) { + return; + } + self.exploreFeedPreferences = newExploreFeedPreferences; + [NSNotificationCenter.defaultCenter postNotificationName:WMFExploreFeedPreferencesDidSaveNotification object:self.exploreFeedPreferences]; + }); + self.cachedCountOfVisibleContentGroupKinds = nil; + } + } +} + +- (BOOL)anyContentGroupsVisibleInTheFeedForSiteURL:(NSURL *)siteURL { + return [self.exploreFeedPreferences objectForKey:siteURL.wmf_contentLanguageCode] != nil; +} + +- (NSArray *)contentLanguageCodesForContentGroupKind:(WMFContentGroupKind)contentGroupKind { + NSMutableArray *contentLanguageCodes = [NSMutableArray new]; + [self.exploreFeedPreferences enumerateKeysAndObjectsUsingBlock:^(NSString *key, id _Nonnull value, BOOL * _Nonnull stop) { + if (![value isKindOfClass:[NSDictionary class]] && [value containsObject:@(contentGroupKind)]) { + [contentLanguageCodes addObject:key]; + } + }]; + return contentLanguageCodes; +} + ++ (NSSet *)customizableContentGroupKindNumbers { + static NSSet *customizableContentGroupKindNumbers; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + customizableContentGroupKindNumbers = [NSSet setWithArray:@[@(WMFContentGroupKindFeaturedArticle), @(WMFContentGroupKindNews), @(WMFContentGroupKindTopRead), @(WMFContentGroupKindOnThisDay), @(WMFContentGroupKindLocation), @(WMFContentGroupKindLocationPlaceholder), @(WMFContentGroupKindRandom), @(WMFContentGroupKindNotification)]]; + }); + return customizableContentGroupKindNumbers; +} + ++ (NSSet *)globalContentGroupKindNumbers { + static NSSet *globalContentGroupKindNumbers; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + globalContentGroupKindNumbers = [NSSet setWithArray:@[@(WMFContentGroupKindPictureOfTheDay), @(WMFContentGroupKindContinueReading), @(WMFContentGroupKindRelatedPages)]]; + }); + return globalContentGroupKindNumbers; +} + +- (BOOL)isGlobalContentGroupKindInFeed:(WMFContentGroupKind)contentGroupKind { + NSAssert([self isGlobal:contentGroupKind], @"Content group kind is not global"); + NSNumber *globalCardPreferenceNumber = [self.globalCardPreferences objectForKey:@(contentGroupKind)]; + return [globalCardPreferenceNumber boolValue]; +} + +- (BOOL)isGlobal:(WMFContentGroupKind)contentGroupKind { + return [[WMFExploreFeedContentController globalContentGroupKindNumbers] containsObject:@(contentGroupKind)]; +} + +- (NSDictionary *)globalCardPreferences { + NSDictionary *globalCardPreferences = [self.exploreFeedPreferences objectForKey:WMFExploreFeedPreferencesGlobalCardsKey] ?: [self defaultGlobalCardsPreferences]; + return globalCardPreferences; +} + +- (BOOL)areGlobalContentGroupKindsInFeed { + for (NSNumber *globalCardPreferenceNumber in [self.globalCardPreferences allValues]) { + if ([globalCardPreferenceNumber boolValue]) { + return true; + } + continue; + } + return false; +} + +- (NSDictionary *)exploreFeedPreferencesInManagedObjectContext:(NSManagedObjectContext *)moc { + WMFKeyValue *keyValue = [moc wmf_keyValueForKey:WMFExploreFeedPreferencesKey]; + NSDictionary *exploreFeedPreferences = (NSDictionary *)keyValue.value; + if (exploreFeedPreferences && [exploreFeedPreferences objectForKey:WMFExploreFeedPreferencesGlobalCardsKey]) { + return exploreFeedPreferences; + } + [moc wmf_setValue:[self defaultExploreFeedPreferences] forKey:WMFExploreFeedPreferencesKey]; + [self save:moc]; + NSDictionary *preferences = (NSDictionary *)[moc wmf_keyValueForKey:WMFExploreFeedPreferencesKey].value; + assert(preferences); + return preferences; +} + +- (NSDictionary *)defaultExploreFeedPreferences { + NSMutableDictionary *defaultExploreFeedPreferences = [NSMutableDictionary dictionaryWithCapacity:self.preferredSiteURLs.count + 1]; + for (NSURL *siteURL in self.preferredSiteURLs) { + [defaultExploreFeedPreferences setObject:[WMFExploreFeedContentController customizableContentGroupKindNumbers] forKey:siteURL.wmf_contentLanguageCode]; + } + [defaultExploreFeedPreferences setObject:[self defaultGlobalCardsPreferences] forKey:WMFExploreFeedPreferencesGlobalCardsKey]; + return defaultExploreFeedPreferences; +} + +- (NSDictionary *)defaultGlobalCardsPreferences { + NSMutableDictionary *defaultGlobalCardsPreferences = [NSMutableDictionary new]; + for (NSNumber *globalContentGroupKindNumber in [WMFExploreFeedContentController globalContentGroupKindNumbers]) { + [defaultGlobalCardsPreferences setObject:[NSNumber numberWithBool:YES] forKey:globalContentGroupKindNumber]; + } + return defaultGlobalCardsPreferences; +} + +- (void)toggleContentGroupOfKind:(WMFContentGroupKind)contentGroupKind isOn:(BOOL)isOn updateFeed:(BOOL)updateFeed { + [self toggleContentGroupOfKind:contentGroupKind forSiteURLs:self.preferredSiteURLs isOn:isOn waitForCallbackFromCoordinator:YES apply:YES updateFeed:updateFeed]; +} + +- (void)toggleContentGroupOfKind:(WMFContentGroupKind)contentGroupKind isOn:(BOOL)isOn waitForCallbackFromCoordinator:(BOOL)waitForCallbackFromCoordinator apply:(BOOL)apply updateFeed:(BOOL)updateFeed { + [self toggleContentGroupOfKind:contentGroupKind forSiteURLs:self.preferredSiteURLs isOn:isOn waitForCallbackFromCoordinator:waitForCallbackFromCoordinator apply:apply updateFeed:updateFeed]; +} + +- (void)toggleContentGroupOfKind:(WMFContentGroupKind)contentGroupKind isOn:(BOOL)isOn forSiteURL:(NSURL *)siteURL updateFeed:(BOOL)updateFeed { + [self toggleContentGroupOfKind:contentGroupKind forSiteURLs:[NSArray arrayWithObject:siteURL] isOn:isOn waitForCallbackFromCoordinator:YES apply:YES updateFeed:updateFeed]; +} + +- (void)toggleAllContentGroupKinds:(BOOL)on updateFeed:(BOOL)updateFeed { + [self updateExploreFeedPreferences:^NSDictionary *(NSDictionary *oldPreferences) { + if (on) { + return self.defaultExploreFeedPreferences; + } else { + NSMutableDictionary *allTurnedOff = [NSMutableDictionary new]; + NSMutableDictionary *globalCardPreferences = [NSMutableDictionary new]; + for (NSNumber *globalContentGroupKindNumber in [WMFExploreFeedContentController globalContentGroupKindNumbers]) { + [globalCardPreferences setObject:[NSNumber numberWithBool:NO] forKey:globalContentGroupKindNumber]; + } + [allTurnedOff setObject:globalCardPreferences forKey:WMFExploreFeedPreferencesGlobalCardsKey]; + return allTurnedOff; + } + } willTurnOnContentGroupOrLanguage:on waitForCallbackFromCoordinator:NO apply:YES updateFeed:updateFeed]; +} + +-(void)toggleContentForSiteURL:(NSURL *)siteURL isOn:(BOOL)isOn waitForCallbackFromCoordinator:(BOOL)waitForCallbackFromCoordinator updateFeed:(BOOL)updateFeed { + [self updateExploreFeedPreferences:^NSDictionary *(NSDictionary *oldPreferences) { + NSString *key = siteURL.wmf_contentLanguageCode; + NSMutableDictionary *newPreferences = [oldPreferences mutableCopy]; + if (isOn) { + [newPreferences setObject:[WMFExploreFeedContentController customizableContentGroupKindNumbers] forKey:key]; + } else { + if ([newPreferences objectForKey:key]) { + [newPreferences removeObjectForKey:key]; + } + } + return newPreferences; + } willTurnOnContentGroupOrLanguage:isOn waitForCallbackFromCoordinator:YES apply:YES updateFeed:updateFeed]; +} + +- (void)toggleContentGroupOfKind:(WMFContentGroupKind)contentGroupKind forSiteURLs:(NSArray *)siteURLs isOn:(BOOL)isOn waitForCallbackFromCoordinator:(BOOL)waitForCallbackFromCoordinator apply:(BOOL)apply updateFeed:(BOOL)updateFeed { + [self updateExploreFeedPreferences:^NSDictionary *(NSDictionary *oldPreferences) { + NSMutableDictionary *newPreferences = [oldPreferences mutableCopy]; + if ([self isGlobal:contentGroupKind]) { + NSDictionary *oldGlobalCardPreferences = [newPreferences objectForKey:WMFExploreFeedPreferencesGlobalCardsKey] ?: [self defaultGlobalCardsPreferences]; + NSMutableDictionary *newGlobalCardPreferences = [oldGlobalCardPreferences mutableCopy]; + [newGlobalCardPreferences setObject:[NSNumber numberWithBool:isOn] forKey:@(contentGroupKind)]; + [newPreferences setObject:newGlobalCardPreferences forKey:WMFExploreFeedPreferencesGlobalCardsKey]; + } else { + for (NSURL *siteURL in siteURLs) { + NSString *key = siteURL.wmf_contentLanguageCode; + NSSet *oldVisibleContentGroupKindNumbers = [newPreferences objectForKey:key]; + NSMutableSet *newVisibleContentGroupKindNumbers; + + if (oldVisibleContentGroupKindNumbers) { + newVisibleContentGroupKindNumbers = [oldVisibleContentGroupKindNumbers mutableCopy]; + } else { + newVisibleContentGroupKindNumbers = [NSMutableSet set]; + } + + if (isOn) { + [newVisibleContentGroupKindNumbers addObject:@(contentGroupKind)]; + } else { + [newVisibleContentGroupKindNumbers removeObject:@(contentGroupKind)]; + } + + BOOL isPlaces = contentGroupKind == WMFContentGroupKindLocation || contentGroupKind == WMFContentGroupKindLocationPlaceholder; + if (isPlaces) { + WMFContentGroupKind otherPlacesContentGroupKind = contentGroupKind == WMFContentGroupKindLocation ? WMFContentGroupKindLocationPlaceholder : WMFContentGroupKindLocation; + if (isOn) { + [newVisibleContentGroupKindNumbers addObject:@(otherPlacesContentGroupKind)]; + } else { + [newVisibleContentGroupKindNumbers removeObject:@(otherPlacesContentGroupKind)]; + } + } + + if (newVisibleContentGroupKindNumbers.count == 0) { + [newPreferences removeObjectForKey:key]; + } else { + [newPreferences setObject:newVisibleContentGroupKindNumbers forKey:key]; + } + } + } + return newPreferences; + } willTurnOnContentGroupOrLanguage:isOn waitForCallbackFromCoordinator:waitForCallbackFromCoordinator apply:apply updateFeed:updateFeed]; +} + +- (void)toggleGlobalContentGroupKinds:(BOOL)on updateFeed:(BOOL)updateFeed{ + [self updateExploreFeedPreferences:^NSDictionary *(NSDictionary *oldPreferences) { + NSMutableDictionary *newPreferences = [oldPreferences mutableCopy]; + NSDictionary *oldGlobalCardPreferences = [newPreferences objectForKey:WMFExploreFeedPreferencesGlobalCardsKey] ?: [self defaultGlobalCardsPreferences]; + NSMutableDictionary *newGlobalCardPreferences = [oldGlobalCardPreferences mutableCopy]; + for (id key in newGlobalCardPreferences.allKeys) { + [newGlobalCardPreferences setObject:[NSNumber numberWithBool:on] forKey:key]; + } + [newPreferences setObject:newGlobalCardPreferences forKey:WMFExploreFeedPreferencesGlobalCardsKey]; + return newPreferences; + } willTurnOnContentGroupOrLanguage:on waitForCallbackFromCoordinator:YES apply:YES updateFeed:updateFeed]; +} + +- (void)saveNewExploreFeedPreferences:(NSDictionary *)newExploreFeedPreferences apply:(BOOL)apply updateFeed:(BOOL)updateFeed { + WMFAssertMainThread(@"Saving explore feed preferences should be performed on the main thread"); + [self.dataStore.viewContext wmf_setValue:newExploreFeedPreferences forKey:WMFExploreFeedPreferencesKey]; + [self save:self.dataStore.viewContext]; + if (apply) { + WMFAsyncBlockOperation *op = [[WMFAsyncBlockOperation alloc] initWithAsyncBlock:^(WMFAsyncBlockOperation *_Nonnull op) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSManagedObjectContext *moc = self.dataStore.feedImportContext; + [moc performBlock:^{ + [self applyExploreFeedPreferencesToAllObjectsInManagedObjectContext:moc]; + [self save:moc]; + dispatch_async(dispatch_get_main_queue(), ^{ + [op finish]; + if (updateFeed) { + [self updateFeedSourcesUserInitiated:NO completion:nil]; + } else { + [[WMFWidgetController shared] reloadAllWidgetsIfNecessary]; + } + }); + }]; + }); + }]; + [self.operationQueue addOperation:op]; + } +} + +- (void)rejectNewExploreFeedPreferences { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:WMFNewExploreFeedPreferencesWereRejectedNotification object:nil]; + }); +} + +- (void)updateExploreFeedPreferences:(NSDictionary *(^)(NSDictionary *newPreferences))update willTurnOnContentGroupOrLanguage:(BOOL)willTurnOnContentGroupOrLanguage waitForCallbackFromCoordinator:(BOOL)waitForCallbackFromCoordinator apply:(BOOL)apply updateFeed:(BOOL)updateFeed { + dispatch_async(dispatch_get_main_queue(), ^{ + NSManagedObjectContext *moc = self.dataStore.viewContext; + NSDictionary *oldPreferences = [self exploreFeedPreferencesInManagedObjectContext:moc]; + assert(oldPreferences); + NSDictionary *newPreferences = update(oldPreferences); + if (waitForCallbackFromCoordinator) { + [self.exploreFeedPreferencesUpdateCoordinator configureWithOldExploreFeedPreferences:oldPreferences newExploreFeedPreferences:newPreferences willTurnOnContentGroupOrLanguage:willTurnOnContentGroupOrLanguage updateFeed:updateFeed]; + [[NSNotificationCenter defaultCenter] postNotificationName:WMFExploreFeedPreferencesDidChangeNotification object:self.exploreFeedPreferencesUpdateCoordinator]; + } else { + [self saveNewExploreFeedPreferences:newPreferences apply:apply updateFeed:updateFeed]; + } + }); +} + +- (void)dismissCollapsedContentGroups { + WMFAsyncBlockOperation *op = [[WMFAsyncBlockOperation alloc] initWithAsyncBlock:^(WMFAsyncBlockOperation *_Nonnull op) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSManagedObjectContext *moc = self.dataStore.feedImportContext; + [moc performBlock:^{ + NSFetchRequest *fetchRequest = [WMFContentGroup fetchRequest]; + fetchRequest.predicate = [NSPredicate predicateWithFormat:@"undoTypeInteger != 0"]; + NSError *error = nil; + NSArray *contentGroups = [moc executeFetchRequest:fetchRequest error:&error]; + if (error) { + DDLogError(@"Error fetching WMFContentGroup: %@", error); + } + for (WMFContentGroup *contentGroup in contentGroups) { + if (contentGroup.undoType == WMFContentGroupUndoTypeContentGroup) { + [contentGroup markDismissed]; + } + contentGroup.isVisible = NO; + contentGroup.undoType = WMFContentGroupUndoTypeNone; + } + [self save:moc]; + dispatch_async(dispatch_get_main_queue(), ^{ + [op finish]; + }); + }]; + }); + }]; + [self.operationQueue addOperation:op]; +} + +- (NSInteger)countOfVisibleContentGroupKinds { + if (self.cachedCountOfVisibleContentGroupKinds) { + return self.cachedCountOfVisibleContentGroupKinds.integerValue; + } + NSInteger count = 0; + for (NSNumber *isGlobalCardVisible in [self.globalCardPreferences allValues]) { + if (!isGlobalCardVisible.boolValue) { + continue; + } + count++; + } + for (id value in self.exploreFeedPreferences.allValues) { + if ([value isKindOfClass:[NSSet class]]) { + NSSet *contentGroupKindNumbers = (NSSet *)value; + count += contentGroupKindNumbers.count; + break; + } + } + self.cachedCountOfVisibleContentGroupKinds = [NSNumber numberWithInteger:count]; + return count; +} + +- (void)applyExploreFeedPreferencesToAllObjectsInManagedObjectContext:(NSManagedObjectContext *)moc { + NSFetchRequest *fetchRequest = [WMFContentGroup fetchRequest]; + NSError *error = nil; + NSArray *contentGroups = [moc executeFetchRequest:fetchRequest error:&error]; + if (error) { + DDLogError(@"Error fetching WMFContentGroup: %@", error); + } + [self applyExploreFeedPreferencesToObjects:contentGroups inManagedObjectContext:moc]; +} + +- (void)applyExploreFeedPreferencesToObjects:(id)objects inManagedObjectContext:(NSManagedObjectContext *)moc { + NSDictionary *exploreFeedPreferences = [self exploreFeedPreferencesInManagedObjectContext:moc]; + for (NSManagedObject *object in objects) { + if (![object isKindOfClass:[WMFContentGroup class]]) { + continue; + } + + WMFContentGroup *contentGroup = (WMFContentGroup *)object; + [contentGroup updateDailySortPriorityWithSortOrderByContentLanguageCode:self.sortOrderByContentLanguageCode]; + + // Skip collapsed cards, let them be visible + if (contentGroup.undoType != WMFContentGroupUndoTypeNone) { + continue; + } + + // Do not let preferences affect the notifications card + if (contentGroup.contentGroupKind == WMFContentGroupKindNotification) { + continue; + } + + BOOL isVisible; + if ([self isGlobal:contentGroup.contentGroupKind]) { + NSDictionary *globalCardPreferences = [exploreFeedPreferences objectForKey:WMFExploreFeedPreferencesGlobalCardsKey]; + BOOL isGlobalCardVisible = [[globalCardPreferences objectForKey:@(contentGroup.contentGroupKind)] boolValue]; + isVisible = isGlobalCardVisible && !contentGroup.wasDismissed; + } else { + NSSet *visibleContentGroupKinds = [exploreFeedPreferences objectForKey:contentGroup.siteURL.wmf_contentLanguageCode]; + NSNumber *contentGroupNumber = @(contentGroup.contentGroupKindInteger); + if (![[WMFExploreFeedContentController customizableContentGroupKindNumbers] containsObject:contentGroupNumber]) { + continue; + } + if ([visibleContentGroupKinds containsObject:contentGroupNumber]) { + isVisible = !contentGroup.wasDismissed; + } else { + isVisible = NO; + } + } + if (isVisible != contentGroup.isVisible) { + contentGroup.isVisible = isVisible; + } + } +} + +- (void)save:(NSManagedObjectContext *)moc { + NSError *error = nil; + if (moc.hasChanges && ![moc save:&error]) { + DDLogError(@"Error saving WMFExploreFeedContentController managedObjectContext"); + } +} + +#pragma mark - Debug + +#if DEBUG + +- (void)debugChaos { + BOOL needsTeardown = arc4random_uniform(2) > 0; + NSManagedObjectContext *moc = needsTeardown ? self.dataStore.feedImportContext : self.dataStore.viewContext; + WMFAsyncBlockOperation *op = [[WMFAsyncBlockOperation alloc] initWithAsyncBlock:^(WMFAsyncBlockOperation *_Nonnull op) { + [moc performBlock:^{ + NSFetchRequest *request = [WMFContentGroup fetchRequest]; + NSInteger count = [moc countForFetchRequest:request error:nil]; + request.fetchLimit = (NSUInteger)arc4random_uniform((uint32_t)count); + request.fetchOffset = (NSUInteger)arc4random_uniform((uint32_t)(count - request.fetchLimit)); + NSArray *results = [moc executeFetchRequest:request error:nil]; + for (WMFContentGroup *group in results) { + uint32_t seed = arc4random_uniform(5); + int32_t random = (15 - (int32_t)arc4random_uniform(30)); + switch (seed) { + case 0: + group.midnightUTCDate = [group.midnightUTCDate dateByAddingTimeInterval:86400 * random]; + group.contentMidnightUTCDate = [group.contentMidnightUTCDate dateByAddingTimeInterval:86400 * random]; + group.date = [group.date dateByAddingTimeInterval:86400 * random]; + break; + case 1: + [moc deleteObject:group]; + case 2: + group.dailySortPriority = group.dailySortPriority + random; + default: + break; + } + } + NSError *saveError = nil; + if ([moc hasChanges] && ![moc save:&saveError]) { + DDLogError(@"chaos error: %@", saveError); + } + dispatch_async(dispatch_get_main_queue(), ^{ + if (needsTeardown) { + [self.dataStore teardownFeedImportContext]; + } + [op finish]; + }); + }]; + }]; + [self.operationQueue addOperation:op]; +} +#endif + +- (void)cancelAllFetches { + [self.operationQueue cancelAllOperations]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if (context == &kvo_WMFExploreFeedContentController_operationQueue_operationCount) { + if (self.operationQueue.operationCount == 0 && self.isBusy) { + self.busy = NO; + [[NSNotificationCenter defaultCenter] postNotificationName:WMFExploreFeedContentControllerBusyStateDidChange object:self]; + } else if (self.operationQueue.operationCount > 0 && !self.isBusy) { + self.busy = YES; + [[NSNotificationCenter defaultCenter] postNotificationName:WMFExploreFeedContentControllerBusyStateDidChange object:self]; + } + } else { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + +- (void)performBackgroundFetch:(void (^)(UIBackgroundFetchResult))completion { + [self updateBackgroundSourcesWithCompletion:completion]; +} + +- (void)performDeduplicatedFetch:(nullable dispatch_block_t)completion { + WMFAssertMainThread(@"performDeduplicatedFetch: must be called on the main thread"); + if (self.operationQueue.operationCount > 0) { + NSAssert(self.operationQueue.maxConcurrentOperationCount == 1, @"The strategy of adding this block as a completion doesn't work if the maxConcurrentOperationCount != 1"); + if (completion) { + [self.operationQueue addOperationWithBlock:completion]; + } + return; + } + [self performBackgroundFetch:^(UIBackgroundFetchResult fetchResult) { + if (completion) { + completion(); + } + }]; +} + +@end + +@implementation WMFExploreFeedContentController (LanguageVariantMigration) + +/// The expected dictionary uses language codes as the key with the value being the desired language variant code for that language. +/// Move from siteURL-based to contentLanguageCode-based keys to support language variants +- (void)migrateExploreFeedSettingsToLanguageVariants:(NSDictionary *)languageMapping inManagedObjectContext:(NSManagedObjectContext *)moc{ + + WMFKeyValue *keyValue = [moc wmf_keyValueForKey:WMFExploreFeedPreferencesKey]; + NSDictionary *originalPreferences = (NSDictionary *)keyValue.value; + + NSMutableDictionary *migratedPreferences = [[NSMutableDictionary alloc] init]; + for (NSString *key in originalPreferences.allKeys) { + // Just pass the global key along as-is + if ([key isEqualToString:WMFExploreFeedPreferencesGlobalCardsKey]) { + [migratedPreferences setValue:[originalPreferences valueForKey:key] forKey:key]; + } + else { + NSString *languageCode = nil; + // Remaining keys should be site URL strings prior to migration + if ([key hasPrefix:@"http"]) { + NSURL *oldKeyURL = [NSURL URLWithString:key]; + languageCode = oldKeyURL.wmf_languageCode; + } + // Interim code for migration may have been previously run + // Allow for that case as well + else { + languageCode = key; + } + if (languageCode) { + NSString *languageVariantCode = languageMapping[languageCode]; + NSString *newKey = languageVariantCode ? : languageCode; + [migratedPreferences setValue:[originalPreferences valueForKey:key] forKey:newKey]; + } + } + } + [moc wmf_setValue:migratedPreferences forKey:WMFExploreFeedPreferencesKey]; + [self save:moc]; + __unused NSDictionary *preferences = (NSDictionary *)[moc wmf_keyValueForKey:WMFExploreFeedPreferencesKey].value; + assert(preferences); +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFFeedOnThisDayEvent+LocalizedDates.swift b/Apps/Wikipedia/WMF Framework/WMFFeedOnThisDayEvent+LocalizedDates.swift new file mode 100644 index 0000000..37472c1 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFFeedOnThisDayEvent+LocalizedDates.swift @@ -0,0 +1,6 @@ +extension WMFFeedOnThisDayEvent { + // Returns year string - i.e. '1000' (for AD) or '200 BC'. (Negative years are 'BC') + public var yearString: String? { + return DateFormatter.wmf_yearString(for: year?.intValue ?? 0, with: languageCode) + } +} diff --git a/Apps/Wikipedia/WMF Framework/WMFHTMLElement.h b/Apps/Wikipedia/WMF Framework/WMFHTMLElement.h new file mode 100644 index 0000000..b42b76d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFHTMLElement.h @@ -0,0 +1,13 @@ +@import Foundation; + +@interface WMFHTMLElement : NSObject + +@property (nonatomic, nonnull) NSString *tagName; +@property (nonatomic) NSInteger startLocation; +@property (nonatomic) NSInteger endLocation; +@property (nonatomic, nullable) NSMutableArray *children; +@property (nonatomic) NSUInteger nestingDepth; + +- (nonnull instancetype)initWithTagName:(nonnull NSString *)tagName; + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFHTMLElement.m b/Apps/Wikipedia/WMF Framework/WMFHTMLElement.m new file mode 100644 index 0000000..b7b39e2 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFHTMLElement.m @@ -0,0 +1,16 @@ +#import "WMFHTMLElement.h" + +@implementation WMFHTMLElement + +- (instancetype)initWithTagName:(NSString *)tagName { + self = [super self]; + if (self) { + self.tagName = tagName; + self.startLocation = NSNotFound; + self.endLocation = NSNotFound; + self.nestingDepth = 0; + } + return self; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataClass.h b/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataClass.h new file mode 100644 index 0000000..678a551 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataClass.h @@ -0,0 +1,16 @@ +#import + +@class NSObject; + +NS_ASSUME_NONNULL_BEGIN + +/** + * WMFKeyValue is utilized as a key/value store for values related to a user's Core Data library. It can be used for sensitive data that might not be appropriate for NSUserDefaults or anything that is tied to the Core Data library. + */ +@interface WMFKeyValue : NSManagedObject + +@end + +NS_ASSUME_NONNULL_END + +#import diff --git a/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataClass.m b/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataClass.m new file mode 100644 index 0000000..bead71c --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataClass.m @@ -0,0 +1,5 @@ +#import "WMFKeyValue+CoreDataClass.h" + +@implementation WMFKeyValue + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataProperties.h b/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataProperties.h new file mode 100644 index 0000000..55ced4e --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataProperties.h @@ -0,0 +1,16 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface WMFKeyValue (CoreDataProperties) + ++ (NSFetchRequest *)fetchRequest; + +@property (nullable, nonatomic, copy) NSString *key; +@property (nullable, nonatomic, copy) NSString *group; +@property (nullable, nonatomic, copy) NSDate *date; +@property (nullable, nonatomic, retain) id value; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataProperties.m b/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataProperties.m new file mode 100644 index 0000000..3e75259 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFKeyValue+CoreDataProperties.m @@ -0,0 +1,14 @@ +#import + +@implementation WMFKeyValue (CoreDataProperties) + ++ (NSFetchRequest *)fetchRequest { + return [[NSFetchRequest alloc] initWithEntityName:@"WMFKeyValue"]; +} + +@dynamic key; +@dynamic group; +@dynamic date; +@dynamic value; + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFLegacyFetcher.h b/Apps/Wikipedia/WMF Framework/WMFLegacyFetcher.h new file mode 100644 index 0000000..1189366 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFLegacyFetcher.h @@ -0,0 +1,33 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@class WMFSession; +@class WMFConfiguration; +@class WMFFetcher; +@class MediaWikiAPIDisplayError; + +// Bridge from old Obj-C fetcher classes to new Swift fetcher class +@interface WMFLegacyFetcher : NSObject + +- (instancetype)initWithSession:(WMFSession *)session configuration:(WMFConfiguration *)configuration NS_DESIGNATED_INITIALIZER; +- (instancetype)init; + +@property (nonatomic, readonly) WMFSession *session; +@property (nonatomic, readonly) WMFConfiguration *configuration; +@property (nonatomic, strong, readonly) WMFFetcher *fetcher; + +- (NSString *)performMediaWikiAPIPOSTWithCSRFTokenForURL:(NSURL *)URL withBodyParameters:(NSDictionary *)bodyParameters completionHandler:(void (^)(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error))completionHandler; +- (NSURLSessionTask *)performMediaWikiAPIGETForURL:(NSURL *)URL withQueryParameters:(NSDictionary *)queryParameters completionHandler:(void (^)(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error)) completionHandler; +- (NSURLSessionTask *)performMediaWikiAPIGETForURLRequest:(NSURLRequest *)urlRequest completionHandler:(void (^)(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error)) completionHandler; +- (NSURLSessionTask *)performCancelableMediaWikiAPIGETForURL:(NSURL *)URL cancellationKey:(NSString *)cancellationKey withQueryParameters:(NSDictionary *)queryParameters completionHandler:(void (^)(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error)) completionHandler; +- (NSURLSessionTask *)performMediaWikiAPIPOSTForURL:(NSURL *)URL withBodyParameters:(NSDictionary *)bodyParameters completionHandler:(void (^)(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error))completionHandler; + +- (void)resolveMediaWikiApiErrorFromResult: (NSDictionary *)result siteURL:(NSURL *)siteURL completionHandler:(void (^)(MediaWikiAPIDisplayError * displayError)) completionHandler; + +- (void)cancelAllFetches; // only cancels tasks started with the methods provided by WMFLegacyFetcher - tasks started directly on the session are not canceled +- (void)cancelTaskWithCancellationKey:(NSString *)cancellationKey; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/WMFLegacyFetcher.m b/Apps/Wikipedia/WMF Framework/WMFLegacyFetcher.m new file mode 100644 index 0000000..bdf9611 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFLegacyFetcher.m @@ -0,0 +1,75 @@ +#import "WMFLegacyFetcher.h" +#import + +@interface WMFLegacyFetcher () + +@property (nonatomic, strong, readwrite) WMFFetcher *fetcher; + +@end + +@implementation WMFLegacyFetcher + +- (instancetype)init { + // SINGLETONTODO + MWKDataStore *dataStore = [MWKDataStore shared]; + self = [self initWithSession:dataStore.session configuration:dataStore.configuration]; + return self; +} + +- (instancetype)initWithSession:(WMFSession *)session configuration:(WMFConfiguration *)configuration { + self = [super init]; + if (self) { + self.fetcher = [[WMFFetcher alloc] initWithSession:session configuration:configuration]; + } + return self; +} + +- (void)dealloc { + [self cancelAllFetches]; +} + +- (WMFSession *)session { + return self.fetcher.session; +} + +- (WMFConfiguration *)configuration { + return self.fetcher.configuration; +} + +- (NSURLSessionTask *)performMediaWikiAPIPOSTForURL:(NSURL *)URL withBodyParameters:(NSDictionary *)bodyParameters completionHandler:(void (^)(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error))completionHandler { + return [self.fetcher performMediaWikiAPIPOSTForURL:URL withBodyParameters:bodyParameters cancellationKey:nil reattemptLoginOn401Response:true completionHandler:completionHandler]; +} + +- (NSString *)performMediaWikiAPIPOSTWithCSRFTokenForURL:(NSURL *)URL withBodyParameters:(NSDictionary *)bodyParameters completionHandler:(void (^)(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error))completionHandler { + return [self.fetcher performTokenizedMediaWikiAPIPOSTWithTokenType:WMFTokenTypeCsrf toURL:URL withBodyParameters:bodyParameters cancellationKey:nil reattemptLoginOn401Response:true completionHandler:completionHandler]; +} + +- (NSURLSessionTask *)performMediaWikiAPIGETForURL:(NSURL *)URL withQueryParameters:(NSDictionary *)queryParameters completionHandler:(void (^)(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error)) completionHandler { + return [self performCancelableMediaWikiAPIGETForURL:URL cancellationKey:NSUUID.UUID.UUIDString withQueryParameters:queryParameters completionHandler:completionHandler]; +} + +- (NSURLSessionTask *)performMediaWikiAPIGETForURLRequest:(NSURLRequest *)urlRequest completionHandler:(void (^)(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error)) completionHandler { + return [self performCancelableMediaWikiAPIGETForURLRequest:urlRequest cancellationKey:NSUUID.UUID.UUIDString completionHandler:completionHandler]; +} + +- (NSURLSessionTask *)performCancelableMediaWikiAPIGETForURL:(NSURL *)URL cancellationKey:(NSString *)cancellationKey withQueryParameters:(NSDictionary *)queryParameters completionHandler:(void (^)(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error)) completionHandler { + return [self.fetcher performMediaWikiAPIGETForURL:URL withQueryParameters:queryParameters cancellationKey:cancellationKey completionHandler:completionHandler]; +} + +- (NSURLSessionTask *)performCancelableMediaWikiAPIGETForURLRequest:(NSURLRequest *)urlRequest cancellationKey:(NSString *)cancellationKey completionHandler:(void (^)(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error)) completionHandler { + return [self.fetcher performMediaWikiAPIGETForURLRequest:urlRequest cancellationKey:cancellationKey completionHandler:completionHandler]; +} + +- (void)resolveMediaWikiApiErrorFromResult: (NSDictionary *)result siteURL:(NSURL *)siteURL completionHandler:(void (^)(MediaWikiAPIDisplayError * displayError)) completionHandler { + [self.fetcher resolveMediaWikiApiErrorFromResult:result siteURL:siteURL completionHandler:completionHandler]; +} + +- (void)cancelAllFetches { + [self.fetcher cancelAllTasks]; +} + +- (void)cancelTaskWithCancellationKey:(NSString *)cancellationKey { + [self.fetcher cancelTaskForKey:cancellationKey]; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFLegacySerializer.h b/Apps/Wikipedia/WMF Framework/WMFLegacySerializer.h new file mode 100644 index 0000000..93b672d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFLegacySerializer.h @@ -0,0 +1,13 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface WMFLegacySerializer : NSObject + +// Serializes {-object-}s at "key.path" in the form {"key": {"path": [{-object-}, {-object-}]}} +// The languageVariantCode is propogated to the URL properties of the graph of deserialized model objects ++ (nullable NSArray *)modelsOfClass:(Class)modelClass fromArrayForKeyPath:(NSString *)keyPath inJSONDictionary:(nullable NSDictionary *)JSONDictionary languageVariantCode:(nullable NSString *)languageVariantCode error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/WMF Framework/WMFLegacySerializer.m b/Apps/Wikipedia/WMF Framework/WMFLegacySerializer.m new file mode 100644 index 0000000..5f392df --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFLegacySerializer.m @@ -0,0 +1,29 @@ +#import +#import +#import + +@implementation WMFLegacySerializer + ++ (nullable NSArray *)modelsOfClass:(Class)modelClass fromArrayForKeyPath:(NSString *)keyPath inJSONDictionary:(nullable NSDictionary *)JSONDictionary languageVariantCode:(nullable NSString *)languageVariantCode error:(NSError **)error { + NSArray *maybeJSONDictionaries = [JSONDictionary valueForKeyPath:keyPath]; + + if (![maybeJSONDictionaries isKindOfClass:[NSArray class]]) { + if (error) { + *error = [WMFFetcher unexpectedResponseError]; + } + return nil; + } + + return [self modelsOfClass:modelClass fromUntypedArray:maybeJSONDictionaries languageVariantCode: languageVariantCode error:error]; +} + +// Filters the untyped array to only include NSDictionary objects before serializing into modelClass ++ (nullable NSArray *)modelsOfClass:(Class)modelClass fromUntypedArray:(NSArray *)untypedArray languageVariantCode:(nullable NSString *)languageVariantCode error:(NSError **)error { + NSArray *JSONDictionaries = [untypedArray wmf_select:^BOOL(id _Nonnull maybeJSONDictionary) { + return [maybeJSONDictionary isKindOfClass:[NSDictionary class]]; + }]; + + return [MTLJSONAdapter modelsOfClass:modelClass fromJSONArray:JSONDictionaries languageVariantCode: languageVariantCode error:error]; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFOnThisDayEventsFetcher.h b/Apps/Wikipedia/WMF Framework/WMFOnThisDayEventsFetcher.h new file mode 100644 index 0000000..82d7477 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFOnThisDayEventsFetcher.h @@ -0,0 +1,12 @@ +@import Foundation; +#import +#import + +@class WMFFeedOnThisDayEvent; + +@interface WMFOnThisDayEventsFetcher : WMFLegacyFetcher + ++ (BOOL)isOnThisDaySupportedByLanguage:(NSString *)languageCode NS_SWIFT_NAME(isOnThisDaySupported(by:)); +- (void)fetchOnThisDayEventsForURL:(NSURL *)siteURL month:(NSUInteger)month day:(NSUInteger)day failure:(WMFErrorHandler)failure success:(void (^)(NSArray *announcements))success; + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFOnThisDayEventsFetcher.m b/Apps/Wikipedia/WMF Framework/WMFOnThisDayEventsFetcher.m new file mode 100644 index 0000000..c13b8b9 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFOnThisDayEventsFetcher.m @@ -0,0 +1,55 @@ +#import "WMFOnThisDayEventsFetcher.h" +#import "WMFFeedOnThisDayEvent.h" +#import +#import + +@implementation WMFOnThisDayEventsFetcher + ++ (NSSet *)supportedLanguages { + static dispatch_once_t onceToken; + static NSSet *supportedLanguages; + dispatch_once(&onceToken, ^{ + supportedLanguages = [NSSet setWithObjects:@"en", @"de", @"sv", @"fr", @"es", @"ru", @"pt", @"ar", @"uk", nil]; + }); + return supportedLanguages; +} + ++ (BOOL)isOnThisDaySupportedByLanguage:(NSString *)languageCode { + return [[WMFOnThisDayEventsFetcher supportedLanguages] containsObject:languageCode]; +} + +- (void)fetchOnThisDayEventsForURL:(NSURL *)siteURL month:(NSUInteger)month day:(NSUInteger)day failure:(WMFErrorHandler)failure success:(void (^)(NSArray *announcements))success { + NSParameterAssert(siteURL); + if (siteURL == nil || siteURL.wmf_languageCode == nil || ![WMFOnThisDayEventsFetcher isOnThisDaySupportedByLanguage:siteURL.wmf_languageCode] || month < 1 || day < 1) { + NSError *error = [WMFFetcher invalidParametersError]; + failure(error); + return; + } + + NSString *monthString = [NSString stringWithFormat:@"%lu", (unsigned long)month]; + NSString *dayString = [NSString stringWithFormat:@"%lu", (unsigned long)day]; + NSArray *path = @[@"feed", @"onthisday", @"events", monthString, dayString]; + NSURL *url = [self.configuration feedContentAPIURLForURL:siteURL appendingPathComponents:path]; + [self.session getJSONDictionaryFromURL:url ignoreCache:YES completionHandler:^(NSDictionary * _Nullable result, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error) { + if (error) { + failure(error); + return; + } + + if (response.statusCode == 304) { + failure([WMFFetcher noNewDataError]); + return; + } + + NSError *serializerError = nil; + NSArray *events = [WMFLegacySerializer modelsOfClass:[WMFFeedOnThisDayEvent class] fromArrayForKeyPath:@"events" inJSONDictionary:result languageVariantCode: url.wmf_languageVariantCode error:&serializerError]; + if (serializerError) { + failure(serializerError); + return; + } + + success(events); + }]; +} + +@end diff --git a/Apps/Wikipedia/WMF Framework/WMFPreferredLanguageInfoProvider.h b/Apps/Wikipedia/WMF Framework/WMFPreferredLanguageInfoProvider.h new file mode 100644 index 0000000..c35e1fa --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFPreferredLanguageInfoProvider.h @@ -0,0 +1,11 @@ +#ifndef WMFPreferredLanguageCodesProviding_h +#define WMFPreferredLanguageCodesProviding_h +NS_ASSUME_NONNULL_BEGIN +@protocol WMFPreferredLanguageInfoProvider + +- (void)getPreferredContentLanguageCodes:(void (^)(NSArray *))completion; +- (void)getPreferredLanguageCodes:(void (^)(NSArray *))completion; + +@end +NS_ASSUME_NONNULL_END +#endif /* WMFPreferredLanguageCodesProviding_h */ diff --git a/Apps/Wikipedia/WMF Framework/WMFQuoteMacros.h b/Apps/Wikipedia/WMF Framework/WMFQuoteMacros.h new file mode 100644 index 0000000..d96e02d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFQuoteMacros.h @@ -0,0 +1,7 @@ +#ifndef WMFQuoteMacros_h +#define WMFQuoteMacros_h + +#define QUOTE2(x) #x +#define QUOTE(x) QUOTE2(x) + +#endif /* WMFQuoteMacros_h */ diff --git a/Apps/Wikipedia/WMF Framework/WMFSecureUnarchiveFromDataTransformer.swift b/Apps/Wikipedia/WMF Framework/WMFSecureUnarchiveFromDataTransformer.swift new file mode 100644 index 0000000..e089b10 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFSecureUnarchiveFromDataTransformer.swift @@ -0,0 +1,9 @@ +import Foundation + +/// SecureUnarchiveFromDataTransformer allows us to utilize transformable properties with a list of allowed classes +@objc(WMFSecureUnarchiveFromDataTransformer) +class SecureUnarchiveFromDataTransformer: NSSecureUnarchiveFromDataTransformer { + override class var allowedTopLevelClasses: [AnyClass] { + return super.allowedTopLevelClasses + [WMFMTLModel.self, NSSet.self, CLLocation.self, CLPlacemark.self, RemoteNotificationLinks.self, RemoteNotificationLink.self] + } +} diff --git a/Apps/Wikipedia/WMF Framework/WMFSparklineView.swift b/Apps/Wikipedia/WMF Framework/WMFSparklineView.swift new file mode 100644 index 0000000..235634f --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WMFSparklineView.swift @@ -0,0 +1,198 @@ +import Foundation +import UIKit + + +extension UIBezierPath { + static func CGPointGetMidPointFromPoint(_ fromPoint: CGPoint, toPoint: CGPoint) -> CGPoint { + return CGPoint(x: 0.5*(fromPoint.x + toPoint.x), y: 0.5*(fromPoint.y + toPoint.y)) + } + + static func CGPointGetQuadCurveControlPointFromPoint(_ fromPoint: CGPoint, toPoint: CGPoint) -> CGPoint { + var controlPoint = CGPointGetMidPointFromPoint(fromPoint, toPoint: toPoint) + let deltaY = toPoint.y - controlPoint.y + controlPoint.y += deltaY + return controlPoint + } + + class func quadCurvePathWithPoints(_ points: [CGPoint]) -> UIBezierPath { + let path = UIBezierPath() + + guard points.count > 1 else { + return path + } + + path.move(to: points[0]) + + guard points.count > 2 else { + path.addLine(to: points[1]) + return path + } + + var i = 0 + for toPoint in points[1...(points.count - 1)] { + let fromPoint = points[i] + let midPoint = CGPointGetMidPointFromPoint(fromPoint, toPoint: toPoint) + let midPointControlPoint = CGPointGetQuadCurveControlPointFromPoint(midPoint, toPoint: fromPoint) + path.addQuadCurve(to: midPoint, controlPoint: midPointControlPoint) + let toPointControlPoint = CGPointGetQuadCurveControlPointFromPoint(midPoint, toPoint: toPoint) + path.addQuadCurve(to: toPoint, controlPoint: toPointControlPoint) + i += 1 + } + + path.lineJoinStyle = CGLineJoin.round + path.lineCapStyle = CGLineCap.round + + return path + } +} + + +open class WMFSparklineView : UIView, Themeable { + var sparklineLayer = CAShapeLayer() + var gridlineLayer = CAShapeLayer() + var gradientLayer = CAGradientLayer() + let useLogScale = true + + public func apply(theme: Theme) { + gridlineLayer.strokeColor = theme.colors.border.cgColor + gradientLayer.colors = [theme.colors.rankGradientStart.cgColor, theme.colors.rankGradientEnd.cgColor] + } + + public override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + open var maxDataValue: CGFloat = 0 { + didSet { + setNeedsLayout() + } + } + + open var minDataValue: CGFloat = 0 { + didSet { + setNeedsLayout() + } + } + + open var dataValues: [NSNumber] = [] { + didSet { + setNeedsLayout() + updateMinAndMaxFromDataValues() + } + } + + open var showsVerticalGridlines = false { + didSet { + setNeedsLayout() + } + } + + @IBInspectable open var gridlineWidth: CGFloat = 0.5 { + didSet { + gridlineLayer.lineWidth = gridlineWidth + } + } + + @IBInspectable open var sparklineWidth: CGFloat = 1.0 { + didSet { + sparklineLayer.lineWidth = sparklineWidth + } + } + + open func updateMinAndMaxFromDataValues() { + var min = CGFloat.greatestFiniteMagnitude + var max = CGFloat.leastNormalMagnitude + for val in dataValues { + let val = CGFloat(val.doubleValue) + if val < min { + min = val + } + if val > max { + max = val + } + } + minDataValue = min + maxDataValue = max + } + + func setup() { + apply(theme: Theme.standard) + + gridlineLayer.fillColor = UIColor.clear.cgColor + gridlineWidth = 0.5 + layer.addSublayer(gridlineLayer) + + sparklineLayer.fillColor = UIColor.clear.cgColor + sparklineWidth = 1.5 + sparklineLayer.strokeColor = UIColor.black.cgColor + + gradientLayer.startPoint = CGPoint(x: 0, y: 0.5) + gradientLayer.endPoint = CGPoint(x: 1, y: 0.5) + gradientLayer.mask = sparklineLayer + layer.addSublayer(gradientLayer) + + } + + override open func layoutSubviews() { + super.layoutSubviews() + + sparklineLayer.frame = layer.bounds + gridlineLayer.frame = layer.bounds + gradientLayer.frame = layer.bounds + + let margin: CGFloat = 2 + let minX = margin + let minY = margin + let maxX = layer.bounds.maxX - margin + let maxY = layer.bounds.maxY - margin + let width = maxX - minX + let height = maxY - minY + + let gridlinePath = UIBezierPath() + + let firstGridlineY: CGFloat = minY + gridlinePath.move(to: CGPoint(x: minX, y: firstGridlineY)) + gridlinePath.addLine(to: CGPoint(x: maxX, y:firstGridlineY)) + + let secondGridlineY = maxY + gridlinePath.move(to: CGPoint(x: minX, y: secondGridlineY)) + gridlinePath.addLine(to: CGPoint(x: maxX, y: secondGridlineY)) + + if showsVerticalGridlines { + let lowerGridlineY = 0.33*(firstGridlineY + secondGridlineY) + gridlinePath.move(to: CGPoint(x: minX, y: lowerGridlineY)) + gridlinePath.addLine(to: CGPoint(x: maxX, y: lowerGridlineY)) + + let higherGridlineY = 0.67*(firstGridlineY + secondGridlineY) + gridlinePath.move(to: CGPoint(x: minX, y: higherGridlineY)) + gridlinePath.addLine(to: CGPoint(x: maxX, y: higherGridlineY)) + } + + let delta = maxDataValue - minDataValue + let lastIndex = dataValues.count - 1 + let xInterval = width/CGFloat(lastIndex) + var points = [CGPoint]() + for (i, dataValue) in dataValues.enumerated() { + let floatValue = CGFloat(dataValue.doubleValue) + let relativeY = floatValue - minDataValue + let normalizedY = 1 - relativeY/delta + let y = minY + height*normalizedY + let x = xInterval * CGFloat(i) + points.append(CGPoint(x: x, y: y)) + if showsVerticalGridlines && i != 0 && i != lastIndex { + gridlinePath.move(to: CGPoint(x: x, y: minY - 5)) + gridlinePath.addLine(to: CGPoint(x: x, y: maxY + 5)) + } + } + gridlineLayer.path = gridlinePath.cgPath + + sparklineLayer.path = UIBezierPath.quadCurvePathWithPoints(points).cgPath + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Widget/Models/WidgetCache.swift b/Apps/Wikipedia/WMF Framework/Widget/Models/WidgetCache.swift new file mode 100644 index 0000000..39b5652 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Widget/Models/WidgetCache.swift @@ -0,0 +1,17 @@ +import Foundation + +public struct WidgetCache: Codable { + + // MARK: - Properties + + public var settings: WidgetSettings + public var featuredContent: WidgetFeaturedContent? + + // MARK: - Public + + public init(settings: WidgetSettings, featuredContent: WidgetFeaturedContent?) { + self.settings = settings + self.featuredContent = featuredContent + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Widget/Models/WidgetFeaturedContent.swift b/Apps/Wikipedia/WMF Framework/Widget/Models/WidgetFeaturedContent.swift new file mode 100644 index 0000000..f245d74 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Widget/Models/WidgetFeaturedContent.swift @@ -0,0 +1,81 @@ +import Foundation + +public struct WidgetFeaturedContent: Codable { + + // MARK: - Nested Types + + enum CodingKeys: String, CodingKey { + case featuredArticle = "tfa" + case fetchDate + case fetchedLanguageVariantCode + } + + public struct FeaturedArticleContent: Codable { + + // MARK: - Featured Article - Nested Types + + enum CodingKeys: String, CodingKey { + case displayTitle = "displaytitle" + case description + case extract + case languageCode = "lang" + case languageDirection = "dir" + case contentURL = "content_urls" + case thumbnailImageSource = "thumbnail" + case originalImageSource = "originalimage" + } + + public struct ContentURL: Codable { + public struct PageURL: Codable { + public let page: String + } + + public let desktop: PageURL + } + + public struct ImageSource: Codable { + enum CodingKeys: String, CodingKey { + case source + case width + case height + case data + } + + public let source: String + public let width: Int + public let height: Int + public var data: Data? + } + + // MARK: - Featured Article - Properties + + public var displayTitle: String + public let description: String? + public let extract: String + public let languageCode: String + public let languageDirection: String + public let contentURL: ContentURL + public var thumbnailImageSource: ImageSource? + public var originalImageSource: ImageSource? + } + + // MARK: - Properties + + public var featuredArticle: FeaturedArticleContent? + + // MARK: - Properties - Network Fetch Metadata + + public var fetchDate: Date? + public var fetchedLanguageVariantCode: String? + + // MARK: - Public + + public static func previewContent() -> WidgetFeaturedContent? { + if let previewContentFilePath = Bundle.main.path(forResource: "Featured Article Widget Preview Content", ofType: "json"), let jsonData = try? String(contentsOfFile: previewContentFilePath).data(using: .utf8) { + return try? JSONDecoder().decode(WidgetFeaturedContent.self, from: jsonData) + } + + return nil + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Widget/Models/WidgetSettings.swift b/Apps/Wikipedia/WMF Framework/Widget/Models/WidgetSettings.swift new file mode 100644 index 0000000..c30d7e8 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Widget/Models/WidgetSettings.swift @@ -0,0 +1,21 @@ +import Foundation + +public struct WidgetSettings: Codable { + + // MARK: - Properties + + public static let `default` = WidgetSettings(siteURL: URL(string: "https://en.wikipedia.org")!, languageCode: "en", languageVariantCode: nil) + + public let siteURL: URL + public let languageCode: String + public let languageVariantCode: String? + + // MARK: - Public + + public init(siteURL: URL, languageCode: String, languageVariantCode: String?) { + self.siteURL = siteURL + self.languageCode = languageCode + self.languageVariantCode = languageVariantCode + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Widget/WidgetContentFetcher.swift b/Apps/Wikipedia/WMF Framework/Widget/WidgetContentFetcher.swift new file mode 100644 index 0000000..b4ed801 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Widget/WidgetContentFetcher.swift @@ -0,0 +1,74 @@ +import Foundation + +public final class WidgetContentFetcher { + + // MARK: - Nested Types + + public enum FetcherError: Error { + case urlFailure + case contentFailure + case unsupportedLanguage + } + + public typealias FeaturedContentResult = Result + + // MARK: - Properties + + public static let shared = WidgetContentFetcher() + + let session = Session(configuration: .current) + + // From supported language list at https://www.mediawiki.org/wiki/Wikifeeds + private let supportedFeaturedArticleLanguageCodes = ["bg", "bn", "bs", "cs", "de", "el", "en", "fa", "he", "hu", "ja", "la", "no", "sco", "sd", "sv", "ur", "vi", "zh"] + + // MARK: - Public - Featured Article Widget + + public func fetchFeaturedContent(forDate date: Date, siteURL: URL, languageCode: String, languageVariantCode: String? = nil, completion: @escaping (FeaturedContentResult) -> Void) { + guard supportedFeaturedArticleLanguageCodes.contains(languageCode) else { + completion(.failure(.unsupportedLanguage)) + return + } + + var featuredURL = WMFFeedContentFetcher.feedContentURL(forSiteURL: siteURL, on: date, configuration: .current) + featuredURL.wmf_languageVariantCode = languageVariantCode + + let task = session.dataTask(with: featuredURL) { data, _, error in + if let data = data, var decoded = try? JSONDecoder().decode(WidgetFeaturedContent.self, from: data) { + decoded.fetchDate = Date() + completion(.success(decoded)) + } else { + completion(.failure(.contentFailure)) + } + } + + guard let dataTask = task else { + completion(.failure(.urlFailure)) + return + } + + dataTask.resume() + } + + public func fetchImageDataFrom(imageSource: WidgetFeaturedContent.FeaturedArticleContent.ImageSource, completion: @escaping (Result) -> Void) { + guard let imageURL = URL(string: imageSource.source) else { + completion(.failure(.urlFailure)) + return + } + + let task = session.dataTask(with: imageURL) { data, _, error in + if let data = data { + completion(.success(data)) + } else { + completion(.failure(.contentFailure)) + } + } + + guard let dataTask = task else { + completion(.failure(.urlFailure)) + return + } + + dataTask.resume() + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Widget/WidgetController.swift b/Apps/Wikipedia/WMF Framework/Widget/WidgetController.swift new file mode 100644 index 0000000..f15537d --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Widget/WidgetController.swift @@ -0,0 +1,259 @@ +import Foundation +import WidgetKit +import CocoaLumberjackSwift + +@objc(WMFWidgetController) +public final class WidgetController: NSObject { + + // MARK: Nested Types + + public enum SupportedWidget: String { + case featuredArticle = "org.wikimedia.wikipedia.widgets.featuredArticle" + case onThisDay = "org.wikimedia.wikipedia.widgets.onThisDay" + case pictureOfTheDay = "org.wikimedia.wikipedia.widgets.potd" + case topRead = "org.wikimedia.wikipedia.widgets.topRead" + + public var identifier: String { + return self.rawValue + } + } + + // MARK: Properties + + @objc public static let shared = WidgetController() + private let sharedCache = SharedContainerCache(fileName: SharedContainerCacheCommonNames.widgetCache, defaultCache: { WidgetCache(settings: .default, featuredContent: nil) }) + + // MARK: Public + + @objc public func reloadAllWidgetsIfNecessary() { + guard !Bundle.main.isAppExtension else { + return + } + + let dataStore = MWKDataStore.shared() + let appLanguage = dataStore.languageLinkController.appLanguage + if let siteURL = appLanguage?.siteURL, let languageCode = appLanguage?.languageCode { + let updatedWidgetSettings = WidgetSettings(siteURL: siteURL, languageCode: languageCode, languageVariantCode: appLanguage?.languageVariantCode) + updateCacheWith(settings: updatedWidgetSettings) + } + + WidgetCenter.shared.reloadAllTimelines() + } + + public func reloadFeaturedArticleWidgetIfNecessary() { + guard !Bundle.main.isAppExtension else { + return + } + + let dataStore = MWKDataStore.shared() + let appLanguage = dataStore.languageLinkController.appLanguage + if let siteURL = appLanguage?.siteURL, let languageCode = appLanguage?.languageCode { + let updatedWidgetSettings = WidgetSettings(siteURL: siteURL, languageCode: languageCode, languageVariantCode: appLanguage?.languageVariantCode) + updateCacheWith(settings: updatedWidgetSettings) + } + + WidgetCenter.shared.reloadTimelines(ofKind: SupportedWidget.featuredArticle.rawValue) + } + + /// For requesting background time from widgets + /// - Parameter userCompletion: the completion block to call with the result + /// - Parameter task: block that takes the `MWKDataStore` to use for updates and the completion block to call when done as parameters + public func startWidgetUpdateTask(_ userCompletion: @escaping (T) -> Void, _ task: @escaping (MWKDataStore, @escaping (T) -> Void) -> Void) { + getRetainedSharedDataStore { dataStore in + task(dataStore, { result in + DispatchQueue.main.async { + self.releaseSharedDataStore { + userCompletion(result) + } + } + }) + } + } + + public func fetchNewestWidgetContentGroup(with kind: WMFContentGroupKind, in dataStore: MWKDataStore, isNetworkFetchAllowed: Bool, isAnyLanguageAllowed: Bool = false, completion: @escaping (WMFContentGroup?) -> Void) { + fetchCachedWidgetContentGroup(with: kind, isAnyLanguageAllowed: isAnyLanguageAllowed, in: dataStore) { (contentGroup) in + guard let todaysContentGroup = contentGroup, todaysContentGroup.isForToday else { + guard isNetworkFetchAllowed else { + completion(contentGroup) + return + } + self.updateFeedContent(in: dataStore) { + // if there's no cached content group after update, return nil + self.fetchCachedWidgetContentGroup(with: kind, isAnyLanguageAllowed: isAnyLanguageAllowed, in: dataStore, completion: completion) + } + return + } + completion(todaysContentGroup) + } + } + + private func fetchCachedWidgetContentGroup(with kind: WMFContentGroupKind, isAnyLanguageAllowed: Bool, in dataStore: MWKDataStore, completion: @escaping (WMFContentGroup?) -> Void) { + assert(Thread.isMainThread, "Cached widget content group must be fetched from the main queue") + let moc = dataStore.viewContext + let siteURL = isAnyLanguageAllowed ? dataStore.primarySiteURL : nil + completion(moc.newestGroup(of: kind, forSiteURL: siteURL)) + } + + public func updateFeedContent(in dataStore: MWKDataStore, completion: @escaping () -> Void) { + dataStore.feedContentController.performDeduplicatedFetch(completion) + } + + private var dataStoreRetainCount: Int = 0 + private var _dataStore: MWKDataStore? + private var completions: [(MWKDataStore) -> Void] = [] + private var isCreatingDataStore: Bool = false + private var backgroundActivity: NSObjectProtocol? + + /// Returns a `MWKDataStore`for use with widget updates. + /// Manages a shared instance and a reference count for use by multiple widgets. + /// Call `releaseSharedDataStore()` when finished with the data store. + private func getRetainedSharedDataStore(completion: @escaping (MWKDataStore) -> Void) { + assert(Thread.isMainThread, "Data store must be obtained from the main queue") + dataStoreRetainCount += 1 + if let dataStore = _dataStore { + completion(dataStore) + return + } + completions.append(completion) + guard !isCreatingDataStore else { + return + } + isCreatingDataStore = true + backgroundActivity = ProcessInfo.processInfo.beginActivity(options: [.background, .suddenTerminationDisabled, .automaticTerminationDisabled], reason: "Wikipedia Extension - " + UUID().uuidString) + let dataStore = MWKDataStore() + dataStore.performLibraryUpdates { + DispatchQueue.main.async { + self._dataStore = dataStore + self.isCreatingDataStore = false + self.completions.forEach { $0(dataStore) } + self.completions.removeAll() + } + } needsMigrateBlock: { + DDLogDebug("Needed a migration from the widgets") + } + } + + /// Releases the shared `MWKDataStore` returned by `getRetainedSharedDataStore()`. + private func releaseSharedDataStore(completion: @escaping () -> Void) { + assert(Thread.isMainThread, "Data store must be released from the main queue") + dataStoreRetainCount -= 1 + guard dataStoreRetainCount <= 0 else { + completion() + return + } + dataStoreRetainCount = 0 + let asyncBackgroundActivity = backgroundActivity + defer { + backgroundActivity = nil + } + let finishBackgroundActivity = { + if let asyncBackgroundActivity = asyncBackgroundActivity { + ProcessInfo.processInfo.endActivity(asyncBackgroundActivity) + } + } + guard let dataStoreToTeardown = _dataStore else { + completion() + finishBackgroundActivity() + return + } + _dataStore = nil + dataStoreToTeardown.teardown { + completion() + finishBackgroundActivity() + #if DEBUG + guard !self.isCreatingDataStore, self._dataStore == nil else { // Don't check open files if another MWKDataStore was created after this one was destroyed + return + } + let openFiles = self.openFilePaths() + let openSqliteFile = openFiles.first(where: { $0.hasSuffix(".sqlite") }) + assert(openSqliteFile == nil, "There should be no open sqlite files (which in our case are Core Data persistent stores) in the shared app container after the data store is released. The widget still has a lock on these files: \(openFiles)") + #endif + } + } + + #if DEBUG + /// From https://developer.apple.com/forums/thread/655225?page=2 + func openFilePaths() -> [String] { + (0..= 0 else { + return nil + } + // Return "?" for file descriptors not associated with a path, for + // example, a socket. + var path = [CChar](repeating: 0, count: Int(MAXPATHLEN)) + guard fcntl(fd, F_GETPATH, &path) >= 0 else { + return "?" + } + return String(cString: path) + } + } + #endif + +} + +/// When the old widget data loading model is removed, this should be moved out of this extension into the class itself and refactored (e.g. the properties here don't need to be computed). +public extension WidgetController { + + /// This is currently unused. It will be useful when we update the main app to also update the widget's cache when it performs any updates to the featured content in the explore feed. + func updateCacheWith(featuredContent: WidgetFeaturedContent) { + var updatedCache = sharedCache.loadCache() + updatedCache.featuredContent = featuredContent + sharedCache.saveCache(updatedCache) + } + + func updateCacheWith(settings: WidgetSettings) { + var updatedCache = sharedCache.loadCache() + updatedCache.settings = settings + sharedCache.saveCache(updatedCache) + } + + // MARK: - Featured Article Widget + + func fetchFeaturedContent(isSnapshot: Bool = false, completion: @escaping (WidgetContentFetcher.FeaturedContentResult) -> Void) { + func performCompletion(result: WidgetContentFetcher.FeaturedContentResult) { + DispatchQueue.main.async { + completion(result) + } + } + + let fetcher = WidgetContentFetcher.shared + var widgetCache = sharedCache.loadCache() + + guard !isSnapshot else { + let previewSnapshot = widgetCache.featuredContent ?? WidgetFeaturedContent.previewContent() ?? WidgetFeaturedContent(featuredArticle: nil) + performCompletion(result: .success(previewSnapshot)) + return + } + + // If cached data is still relevant, use it + if let cachedContent = widgetCache.featuredContent, let fetchDate = cachedContent.fetchDate, Calendar.current.isDateInToday(fetchDate), let cachedLanguageCode = cachedContent.featuredArticle?.languageCode, cachedLanguageCode == widgetCache.settings.languageCode, widgetCache.settings.languageVariantCode == cachedContent.fetchedLanguageVariantCode { + performCompletion(result: .success(cachedContent)) + return + } + + // Fetch fresh feed content from network + fetcher.fetchFeaturedContent(forDate: Date(), siteURL: widgetCache.settings.siteURL, languageCode: widgetCache.settings.languageCode, languageVariantCode: widgetCache.settings.languageVariantCode) { result in + switch result { + case .success(var featuredContent): + if let featuredArticleThumbnailImageSource = featuredContent.featuredArticle?.thumbnailImageSource { + fetcher.fetchImageDataFrom(imageSource: featuredArticleThumbnailImageSource) { imageResult in + featuredContent.featuredArticle?.thumbnailImageSource?.data = try? imageResult.get() + widgetCache.featuredContent = featuredContent + self.sharedCache.saveCache(widgetCache) + performCompletion(result: .success(featuredContent)) + } + } else { + widgetCache.featuredContent = featuredContent + self.sharedCache.saveCache(widgetCache) + performCompletion(result: .success(featuredContent)) + } + case .failure(let error): + self.sharedCache.saveCache(widgetCache) + performCompletion(result: .failure(error)) + } + } + } + +} diff --git a/Apps/Wikipedia/WMF Framework/Wikipedia 2.xcdatamodeld/Wikipedia 2.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Wikipedia 2.xcdatamodeld/Wikipedia 2.xcdatamodel/contents new file mode 100644 index 0000000..762c50a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Wikipedia 2.xcdatamodeld/Wikipedia 2.xcdatamodel/contents @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/.xccurrentversion b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/.xccurrentversion new file mode 100644 index 0000000..96137ba --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ + + + + + _XCCurrentVersionName + Wikipedia 6.xcdatamodel + + diff --git a/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 2.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 2.xcdatamodel/contents new file mode 100644 index 0000000..5d7f122 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 2.xcdatamodel/contents @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 3.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 3.xcdatamodel/contents new file mode 100644 index 0000000..cfbf4ec --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 3.xcdatamodel/contents @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 4.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 4.xcdatamodel/contents new file mode 100644 index 0000000..290e10a --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 4.xcdatamodel/contents @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 5.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 5.xcdatamodel/contents new file mode 100644 index 0000000..2aa4a77 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 5.xcdatamodel/contents @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 6.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 6.xcdatamodel/contents new file mode 100644 index 0000000..e28b9c1 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia 6.xcdatamodel/contents @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia.xcdatamodel/contents b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia.xcdatamodel/contents new file mode 100644 index 0000000..53415f5 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/Wikipedia.xcdatamodeld/Wikipedia.xcdatamodel/contents @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/WMF Framework/WikipediaSiteInfo.swift b/Apps/Wikipedia/WMF Framework/WikipediaSiteInfo.swift new file mode 100644 index 0000000..0be4c70 --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WikipediaSiteInfo.swift @@ -0,0 +1,13 @@ +import Foundation + +@objc public class WikipediaSiteInfo: NSObject { + static let maxage = 86400 // https://phabricator.wikimedia.org/T245033 + @objc public static let defaultRequestParameters: [String: Any] = [ + "action": "query", + "meta": "siteinfo", + "format": "json", + "formatversion": "2", + "maxage": maxage, + "smaxage": maxage + ] +} diff --git a/Apps/Wikipedia/WMF Framework/WorkerController.swift b/Apps/Wikipedia/WMF Framework/WorkerController.swift new file mode 100644 index 0000000..4ba04ea --- /dev/null +++ b/Apps/Wikipedia/WMF Framework/WorkerController.swift @@ -0,0 +1,14 @@ +import Foundation + +@objc(WMFWorkerControllerDelegate) public protocol WorkerControllerDelegate: NSObjectProtocol { + func workerControllerWillStart(_ periodicWorkerController: WorkerController, workWithIdentifier: String) + func workerControllerDidEnd(_ periodicWorkerController: WorkerController, workWithIdentifier: String) +} + +@objc(WMFWorkerController) public class WorkerController: NSObject { + @objc weak var delegate: WorkerControllerDelegate? + + @objc func cancelWorkWithIdentifier(_ identifier: String) { + // subclassers should override + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/AccentColor.colorset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..092ebbf --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,15 @@ +{ + "colors" : [ + { + "color" : { + "platform" : "ios", + "reference" : "linkColor" + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/Attribution.imageset/Attribution.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/Attribution.imageset/Attribution.pdf new file mode 100644 index 0000000..8deee1a Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/Attribution.imageset/Attribution.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/Attribution.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/Attribution.imageset/Contents.json new file mode 100644 index 0000000..0958b0b --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/Attribution.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Attribution.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-by.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-by.imageset/Contents.json new file mode 100644 index 0000000..366c6f7 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-by.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-by.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-by.imageset/license-by.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-by.imageset/license-by.pdf new file mode 100644 index 0000000..38c58ce Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-by.imageset/license-by.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc.imageset/Contents.json new file mode 100644 index 0000000..63a1fba --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-cc.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc.imageset/license-cc.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc.imageset/license-cc.pdf new file mode 100644 index 0000000..4bc9c52 Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc.imageset/license-cc.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc0.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc0.imageset/Contents.json new file mode 100644 index 0000000..5a7e9ad --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc0.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-zero.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc0.imageset/license-zero.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc0.imageset/license-zero.pdf new file mode 100644 index 0000000..30f110f Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-cc0.imageset/license-zero.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-generic.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-generic.imageset/Contents.json new file mode 100644 index 0000000..be8dbd0 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-generic.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-Generic GNU Free.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-generic.imageset/license-Generic GNU Free.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-generic.imageset/license-Generic GNU Free.pdf new file mode 100644 index 0000000..a0a6cf2 Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-generic.imageset/license-Generic GNU Free.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-no-derivatives.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-no-derivatives.imageset/Contents.json new file mode 100644 index 0000000..8225ab3 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-no-derivatives.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-NoDerivatives.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-no-derivatives.imageset/license-NoDerivatives.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-no-derivatives.imageset/license-NoDerivatives.pdf new file mode 100644 index 0000000..699c559 Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-no-derivatives.imageset/license-NoDerivatives.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-eu.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-eu.imageset/Contents.json new file mode 100644 index 0000000..6bc8d92 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-eu.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-NonCommercial-EU.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-eu.imageset/license-NonCommercial-EU.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-eu.imageset/license-NonCommercial-EU.pdf new file mode 100644 index 0000000..3bde69b Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-eu.imageset/license-NonCommercial-EU.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-jp.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-jp.imageset/Contents.json new file mode 100644 index 0000000..14fb83d --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-jp.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-NonCommercial-JP.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-jp.imageset/license-NonCommercial-JP.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-jp.imageset/license-NonCommercial-JP.pdf new file mode 100644 index 0000000..b0d3bf9 Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial-jp.imageset/license-NonCommercial-JP.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial.imageset/Contents.json new file mode 100644 index 0000000..c53d387 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-NonCommercial.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial.imageset/license-NonCommercial.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial.imageset/license-NonCommercial.pdf new file mode 100644 index 0000000..a7c664b Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-non-commercial.imageset/license-NonCommercial.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-pd.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-pd.imageset/Contents.json new file mode 100644 index 0000000..7c4fd97 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-pd.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-Public domain.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-pd.imageset/license-Public domain.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-pd.imageset/license-Public domain.pdf new file mode 100644 index 0000000..29dba44 Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-pd.imageset/license-Public domain.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-remix.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-remix.imageset/Contents.json new file mode 100644 index 0000000..36efb46 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-remix.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-remix.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-remix.imageset/license-remix.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-remix.imageset/license-remix.pdf new file mode 100644 index 0000000..4a54276 Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-remix.imageset/license-remix.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sa.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sa.imageset/Contents.json new file mode 100644 index 0000000..aefbf3a --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sa.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-ShareAlike.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sa.imageset/license-ShareAlike.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sa.imageset/license-ShareAlike.pdf new file mode 100644 index 0000000..69483b8 Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sa.imageset/license-ShareAlike.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling-plus.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling-plus.imageset/Contents.json new file mode 100644 index 0000000..7a2fae7 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling-plus.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-Sampling plus.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling-plus.imageset/license-Sampling plus.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling-plus.imageset/license-Sampling plus.pdf new file mode 100644 index 0000000..828185c Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling-plus.imageset/license-Sampling plus.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling.imageset/Contents.json new file mode 100644 index 0000000..a86f742 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-sampling.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling.imageset/license-sampling.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling.imageset/license-sampling.pdf new file mode 100644 index 0000000..9471fe9 Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-sampling.imageset/license-sampling.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-share.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-share.imageset/Contents.json new file mode 100644 index 0000000..eda0f62 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-share.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "license-share.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-share.imageset/license-share.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-share.imageset/license-share.pdf new file mode 100644 index 0000000..39e8488 Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/Image Attribution/license-share.imageset/license-share.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/PictureOfTheYear_2019.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/PictureOfTheYear_2019.imageset/Contents.json new file mode 100644 index 0000000..d23056a --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/PictureOfTheYear_2019.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "PictureOfTheYear_2019.jpg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/PictureOfTheYear_2019.imageset/PictureOfTheYear_2019.jpg b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/PictureOfTheYear_2019.imageset/PictureOfTheYear_2019.jpg new file mode 100644 index 0000000..006ba1d Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/Picture of the day/PictureOfTheYear_2019.imageset/PictureOfTheYear_2019.jpg differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/W.imageset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/W.imageset/Contents.json new file mode 100644 index 0000000..5b7e323 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/W.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "W.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/W.imageset/W.pdf b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/W.imageset/W.pdf new file mode 100644 index 0000000..a35c7f1 Binary files /dev/null and b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/W.imageset/W.pdf differ diff --git a/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/WidgetBackground.colorset/Contents.json b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 0000000..462830f --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,15 @@ +{ + "colors" : [ + { + "color" : { + "platform" : "ios", + "reference" : "systemBackgroundColor" + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apps/Wikipedia/Widgets/Extension/Sample Content/Featured Article Widget Preview Content.json b/Apps/Wikipedia/Widgets/Extension/Sample Content/Featured Article Widget Preview Content.json new file mode 100644 index 0000000..0ee0a68 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Sample Content/Featured Article Widget Preview Content.json @@ -0,0 +1,21 @@ +{ + "tfa": { + "thumbnail": { + "data": "/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAADIKADAAQAAAABAAAC+AAAAAD/wAARCAL4AyADAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9sAQwABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB/9sAQwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB/90ABABk/9oADAMBAAIRAxEAPwD/AD/6ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAP//Q/wA/+gAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD//0f8AP/oAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAUDJA/z/Mfz/OgCTyxnqcfQY/9GZoAXy19f0/+2UAHlr6/p/8AbKADy19f0/8AtlAB5a+v6f8A2ygA8tfX9P8A7ZQAeWvr+n/2ygA8tfX9P/tlAB5a+v6f/bKADy19f0/+2UAHlr6/p/8AbKADy19f0/8AtlAB5a+v6f8A2ygA8tfX9P8A7ZQAeWvr+n/2ygA8tfX9P/tlAB5a+v6f/bKADy19f0/+2UAHlr6/p/8AbKADy19f0/8AtlAB5a+v6f8A2ygA8tfX9P8A7ZQAeWvr+n/2ygA8tfX9P/tlAB5a+v6f/bKADy19f0/+2UAHlr6/p/8AbKAGmPngn8h/WSgBdg/yMf8As5Ht9/j0GPnAE8v3/T/7YP5/lQA7y19f0/8AtlAERGDj/I/9C/r698UAJQAUAFAH/9L/AD/6ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAHr+vXj+X9Px79UAJaACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAIX+8fw/lQA2gAoAKAP/T/wA/+gAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA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KACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA//9P/AD/6ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAP//U/wA/+gAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD//1f8AP/oAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA//9k=", + "source": "https://upload.wikimedia.org/wikipedia/commons/e/e1/FullMoon2010.jpg", + "width": 800, + "height": 760 + }, + "content_urls": { + "desktop": { + "page": "https://en.wikipedia.org/wiki/Moon" + } + }, + "lang": "en", + "description": "Earth's natural satellite", + "displaytitle": "Moon", + "dir": "ltr", + "extract": "The Moon is Earth's only permanent natural satellite and the fifth largest moon in the Solar System. The average centre-to-centre distance from the Earth to the Moon is 384,403 kilometres (238,857 miles). The gravitational pull at its surface is about a sixth of Earth's. The Moon makes a complete orbit around the Earth every 27.3 days, and the periodic variations in the geometry of the Earth–Moon–Sun system are responsible for the lunar phases that repeat every 29.5 days. The gravitational, centripetal forces generated by the rotation of the Moon and Earth around a common axis, the barycentre, are largely responsible for the tides on Earth. The Moon is the only celestial body that humans have traveled to and landed on. The first artificial object to escape Earth's gravity and pass near the Moon was the Soviet Union's Luna 1, the first artificial object to impact the lunar surface was Luna 2, and the first photographs of the normally occluded far side of the Moon were made by Luna 3, all in 1959. The U.S. Apollo program has achieved the first (and only) manned missions to date, resulting in six landings between 1969 and 1972." + }, + "fetchDate": 649023094.341888 +} \ No newline at end of file diff --git a/Apps/Wikipedia/Widgets/Extension/Widgets.swift b/Apps/Wikipedia/Widgets/Extension/Widgets.swift new file mode 100644 index 0000000..8632a4a --- /dev/null +++ b/Apps/Wikipedia/Widgets/Extension/Widgets.swift @@ -0,0 +1,15 @@ +import WidgetKit +import SwiftUI + +@main +struct WikipediaWidgets: WidgetBundle { + + @WidgetBundleBuilder + var body: some Widget { + PictureOfTheDayWidget() + OnThisDayWidget() + TopReadWidget() + FeaturedArticleWidget() + } + +} diff --git a/Apps/Wikipedia/Widgets/Info.plist b/Apps/Wikipedia/Widgets/Info.plist new file mode 100644 index 0000000..419573c --- /dev/null +++ b/Apps/Wikipedia/Widgets/Info.plist @@ -0,0 +1,29 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Widgets + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 7.3.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/Apps/Wikipedia/Widgets/Utilities/CGPoint+Extensions.swift b/Apps/Wikipedia/Widgets/Utilities/CGPoint+Extensions.swift new file mode 100644 index 0000000..6aa76c8 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Utilities/CGPoint+Extensions.swift @@ -0,0 +1,17 @@ +import CoreGraphics + +/// Adapted from WMFSparklineView.swift +extension CGPoint { + + static func midPointFrom(_ fromPoint: CGPoint, to toPoint: CGPoint) -> CGPoint { + return CGPoint(x: 0.5*(fromPoint.x + toPoint.x), y: 0.5*(fromPoint.y + toPoint.y)) + } + + static func quadCurveControlPointFrom(_ fromPoint: CGPoint, to toPoint: CGPoint) -> CGPoint { + var controlPoint = midPointFrom(fromPoint, to: toPoint) + let deltaY = toPoint.y - controlPoint.y + controlPoint.y += deltaY + return controlPoint + } + +} diff --git a/Apps/Wikipedia/Widgets/Utilities/Date+Extensions.swift b/Apps/Wikipedia/Widgets/Utilities/Date+Extensions.swift new file mode 100644 index 0000000..636706a --- /dev/null +++ b/Apps/Wikipedia/Widgets/Utilities/Date+Extensions.swift @@ -0,0 +1,13 @@ +import Foundation + +extension Date { + + /// Return a randomized `Date` in the next midnight hour. Will return nil if a `Date` can't be constructed given the calendar + /// and component constraints, though in practice will likely always return some value. + func randomDateShortlyAfterMidnight(calendar: Calendar = .current) -> Date? { + let components = DateComponents(day: 1, minute: .random(in: 0..<30), second: .random(in: 0..<60)) + let startOfDay = calendar.startOfDay(for: self) + return calendar.date(byAdding: components, to: startOfDay) + } + +} diff --git a/Apps/Wikipedia/Widgets/Utilities/UIColor+Extensions.swift b/Apps/Wikipedia/Widgets/Utilities/UIColor+Extensions.swift new file mode 100644 index 0000000..7a38000 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Utilities/UIColor+Extensions.swift @@ -0,0 +1,9 @@ +import SwiftUI + +extension UIColor { + + var asColor: Color { + return Color(self) + } + +} diff --git a/Apps/Wikipedia/Widgets/Utilities/UIImage+Extensions.swift b/Apps/Wikipedia/Widgets/Utilities/UIImage+Extensions.swift new file mode 100644 index 0000000..3757ea9 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Utilities/UIImage+Extensions.swift @@ -0,0 +1,22 @@ +import UIKit +import AVFoundation + +extension UIImage { + + /// Scale image to fit within target size while maintaining aspect ratio + func scaleImageToFit(targetSize: CGSize) -> UIImage? { + let aspectRatioRect = AVFoundation.AVMakeRect(aspectRatio: size, insideRect: CGRect(origin: .zero, size: targetSize)) + let availableSize = aspectRatioRect.size + + let format = UIGraphicsImageRendererFormat() + format.scale = 1 + format.opaque = true + let renderer = UIGraphicsImageRenderer(size: availableSize, format: format) + let resizedImage = renderer.image { context in + draw(in: CGRect(origin: .zero, size: availableSize)) + } + + return resizedImage + } + +} diff --git a/Apps/Wikipedia/Widgets/Utilities/View+Extensions.swift b/Apps/Wikipedia/Widgets/Utilities/View+Extensions.swift new file mode 100644 index 0000000..e6a8cbe --- /dev/null +++ b/Apps/Wikipedia/Widgets/Utilities/View+Extensions.swift @@ -0,0 +1,25 @@ +import SwiftUI + +extension View { + + func readableShadow(intensity: Double = 0.80) -> some View { + return self.shadow(color: Color.black.opacity(intensity), radius: 5, x:0, y: 0) + } + + /// Adds an iOS-version dependent `List` background `Color` + /// - Parameters: + /// - color: `Color` to use as background + /// - edges: safe area edges to ignore + /// - Returns: a modified `View` with the desired background `Color` applied + @ViewBuilder + func listBackgroundColor(_ color: Color, ignoringSafeAreaEdges edges: Edge.Set = .all) -> some View { + if #available(iOS 16, *) { + self + .scrollContentBackground(.hidden) + .background(color).edgesIgnoringSafeArea(edges) + } else { + self.background(color).edgesIgnoringSafeArea(edges) + } + } + +} diff --git a/Apps/Wikipedia/Widgets/Views/Sparkline.swift b/Apps/Wikipedia/Widgets/Views/Sparkline.swift new file mode 100644 index 0000000..405b307 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Views/Sparkline.swift @@ -0,0 +1,216 @@ +import SwiftUI +import WMF + +private struct SparklineShape: Shape { + + // MARK: Private Properties + + private let data: [CGFloat] + + // MARK: Public + + init(data: [NSNumber]?) { + self.data = data?.compactMap { CGFloat($0.doubleValue) } ?? [] + } + + // MARK: Shape + + func path(in rect: CGRect) -> Path { + var path = Path() + + guard data.count > 1, let min = data.min(), let max = data.max() else { + return path + } + + guard min != max else { + path.move(to: CGPoint(x: rect.minX, y: rect.midY)) + path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY)) + return path + } + + let minY = rect.minY + let width = rect.width / CGFloat(data.count - 1) + let height = rect.maxY - rect.minY + + var points: [CGPoint] = [] + + for (index, dataPoint) in data.enumerated() { + let relativeY = dataPoint - CGFloat(min) + let normalizedY = 1 - relativeY/(CGFloat(max-min)) + let y = minY + height * normalizedY + let x = width * CGFloat(index) + points.append(CGPoint(x: x, y: y)) + } + + path.move(to: points[0]) + for (index, point) in points[1...points.count-1].enumerated() { + let fromPoint = points[index] + let midPoint = CGPoint.midPointFrom(fromPoint, to: point) + let midPointControlPoint = CGPoint.quadCurveControlPointFrom(midPoint, to: fromPoint) + path.addQuadCurve(to: midPoint, control: midPointControlPoint) + let toPointControlPoint = CGPoint.quadCurveControlPointFrom(midPoint, to: point) + path.addQuadCurve(to: point, control: toPointControlPoint) + } + + return path + } + +} + +struct SparklineGrid: View { + @Environment(\.colorScheme) private var colorScheme + + // MARK: Properties + + var gridStyle: Sparkline.GridStyle + + // MARK: View + + var body: some View { + switch gridStyle { + case .horizontal: + GeometryReader { proxy in + let yOffset = proxy.size.height / 2.0 + + Path { path in + path.move(to: CGPoint(x: 0, y: yOffset)) + path.addLine(to: CGPoint(x: proxy.size.width, y: yOffset)) + + path.move(to: CGPoint(x: 0, y: yOffset / 2.0)) + path.addLine(to: CGPoint(x: proxy.size.width, y: yOffset / 2.0)) + + path.move(to: CGPoint(x: 0, y: yOffset * 1.5)) + path.addLine(to: CGPoint(x: proxy.size.width, y: yOffset * 1.5)) + } + .stroke(style: StrokeStyle(lineWidth: 1.0, lineCap: .round)) + } + case .horizontalAndVertical: + ZStack { + VStack { + Spacer() + Rectangle().frame(height: 1) + Spacer() + Rectangle().frame(height: 1) + Spacer() + Rectangle().frame(height: 1) + Spacer() + Rectangle().frame(height: 1) + Spacer() + } + HStack { + Spacer() + Rectangle().frame(width: 1) + Spacer() + Rectangle().frame(width: 1) + Spacer() + Rectangle().frame(width: 1) + Spacer() + } + } + } + } +} + +struct Sparkline: View { + @Environment(\.colorScheme) private var colorScheme + + // MARK: Nested Types + + enum Style { + case compact + case compactWithViewCount + case expanded + } + + enum GridStyle { + case horizontal + case horizontalAndVertical + } + + // MARK: Properties + + var style: Style = .compact + var gridStyle: GridStyle = .horizontal + var lineWidth: CGFloat { + return style == .expanded ? 2.25 : 1.5 + } + + var timeSeries: [NSNumber]? = [] + var containerBackgroundColor: Color { + switch style { + case .compactWithViewCount: + return colorScheme == .dark ? Color.white.opacity(0.12) : Color(red: 248/255.0, green: 248/255.0, blue: 250/255.0, opacity: 1) + case .compact: + return colorScheme == .dark ? .black : .white + case .expanded: + return colorScheme == .dark ? .black : .white + } + } + + var gradientStartColor: Color { + colorScheme == .light + ? Theme.light.colors.rankGradientStart.asColor + : Theme.dark.colors.rankGradientStart.asColor + } + + var gradientEndColor: Color { + colorScheme == .light + ? Theme.light.colors.rankGradientEnd.asColor + : Theme.dark.colors.rankGradientEnd.asColor + } + + // MARK: View + + var body: some View { + if style == .compact || style == .compactWithViewCount { + HStack { + Spacer().frame(width: 4) + ZStack { + SparklineGrid(gridStyle: .horizontal) + .foregroundColor(colorScheme == .dark + ? Color(.sRGB, red: 55/255.0, green: 55/255.0, blue: 55/255.0, opacity: 1) + : Color(.sRGB, red: 235/255.0, green: 235/255.0, blue: 235/255.0, opacity: 1) + ) + .layoutPriority(-1) + SparklineShape(data: timeSeries) + .stroke( + LinearGradient(gradient: Gradient(colors: [gradientStartColor, gradientEndColor]), startPoint: /*@START_MENU_TOKEN@*/.leading/*@END_MENU_TOKEN@*/, endPoint: /*@START_MENU_TOKEN@*/.trailing/*@END_MENU_TOKEN@*/), style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round)) + .frame(width: style == .compact ? 22 : 30, alignment: .leading) + .padding([.top, .bottom, .leading, .trailing], 3) + } + if style == .compactWithViewCount { + Text("\(currentViewCountOrEmpty)") + .font(.system(size: 12)) + .fontWeight(.medium) + .foregroundColor(Theme.light.colors.rankGradientEnd.asColor) + } + Spacer().frame(width: 4) + } + .background(containerBackgroundColor) + } else { + ZStack { + SparklineGrid(gridStyle: .horizontalAndVertical) + .foregroundColor(colorScheme == .dark + ? Color(.sRGB, red: 55/255.0, green: 55/255.0, blue: 55/255.0, opacity: 1) + : Color(.sRGB, red: 235/255.0, green: 235/255.0, blue: 235/255.0, opacity: 1) + ) + SparklineShape(data: timeSeries) + .stroke( + LinearGradient(gradient: Gradient(colors: [gradientStartColor, gradientEndColor]), startPoint: /*@START_MENU_TOKEN@*/.leading/*@END_MENU_TOKEN@*/, endPoint: /*@START_MENU_TOKEN@*/.trailing/*@END_MENU_TOKEN@*/), style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round)) + .padding([.top, .bottom, .leading, .trailing], 8) + } + .background(containerBackgroundColor) + } + } + + // MARK: Private + + private var currentViewCountOrEmpty: String { + guard let currentViewCount = timeSeries?.last else { + return "–" + } + + return NumberFormatter.localizedThousandsStringFromNumber(currentViewCount) + } + +} diff --git a/Apps/Wikipedia/Widgets/Widgets/FeaturedArticleWidget+LocalizedStrings.swift b/Apps/Wikipedia/Widgets/Widgets/FeaturedArticleWidget+LocalizedStrings.swift new file mode 100644 index 0000000..119c66c --- /dev/null +++ b/Apps/Wikipedia/Widgets/Widgets/FeaturedArticleWidget+LocalizedStrings.swift @@ -0,0 +1,25 @@ +import Foundation +import WMF + +extension FeaturedArticleWidget { + + enum LocalizedStrings { + static let widgetTitle = WMFLocalizedString("featured-widget-title", value:"Featured article", comment: "Text for title of Featured article widget.") + static let widgetDescription = WMFLocalizedString("featured-widget-description", value:"Discover the best articles on Wikipedia, selected daily by our community.", comment: "Text for description of Featured article widget displayed when adding to home screen.") + + static let widgetContentFailure = WMFLocalizedString("featured-widget-content-failure-for-date", value:"A featured article is not available for this date.", comment: "Text displayed when Featured article is not available on the current date.") + static let widgetLanguageFailure = WMFLocalizedString("featured-widget-language-failure", value: "Your primary Wikipedia language does not support Featured article. You can update your primary Wikipedia in the app’s Settings menu.", comment: "Error message shown when the user's primary language Wikipedia does not support the 'Featured article' feature.") + + static let fromLanguageWikipedia = WMFLocalizedString("featured-widget-from-language-wikipedia", value: "From %1$@ Wikipedia", comment: "Text displayed as Wikipedia source on Featured article widget. %1$@ will be replaced with the language.") + static let fromWikipediaDefault = WMFLocalizedString("featured-widget-from-wikipedia", value: "From Wikipedia", comment: "Text displayed as Wikipedia source on Featured article widget if language is unavailable.") + + static func fromLanguageWikipediaTextFor(languageCode: String?) -> String { + guard let languageCode = languageCode, let localizedLanguageString = Locale.current.localizedString(forLanguageCode: languageCode) else { + return FeaturedArticleWidget.LocalizedStrings.fromWikipediaDefault + } + + return String.localizedStringWithFormat(FeaturedArticleWidget.LocalizedStrings.fromLanguageWikipedia, localizedLanguageString) + } + } + +} diff --git a/Apps/Wikipedia/Widgets/Widgets/FeaturedArticleWidget.swift b/Apps/Wikipedia/Widgets/Widgets/FeaturedArticleWidget.swift new file mode 100644 index 0000000..b568f84 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Widgets/FeaturedArticleWidget.swift @@ -0,0 +1,309 @@ +import SwiftUI +import WidgetKit +import WMF +import UIKit + +// MARK: - Widget + +struct FeaturedArticleWidget: Widget { + private let kind: String = WidgetController.SupportedWidget.featuredArticle.identifier + + public var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: FeaturedArticleProvider(), content: { entry in + FeaturedArticleView(entry: entry) + }) + .configurationDisplayName(FeaturedArticleWidget.LocalizedStrings.widgetTitle) + .description(FeaturedArticleWidget.LocalizedStrings.widgetDescription) + .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) + } +} + +// MARK: - Timeline Entry + +struct FeaturedArticleEntry: TimelineEntry { + + // MARK: - Properties + + var date: Date + var content: WidgetFeaturedContent? + var fetchError: WidgetContentFetcher.FetcherError? + + // MARK: - Computed Properties + + var hasDisplayableContent: Bool { + return fetchError == nil && content?.featuredArticle != nil + } + + var fetchedLanguageCode: String? { + return content?.featuredArticle?.languageCode + } + + var title: String { + return (content?.featuredArticle?.displayTitle as NSString?)?.wmf_stringByRemovingHTML() ?? "" + } + + var description: String { + return content?.featuredArticle?.description ?? "" + } + + var extract: String { + return content?.featuredArticle?.extract ?? "" + } + + var layoutDirection: LayoutDirection { + if let direction = content?.featuredArticle?.languageDirection { + return direction == "rtl" ? .rightToLeft : .leftToRight + } + + return .leftToRight + } + + var contentURL: URL? { + guard let page = content?.featuredArticle?.contentURL.desktop.page else { + return nil + } + + return URL(string: page) + } + + var imageData: Data? { + return content?.featuredArticle?.thumbnailImageSource?.data + } + +} + +// MARK: - Timeline Provider + +struct FeaturedArticleProvider: TimelineProvider { + typealias Entry = FeaturedArticleEntry + + func placeholder(in context: Context) -> FeaturedArticleEntry { + return FeaturedArticleEntry(date: Date(), content: nil) + } + + func getSnapshot(in context: Context, completion: @escaping (FeaturedArticleEntry) -> Void) { + WidgetController.shared.fetchFeaturedContent(isSnapshot: context.isPreview) { result in + let currentDate = Date() + switch result { + case .success(let featuredContent): + completion(FeaturedArticleEntry(date: currentDate, content: featuredContent)) + case .failure(let fetchError): + completion(FeaturedArticleEntry(date: currentDate, content: nil, fetchError: fetchError)) + } + } + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { + WidgetController.shared.fetchFeaturedContent { result in + let currentDate = Date() + switch result { + case .success(let featuredContent): + completion(Timeline(entries: [FeaturedArticleEntry(date: currentDate, content: featuredContent)], policy: .after(currentDate.randomDateShortlyAfterMidnight() ?? currentDate))) + case .failure(let fetchError): + completion(Timeline(entries: [FeaturedArticleEntry(date: currentDate, content: nil, fetchError: fetchError)], policy: .atEnd)) + } + } + } +} + +// MARK: - View + +struct FeaturedArticleView: View { + @Environment(\.widgetFamily) private var widgetFamily + @Environment(\.colorScheme) private var colorScheme + + var entry: FeaturedArticleEntry + + var headerCaptionText: String { + switch widgetFamily { + case .systemLarge: + return FeaturedArticleWidget.LocalizedStrings.fromLanguageWikipediaTextFor(languageCode: entry.fetchedLanguageCode) + default: + return FeaturedArticleWidget.LocalizedStrings.widgetTitle + } + } + + var headerTitleText: String { + switch widgetFamily { + case .systemLarge: + return FeaturedArticleWidget.LocalizedStrings.widgetTitle + default: + return entry.title + } + } + + var backgroundImage: UIImage? { + guard let imageData = entry.imageData else { + return nil + } + + return UIImage(data: imageData) + } + + // MARK: - Nested Views + + @ViewBuilder + var content: some View { + switch widgetFamily { + case .systemLarge: + largeWidgetContent + default: + smallWidgetContent + } + } + + var smallWidgetContent: some View { + headerData + .background(Color(colorScheme == .light ? Theme.light.colors.paperBackground : Theme.dark.colors.paperBackground)) + } + + var largeWidgetContent: some View { + GeometryReader { proxy in + VStack(spacing: 0) { + headerData + .frame(height: proxy.size.height / 2.25) + .clipped() + bodyData + } + } + .background(Color(colorScheme == .light ? Theme.light.colors.paperBackground : Theme.dark.colors.paperBackground)) + } + + var headerData: some View { + ZStack { + headerBackground + VStack(alignment: .leading, spacing: 4) { + Spacer() + HStack { + Text(headerCaptionText) + .font(.caption2) + .fontWeight(.bold) + .foregroundColor(.white) + .readableShadow(intensity: 0.8) + Spacer() + } + HStack { + Text(headerTitleText) + .font(.headline) + .foregroundColor(.white) + .readableShadow(intensity: 0.8) + Spacer() + } + } + .padding() + .background( + Rectangle() + .foregroundColor(.black) + .mask(LinearGradient(gradient: Gradient(colors: [.clear, .black]), startPoint: .center, endPoint: .bottom)) + .opacity(0.35) + ) + } + } + + var bodyData: some View { + VStack(alignment: .leading, spacing: 4) { + HStack { + Text(entry.title) + .foregroundColor(Color(colorScheme == .light ? Theme.light.colors.primaryText : Theme.dark.colors.primaryText)) + .font(.custom("Georgia", size: 21, relativeTo: .title)) + Spacer() + } + HStack { + Text(entry.description) + .foregroundColor(Color(colorScheme == .light ? Theme.light.colors.secondaryText : Theme.dark.colors.secondaryText)) + .font(.caption) + Spacer() + } + Spacer() + .frame(height: 8) + HStack { + Text(entry.extract) + .foregroundColor(Color(colorScheme == .light ? Theme.light.colors.primaryText : Theme.dark.colors.primaryText)) + .font(.caption) + .lineLimit(5) + .lineSpacing(4) + .truncationMode(.tail) + } + } + .padding() + } + + @ViewBuilder + var headerBackground: some View { + GeometryReader { proxy in + if let backgroundImage = backgroundImage { + Image(uiImage: backgroundImage) + .resizable() + .aspectRatio(contentMode: .fill) + } else { + ZStack { + Rectangle() + .foregroundColor(Color(UIColor.blue600)) + Text(entry.extract) + .font(.headline) + .fontWeight(.semibold) + .lineSpacing(6) + .foregroundColor(Color.black.opacity(0.15)) + .frame(width: proxy.size.width * 1.25, height: proxy.size.height * 2, alignment: .topLeading) + .padding(EdgeInsets(top: 16, leading: 10, bottom: 0, trailing: 0)) + } + } + } + } + + func noContent(message: String) -> some View { + Rectangle() + .foregroundColor(Color(UIColor.gray500)) + .overlay( + Text(message) + .font(.caption) + .bold() + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading) + .foregroundColor(.white) + .padding() + ) + } + + @ViewBuilder + var widgetBody: some View { + if entry.hasDisplayableContent { + content + .overlay(FeaturedArticleOverlayView()) + } else if entry.fetchError == .unsupportedLanguage { + noContent(message: FeaturedArticleWidget.LocalizedStrings.widgetLanguageFailure) + } else { + noContent(message: FeaturedArticleWidget.LocalizedStrings.widgetContentFailure) + } + } + + // MARK: - Body + + var body: some View { + widgetBody + .widgetURL(entry.contentURL) + .environment(\.layoutDirection, entry.layoutDirection) + .flipsForRightToLeftLayoutDirection(true) + } +} + +struct FeaturedArticleOverlayView: View { + var body: some View { + VStack(alignment: .trailing) { + HStack(alignment: .top) { + Spacer() + Image("W") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 16, alignment: .trailing) + .foregroundColor(.white) + .padding(EdgeInsets(top: 16, leading: 0, bottom: 0, trailing: 16)) + .readableShadow() + } + Spacer() + .readableShadow() + .padding(EdgeInsets(top: 0, leading: 16, bottom: 16, trailing: 45)) + } + .foregroundColor(.white) + } +} diff --git a/Apps/Wikipedia/Widgets/Widgets/OnThisDayView.swift b/Apps/Wikipedia/Widgets/Widgets/OnThisDayView.swift new file mode 100644 index 0000000..f78d815 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Widgets/OnThisDayView.swift @@ -0,0 +1,452 @@ +import WidgetKit +import SwiftUI +import WMF +import UIKit + +// MARK: - Views + +struct OnThisDayColors { + static func blueColor(_ colorScheme: ColorScheme) -> Color { + return colorScheme == .light ? Color(.blue600) : Color(.blue300) + } + + static func grayColor(_ colorScheme: ColorScheme) -> Color { + return colorScheme == .light ? Color(.gray500) : Color(.gray300) + } + + static func widgetBackgroundColor(_ colorScheme: ColorScheme) -> Color { + return colorScheme == .light ? .white : .black + } + + static func boxShadowColor(_ colorScheme: ColorScheme) -> Color { + return colorScheme == .light ? Color(.gray300.withAlphaComponent(0.55)) : .clear + } + + static func boxBackgroundColor(_ colorScheme: ColorScheme) -> Color { + return colorScheme == .light ? .white : Color(.gray700) + } +} + +struct OnThisDayView: View { + @Environment(\.widgetFamily) private var widgetSize + @Environment(\.sizeCategory) var textSize + var entry: OnThisDayProvider.Entry + + var needsVerticalCompression: Bool { + return textSize == .extraExtraExtraLarge + } + + @ViewBuilder + var body: some View { + GeometryReader { proxy in + switch widgetSize { + case .systemLarge: + VStack(alignment: .leading, spacing: 0) { + OnThisDayHeaderElement(widgetTitle: entry.onThisDayTitle, yearRange: entry.yearRange, monthDay: entry.monthDay) + .padding(.bottom, needsVerticalCompression ? 3 : 9) + MainOnThisDayTopElement(monthDay: entry.monthDay, eventYear: entry.eventYear, eventYearsAgo: entry.eventYearsAgo, fullDate: entry.fullDate) + /// The full `MainOnThisDayElement` is not used in the large widget. We need the `Spacer` and the `eventSnippet` text to be part of the same `VStack` to render correctly. (Otherwise, the "text is so long it must be cutoff" and/or the "text is so short we need blank space at the bottom" scenario perform incorrectly.) + if let eventSnippet = entry.eventSnippet, let title = entry.articleTitle, let articleSnippet = entry.articleSnippet { + LargeWidgetMiddleSection(eventSnippet: eventSnippet, title: title, description: articleSnippet, image: entry.articleImage, link: entry.articleURL ?? entry.contentURL) + .padding(.top, needsVerticalCompression ? 3 : 9) + .layoutPriority(1.0) + } + OnThisDayAdditionalEventsElement(otherEventsText: entry.otherEventsText) + } + .padding([.top, .leading, .trailing], 16) + case .systemMedium: + VStack(alignment: .leading, spacing: 0) { + MainOnThisDayElement(monthDay: entry.monthDay, eventYear: entry.eventYear, eventYearsAgo: entry.eventYearsAgo, fullDate: entry.fullDate, snippet: entry.eventSnippet) + OnThisDayAdditionalEventsElement(otherEventsText: entry.otherEventsText) + } + .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) + case .systemSmall: + MainOnThisDayElement(monthDay: entry.monthDay, eventYear: entry.eventYear, eventYearsAgo: entry.eventYearsAgo, fullDate: entry.fullDate, snippet: entry.eventSnippet) + .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) + default: + MainOnThisDayElement(monthDay: entry.monthDay, eventYear: entry.eventYear, eventYearsAgo: entry.eventYearsAgo, fullDate: entry.fullDate, snippet: entry.eventSnippet) + .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) + } + } + .overlay(errorBox) + .environment(\.layoutDirection, entry.isRTLLanguage ? .rightToLeft : .leftToRight) + .widgetURL(entry.contentURL) + } + + var errorBox: some View { + if let error = entry.error { + return AnyView(ErrorSquare(error: error)) + } else { + return AnyView(EmptyView()) + } + } +} + +struct LargeYValuePreferenceKey: PreferenceKey { + static var defaultValue: CGFloat = 20 + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = nextValue() + } +} + +struct SmallYValuePreferenceKey: PreferenceKey { + static var defaultValue: CGFloat = 20 + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = nextValue() + } +} + +struct ErrorSquare: View { + @Environment(\.widgetFamily) private var widgetSize + let error: OnThisDayData.ErrorType + + var body: some View { + Rectangle().foregroundColor(error.errorColor) + .overlay( + Text(error.errorText) + .font(.caption) + .bold() + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading) + .foregroundColor(.white) + .padding([.leading, .top, .bottom], 16) + .padding(.trailing, widgetSize == .systemSmall ? 16 : 90) + ) + } +} + +struct TimelineView: View { + enum DotStyle { + case large, small, none + } + @Environment(\.colorScheme) var colorScheme + @Environment(\.widgetFamily) private var widgetSize + + @SwiftUI.State private var circleYOffset: CGFloat = 0 + var dotStyle: DotStyle + var isLineTopFaded: Bool + var isLineBottomFaded: Bool + var mainView: Content + + var gradient: LinearGradient { + if isLineTopFaded { + let colorGradient = Gradient(stops: [ + Gradient.Stop(color: OnThisDayColors.widgetBackgroundColor(colorScheme), location: 0), + Gradient.Stop(color: OnThisDayColors.blueColor(colorScheme), location: 0.65) + ]) + return LinearGradient(gradient: colorGradient, startPoint: .top, endPoint: .bottom) + /// No bottom gradient on large widgets. See Phab for details: https://phabricator.wikimedia.org/T259840#6448845 + } else if isLineBottomFaded && widgetSize != .systemLarge { + let colorGradient = Gradient(stops: [ + // Small widgets have a much larger final section, and thus gradient starts later. + Gradient.Stop(color: OnThisDayColors.blueColor(colorScheme), location: (widgetSize == .systemSmall ? 0.75 : 0.45)), + Gradient.Stop(color: OnThisDayColors.widgetBackgroundColor(colorScheme), location: 1.0) + ]) + return LinearGradient(gradient: colorGradient, startPoint: .top, endPoint: .bottom) + } else { + // plain blue + return LinearGradient(gradient: Gradient(colors: [OnThisDayColors.blueColor(colorScheme)]), startPoint: .top, endPoint: .bottom) + } + } + + var body: some View { + let lineWidth: CGFloat = 1 + HStack(alignment: .top) { + ZStack(alignment: .top) { + TimelinePathElement() + .stroke(lineWidth: lineWidth) + .fill(gradient) + switch dotStyle { + case .large: TimelineLargeCircleElement(lineWidth: lineWidth, circleYOffset: circleYOffset) + case .small: TimelineSmallCircleElement(lineWidth: lineWidth, circleYOffset: circleYOffset) + case .none: EmptyView() + } + } + .frame(width: TimelineLargeCircleElement.largeCircleHeight) + mainView + } + .onPreferenceChange(SmallYValuePreferenceKey.self, perform: { yOffset in + if dotStyle == .small { + self.circleYOffset = yOffset + } + }) + .onPreferenceChange(LargeYValuePreferenceKey.self, perform: { yOffset in + if dotStyle == .large { + self.circleYOffset = yOffset + } + }) + } +} + +/// This is extremely hacky. Once adding padding to views and/or a Spacer() view, the timeline portion of view doesn't take up the full vertical spacce that it should. After exploring numerous other options, I went with this choice - adding some arbitrary extra length to the end of the line. Someday when SwiftUI layout works better, we can remove the +20. +struct TimelinePathElement: Shape { + func path(in rect: CGRect) -> Path { + var path = Path() + path.move(to: CGPoint(x: rect.midX, y: rect.minY)) + path.addLine(to: CGPoint(x: rect.midX, y: rect.maxY + 20)) + return path + } +} + +struct TimelineSmallCircleElement: View { + @Environment(\.colorScheme) var colorScheme + + static let smallCircleHeight: CGFloat = 9.0 + let lineWidth: CGFloat + let circleYOffset: CGFloat + + var body: some View { + Circle() + .overlay( + Circle() + .stroke(OnThisDayColors.blueColor(colorScheme), lineWidth: lineWidth) + ).foregroundColor(OnThisDayColors.widgetBackgroundColor(colorScheme)) + .frame(width: TimelineSmallCircleElement.smallCircleHeight, height: TimelineSmallCircleElement.smallCircleHeight) + .padding(EdgeInsets(top: circleYOffset, leading: 0, bottom: 0, trailing: 0)) + } +} + +struct TimelineLargeCircleElement: View { + static let largeCircleHeight: CGFloat = 17.0 + @Environment(\.colorScheme) var colorScheme + + let lineWidth: CGFloat + let circleYOffset: CGFloat + + var body: some View { + GeometryReader { geometry in + Circle() + .stroke(OnThisDayColors.blueColor(colorScheme), lineWidth: lineWidth) + .overlay( + Circle() + .overlay( + Circle() + .stroke(OnThisDayColors.blueColor(colorScheme), lineWidth: lineWidth) + ) + .frame(width: TimelineSmallCircleElement.smallCircleHeight, height: TimelineSmallCircleElement.smallCircleHeight) + .foregroundColor(OnThisDayColors.blueColor(colorScheme)) + ) + .frame(width: geometry.size.width, height: geometry.size.width) + .padding(.top, circleYOffset) + } + } +} + +struct OnThisDayHeaderElement: View { + @Environment(\.colorScheme) var colorScheme + @Environment(\.sizeCategory) var textSize + let widgetTitle: String + let yearRange: String + let monthDay: String + + var needsVerticalCompression: Bool { + return textSize == .extraExtraExtraLarge + } + + var body: some View { + /// Custom spacing (handled by middle text element) from 10 Sept 2020 video call w/ Carolyn, the app designer. + VStack(spacing: 0) { + Text(widgetTitle) + .foregroundColor(OnThisDayColors.grayColor(colorScheme)) + .font(.subheadline) + .bold() + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, alignment: .leading) + Text(monthDay) + .font(.title2) + .bold() + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.top, needsVerticalCompression ? 2 : 5) + .padding(.bottom, needsVerticalCompression ? 2 : 7) + Text(yearRange) + .foregroundColor(OnThisDayColors.grayColor(colorScheme)) + .font(.subheadline) + .bold() + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, alignment: .leading) + } + } +} + +struct MainOnThisDayElement: View { + @Environment(\.widgetFamily) private var widgetSize + + var monthDay: String + var eventYear: String + var eventYearsAgo: String? + var fullDate: String + var snippet: String? + + /// For unknown reasons, the layout of the `TimelineView` for a large widget is different from the rest. (A larger comment is above.) One side affect is that (as of iOS 14, beta 6) the large dot is not properly centered on the large widget. This `isLargeWidget` boolean allows us to manually correct for the error. + + var body: some View { + VStack(spacing: 0) { + MainOnThisDayTopElement(monthDay: monthDay, eventYear: eventYear, eventYearsAgo: eventYearsAgo, fullDate: fullDate) + if let snippet = snippet { + TimelineView(dotStyle: .none, isLineTopFaded: false, isLineBottomFaded: widgetSize == .systemSmall, mainView: + Text(snippet) + .font(.caption) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.top, 9) + .padding(.bottom, (widgetSize == .systemMedium ? 4 : 8)) + ).layoutPriority(1.0) + } + }.layoutPriority(1.0) + } +} + +struct MainOnThisDayTopElement: View { + let eventYearPadding: CGFloat = 16.0 + @Environment(\.colorScheme) var colorScheme + @Environment(\.widgetFamily) private var widgetSize + + var monthDay: String + var eventYear: String + var eventYearsAgo: String? + var fullDate: String + + var firstLineText: String { + if widgetSize != .systemMedium { + return eventYear + } else { + return fullDate + } + } + + private var secondLineText: String? { + if widgetSize == .systemSmall { + return monthDay + } else { + return eventYearsAgo + } + } + + var body: some View { + if let secondLineText = secondLineText { + TimelineView(dotStyle: .large, isLineTopFaded: true, isLineBottomFaded: false, mainView: + Text(firstLineText) + .font(.subheadline) + .foregroundColor(OnThisDayColors.blueColor(colorScheme)) + .frame(maxWidth: .infinity, alignment: .leading) + .overlay( + GeometryReader { geometryProxy in + Color.clear + .preference(key: LargeYValuePreferenceKey.self, value: startYOfCircle(viewHeight: geometryProxy.size.height, circleHeight: TimelineLargeCircleElement.largeCircleHeight, topPadding: eventYearPadding)) + } + ) + .padding(.top, eventYearPadding) + ) + TimelineView(dotStyle: .none, isLineTopFaded: false, isLineBottomFaded: false, mainView: + Text(secondLineText) + .font(.caption) + .foregroundColor(OnThisDayColors.grayColor(colorScheme)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.top, 3) + ) + } + } +} + +struct LargeWidgetMiddleSection: View { + let eventSnippet: String + let title: String + let description: String + let image: UIImage? + let link: URL? + + var body: some View { + TimelineView(dotStyle: .none, isLineTopFaded: false, isLineBottomFaded: false, mainView: + VStack(alignment: .leading, spacing: 0) { + Text(eventSnippet) + .font(.caption) + if let link = link { + Link(destination: link) { + ArticleRectangleBox(title: title, description: description, image: image) + } + } else { + ArticleRectangleBox(title: title, description: description, image: image) + } + Spacer(minLength: 0) + } + ) + } +} + +struct ArticleRectangleBox: View { + @Environment(\.colorScheme) var colorScheme + + let title: String + let description: String + let image: UIImage? + + var body: some View { + HStack(spacing: 9) { + VStack { + Text(title) + .font(.caption) + .bold() + .frame(maxWidth: .infinity, alignment: .leading) + Text(description) + .font(.caption) + .lineLimit(1) + .foregroundColor(OnThisDayColors.grayColor(colorScheme)) + .frame(maxWidth: .infinity, alignment: .leading) + } + if let image = image { + Image(uiImage: image) + .resizable() + .scaledToFill() + .frame(width: 36, height: 36, alignment: .center) + .cornerRadius(2.0) + } + } + .padding(9) + .background( + RoundedRectangle(cornerRadius: 2.0) + .shadow(color: OnThisDayColors.boxShadowColor(colorScheme), radius: 4.0, x: 0, y: 2) + .foregroundColor(OnThisDayColors.boxBackgroundColor(colorScheme)) + ) + .padding([.top, .bottom], 9) + .padding([.trailing], 35) + } +} + +struct OnThisDayAdditionalEventsElement: View { + @Environment(\.colorScheme) var colorScheme + + let otherEventsText: String + + var body: some View { + TimelineView(dotStyle: .small, isLineTopFaded: false, isLineBottomFaded: true, mainView: + Text(otherEventsText) + .font(.footnote) + .bold() + .lineLimit(1) + .foregroundColor(OnThisDayColors.blueColor(colorScheme)) + .frame(maxWidth: .infinity, alignment: .topLeading) + .padding(.top, 2) + .overlay( + GeometryReader { geometryProxy in + Color.clear + .preference(key: SmallYValuePreferenceKey.self, value: startYOfCircle(viewHeight: geometryProxy.size.height, circleHeight: TimelineSmallCircleElement.smallCircleHeight, topPadding: 0)) + /// The padding of 0 is a little arbitrary. These views aren't perfectly laid out in SwiftUI - see the "+20" comment above - and depending on spacing/padding we sometims need to tweak the padding to make this layout properly. + } + ) + .padding(.bottom, 16) + ) + } +} + +private func startYOfCircle(viewHeight: CGFloat, circleHeight: CGFloat, topPadding: CGFloat = 0) -> CGFloat { + return topPadding + ((viewHeight - circleHeight)/2) +} + +// MARK: - Preview + +struct OnThisDayDayWidget_Previews: PreviewProvider { + static var previews: some View { + OnThisDayView(entry: OnThisDayData.shared.placeholderEntryFromLanguage(nil)) + .previewContext(WidgetPreviewContext(family: .systemLarge)) + } +} diff --git a/Apps/Wikipedia/Widgets/Widgets/OnThisDayWidget.swift b/Apps/Wikipedia/Widgets/Widgets/OnThisDayWidget.swift new file mode 100644 index 0000000..d845142 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Widgets/OnThisDayWidget.swift @@ -0,0 +1,281 @@ +import WidgetKit +import SwiftUI +import WMF + +// MARK: - Widget + +struct OnThisDayWidget: Widget { + private let kind: String = WidgetController.SupportedWidget.onThisDay.identifier + + public var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: OnThisDayProvider(), content: { entry in + OnThisDayView(entry: entry) + }) + .configurationDisplayName(CommonStrings.onThisDayTitle) + .description(WMFLocalizedString("widget-onthisday-description", value: "Explore what happened on this day in history.", comment: "Description for 'On this day' view in iOS widget gallery")) + .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) + } +} + +// MARK: - TimelineProvider + +struct OnThisDayProvider: TimelineProvider { + + // MARK: Nested Types + + public typealias Entry = OnThisDayEntry + + // MARK: Properties + + private let dataStore = OnThisDayData.shared + + // MARK: TimelineProvider + + func placeholder(in: Context) -> OnThisDayEntry { + return dataStore.placeholderEntryFromLanguage(nil) + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { + dataStore.fetchLatestAvailableOnThisDayEntry { entry in + let currentDate = Date() + let nextUpdate: Date + if entry.error == nil && entry.isCurrent { + nextUpdate = currentDate.randomDateShortlyAfterMidnight() ?? currentDate + } else { + let components = DateComponents(hour: 2) + nextUpdate = Calendar.current.date(byAdding: components, to: currentDate) ?? currentDate + } + let timeline = Timeline(entries: [entry], policy: .after(nextUpdate)) + completion(timeline) + + } + } + + func getSnapshot(in context: Context, completion: @escaping (OnThisDayEntry) -> Void) { + dataStore.fetchLatestAvailableOnThisDayEntry(usingCache: context.isPreview) { entry in + completion(entry) + } + } + +} + +/// A data source and operation helper for all On This Day of the day widget data +final class OnThisDayData { + + enum ErrorType { + case noInternet, featureNotSupportedInLanguage + + var errorColor: Color { + switch self { + case .featureNotSupportedInLanguage: + return Color(.gray500) + case .noInternet: + return Color(.gray800) + } + } + + var errorText: String { + /// These are intentionally in the iOS system language, not the app's primary language. Everything else in this widget is in the app's primary language. + switch self { + case .featureNotSupportedInLanguage: + return WMFLocalizedString("on-this-day-language-does-not-support-error", value: "Your primary Wikipedia language does not support On this day. You can update your primary Wikipedia in the app’s Settings menu.", comment: "Error message shown when the user's primary language Wikipedia does not have the 'On this day' feature.") + case .noInternet: + return WMFLocalizedString("on-this-day-no-internet-error", value: "No data available", comment: "error message shown when device is not connected to internet") + } + } + } + + // MARK: Properties + + static let shared = OnThisDayData() + + // From https://en.wikipedia.org/api/rest_v1/feed/onthisday/events/01/15, taken on 03 Sept 2020. + func placeholderEntryFromLanguage(_ language: MWKLanguageLink?) -> OnThisDayEntry { + let locale = NSLocale.wmf_locale(for: language?.languageCode) + let isRTL = MWKLanguageLinkController.isLanguageRTL(forContentLanguageCode: language?.contentLanguageCode) + + let fullDate: String + let eventYear: String + let monthDay: String + let calendar = NSCalendar.wmf_utcGregorian() + let date = calendar?.date(from: DateComponents(year: 2001, month: 01, day: 15, hour: 0, minute: 0, second: 0)) + if let date = date { + let fullDateFormatter = DateFormatter.wmf_longDateGMTFormatter(for: language?.languageCode) + fullDate = fullDateFormatter.string(from: date) + let yearWithEraFormatter = DateFormatter.wmf_yearGMTDateFormatter(for: language?.languageCode) + eventYear = yearWithEraFormatter.string(from: date) + let monthDayFormatter = DateFormatter.wmf_monthNameDayNumberGMTFormatter(for: language?.languageCode) + monthDay = monthDayFormatter.string(from: date) + } else { + fullDate = "January 15, 2001" + eventYear = "2001" + monthDay = "January 15" + } + + let eventSnippet = WMFLocalizedString("widget-onthisday-placeholder-event-snippet", languageCode: language?.languageCode, value: "Wikipedia, a free wiki content encyclopedia, goes online.", comment: "Placeholder text for On This Day widget: Event describing launch of Wikipedia") + let articleSnippet = WMFLocalizedString("widget-onthisday-placeholder-article-snippet", languageCode: language?.languageCode, value: "Free online encyclopedia that anyone can edit", comment: "Placeholder text for On This Day widget: Article description for an article about Wikipedia") + + // It seems that projects whose article is not titled "Wikipedia" (Arabic, for instance) all redirect this URL appropriately. + let articleURL = URL(string: ((language?.siteURL.absoluteString ?? "https://en.wikipedia.org") + "/wiki/Wikipedia")) + + let entry: OnThisDayEntry = OnThisDayEntry(isRTLLanguage: isRTL, + error: nil, + + onThisDayTitle: CommonStrings.onThisDayTitle(with: language?.languageCode), + monthDay: monthDay, + fullDate: fullDate, + otherEventsText: CommonStrings.onThisDayFooterWith(with: 49, languageCode: language?.languageCode), + contentURL: URL(string: "https://en.wikipedia.org/wiki/Wikipedia:On_this_day/Today"), + eventSnippet: eventSnippet, + eventYear: eventYear, + eventYearsAgo: String(format: WMFLocalizedDateFormatStrings.yearsAgo(forWikiLanguage: language?.languageCode), locale: locale, (Calendar.current.component(.year, from: Date()) - 2001)), + articleTitle: CommonStrings.plainWikipediaName(with: language?.languageCode), + articleSnippet: articleSnippet, + articleImage: UIImage(named: "W"), + articleURL: articleURL, + yearRange: CommonStrings.onThisDayHeaderDateRangeMessage(with: language?.languageCode, locale: locale, lastEvent: "69", firstEvent: "2019")) + return entry + } + + // MARK: Public + + func fetchLatestAvailableOnThisDayEntry(usingCache: Bool = false, _ userCompletion: @escaping (OnThisDayEntry) -> Void) { + let widgetController = WidgetController.shared + widgetController.startWidgetUpdateTask(userCompletion) { (dataStore, widgetTaskCompletion) in + guard let appLanguage = dataStore.languageLinkController.appLanguage, + WMFOnThisDayEventsFetcher.isOnThisDaySupported(by: appLanguage.languageCode) else { + let errorEntry = OnThisDayEntry.errorEntry(for: .featureNotSupportedInLanguage) + widgetTaskCompletion(errorEntry) + return + } + widgetController.fetchNewestWidgetContentGroup(with: .onThisDay, in: dataStore, isNetworkFetchAllowed: !usingCache) { (contentGroup) in + guard let contentGroup = contentGroup else { + widgetTaskCompletion(self.placeholderEntryFromLanguage(dataStore.languageLinkController.appLanguage)) + return + } + self.assembleOnThisDayFromContentGroup(contentGroup, dataStore: dataStore, usingImageCache: usingCache, completion: widgetTaskCompletion) + } + } + } + + private func assembleOnThisDayFromContentGroup(_ contentGroup: WMFContentGroup, dataStore: MWKDataStore, usingImageCache: Bool = false, completion: @escaping (OnThisDayEntry) -> Void) { + + guard let previewEvents = contentGroup.contentPreview as? [WMFFeedOnThisDayEvent], + let previewEvent = previewEvents.first, + let entry = OnThisDayEntry(contentGroup) + else { + let language = dataStore.languageLinkController.appLanguage + completion(placeholderEntryFromLanguage(language)) + return + } + + let sendDataToWidget: (UIImage?) -> Void = { image in + var entryWithImage = entry + entryWithImage.articleImage = image + completion(entryWithImage) + } + + let imageURLRaw = previewEvent.articlePreviews?.first?.thumbnailURL + guard let imageURL = imageURLRaw, !usingImageCache else { + /// The argument sent to `sendDataToWidget` on the next line could be nil because `imageURLRaw` is nil, or `cachedImage` returns nil. + /// `sendDataToWidget` will appropriately handle a nil image, so we don't need to worry about that here. + let cachedImage = dataStore.cacheController.imageCache.cachedImage(withURL: imageURLRaw)?.staticImage + sendDataToWidget(cachedImage) + return + } + + dataStore.cacheController.imageCache.fetchImage(withURL: imageURL, failure: { _ in + sendDataToWidget(nil) + }, success: { fetchedImage in + sendDataToWidget(fetchedImage.image.staticImage) + }) + } + + private func handleNoInternetError(_ completion: @escaping (OnThisDayEntry) -> Void) { + let errorEntry = OnThisDayEntry.errorEntry(for: .noInternet) + completion(errorEntry) + } +} + +// MARK: - Model + +struct OnThisDayEntry: TimelineEntry { + let date = Date() + var isCurrent: Bool = false + let isRTLLanguage: Bool + let error: OnThisDayData.ErrorType? + + let onThisDayTitle: String + let monthDay: String + let fullDate: String + let otherEventsText: String + let contentURL: URL? + let eventSnippet: String? + let eventYear: String + let eventYearsAgo: String? + let articleTitle: String? + let articleSnippet: String? + var articleImage: UIImage? + let articleURL: URL? + let yearRange: String +} + +extension OnThisDayEntry { + init?(_ contentGroup: WMFContentGroup) { + guard + let midnightUTCDate = contentGroup.midnightUTCDate, + let calendar = NSCalendar.wmf_utcGregorian(), + let previewEvents = contentGroup.contentPreview as? [WMFFeedOnThisDayEvent], + let previewEvent = previewEvents.first, + let allEvents = contentGroup.fullContent?.object as? [WMFFeedOnThisDayEvent], + let earliestEventYear = allEvents.last?.yearString, + let latestEventYear = allEvents.first?.yearString, + let article = previewEvents.first?.articlePreviews?.first, + let year = previewEvent.year?.intValue, + let eventsCount = contentGroup.countOfFullContent?.intValue + else { + return nil + } + let language = contentGroup.siteURL?.wmf_languageCode + let monthDayFormatter = DateFormatter.wmf_monthNameDayNumberGMTFormatter(for: language) + monthDay = monthDayFormatter.string(from: midnightUTCDate) + var components = calendar.components([.month, .year, .day], from: midnightUTCDate) + components.year = year + if let date = calendar.date(from: components) { + let fullDateFormatter = DateFormatter.wmf_longDateGMTFormatter(for: language) + fullDate = fullDateFormatter.string(from: date) + let yearWithEraFormatter = DateFormatter.wmf_yearGMTDateFormatter(for: language) + eventYear = yearWithEraFormatter.string(from: date) + } else { + fullDate = "" + eventYear = "" + } + onThisDayTitle = CommonStrings.onThisDayTitle(with: language) + isRTLLanguage = contentGroup.isRTL + error = nil + otherEventsText = CommonStrings.onThisDayFooterWith(with: (eventsCount - 1), languageCode: language) + eventSnippet = previewEvent.text + articleTitle = article.displayTitle + articleSnippet = article.descriptionOrSnippet + articleURL = article.articleURL + let locale = NSLocale.wmf_locale(for: language) + let currentYear = Calendar.current.component(.year, from: Date()) + let yearsSinceEvent = currentYear - year + eventYearsAgo = String(format: WMFLocalizedDateFormatStrings.yearsAgo(forWikiLanguage: language), locale: locale, yearsSinceEvent) + yearRange = CommonStrings.onThisDayHeaderDateRangeMessage(with: language, locale: locale, lastEvent: earliestEventYear, firstEvent: latestEventYear) + + if let previewEventIndex = allEvents.firstIndex(of: previewEvent), + let dynamicURL = URL(string: "https://en.wikipedia.org/wiki/Wikipedia:On_this_day/Today?\(previewEventIndex)") { + contentURL = dynamicURL + } else { + contentURL = URL(string: "https://en.wikipedia.org/wiki/Wikipedia:On_this_day/Today") + } + isCurrent = contentGroup.isForToday + } + + static func errorEntry(for error: OnThisDayData.ErrorType) -> OnThisDayEntry { + let isRTL = Locale.lineDirection(forLanguage: Locale.autoupdatingCurrent.languageCode ?? "en") == .rightToLeft + let destinationURL = URL(string: "wikipedia://explore") + return OnThisDayEntry(isRTLLanguage: isRTL, error: error, onThisDayTitle: "", monthDay: "", fullDate: "", otherEventsText: "", contentURL: destinationURL, eventSnippet: nil, eventYear: "", eventYearsAgo: nil, articleTitle: nil, articleSnippet: nil, articleImage: nil, articleURL: nil, yearRange: "") + } +} diff --git a/Apps/Wikipedia/Widgets/Widgets/PictureOfTheDayWidget+LocalizedStrings.swift b/Apps/Wikipedia/Widgets/Widgets/PictureOfTheDayWidget+LocalizedStrings.swift new file mode 100644 index 0000000..835a4f6 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Widgets/PictureOfTheDayWidget+LocalizedStrings.swift @@ -0,0 +1,12 @@ +import Foundation +import WMF + +extension PictureOfTheDayWidget { + + enum LocalizedStrings { + static let widgetTitle = WMFLocalizedString("potd-widget-title", value:"Picture of the day", comment: "Text for title of Picture of the day widget.") + static let widgetDescription = WMFLocalizedString("potd-widget-description", value:"Enjoy a beautiful daily photo selected by our community.", comment: "Text for description of Picture of the day widget displayed when adding to home screen.") + static let sampleEntryDescription = WMFLocalizedString("potd-sample-entry-description", value:"Two bulls running while the jockey holds on to them in pacu jawi (from Minangkabau, \"bull race\"), a traditional bull race in Tanah Datar, West Sumatra, Indonesia. 2015, Final-45.", comment: "Text for sample entry image caption. It is displayed when app is unable to fetch Picture of the day data.") + } + +} diff --git a/Apps/Wikipedia/Widgets/Widgets/PictureOfTheDayWidget.swift b/Apps/Wikipedia/Widgets/Widgets/PictureOfTheDayWidget.swift new file mode 100644 index 0000000..95c1ca4 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Widgets/PictureOfTheDayWidget.swift @@ -0,0 +1,331 @@ +import WidgetKit +import SwiftUI +import WMF +import UIKit + +// MARK: - Widget + +struct PictureOfTheDayWidget: Widget { + private let kind: String = WidgetController.SupportedWidget.pictureOfTheDay.identifier + + public var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: PictureOfTheDayProvider(), content: { entry in + PictureOfTheDayView(entry: entry) + }) + .configurationDisplayName(PictureOfTheDayWidget.LocalizedStrings.widgetTitle) + .description(PictureOfTheDayWidget.LocalizedStrings.widgetDescription) + .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) + } +} + +// MARK: - Data + +/// A data source and operation helper for all Picture of the day widget data +final class PictureOfTheDayData { + + // MARK: Properties + static var sampleEntry: PictureOfTheDayEntry { + return PictureOfTheDayEntry(date: Date(), kind: .sample, image: UIImage(named: "PictureOfTheYear_2019"), imageDescription: PictureOfTheDayWidget.LocalizedStrings.sampleEntryDescription) + } + + static var placeholderEntry: PictureOfTheDayEntry { + return PictureOfTheDayEntry(date: Date(), kind: .placeholder, contentDate: nil, contentURL: nil, imageURL: nil, image: nil, imageDescription: nil) + } + + // MARK: Public + + static func fetchLatestAvailablePictureEntry(for imageSize: CGSize, usingCache: Bool = false, completion userCompletion: @escaping (PictureOfTheDayEntry) -> Void) { + let widgetController = WidgetController.shared + widgetController.startWidgetUpdateTask(userCompletion) { (dataStore, widgetUpdateTaskCompletion) in + widgetController.fetchNewestWidgetContentGroup(with: .pictureOfTheDay, in: dataStore, isNetworkFetchAllowed: !usingCache, isAnyLanguageAllowed: true) { (contentGroup) in + guard let contentGroup = contentGroup else { + widgetUpdateTaskCompletion(self.placeholderEntry) + return + } + self.assemblePictureEntryFromContentGroup(contentGroup, dataStore: dataStore, imageSize: imageSize, usingImageCache: usingCache, completion: widgetUpdateTaskCompletion) + } + } + } + + // MARK: Private + + + private static func assemblePictureEntryFromContentGroup(_ contentGroup: WMFContentGroup, dataStore: MWKDataStore, imageSize: CGSize, usingImageCache: Bool = false, completion: @escaping (PictureOfTheDayEntry) -> Void) { + guard let imageContent = contentGroup.contentPreview as? WMFFeedImage else { + completion(sampleEntry) + return + } + + let contentDate = contentGroup.date + let contentURL = contentGroup.url + let isCurrent = contentGroup.isForToday + let canonicalPageTitle = imageContent.canonicalPageTitle + let imageThumbnailURL: URL = imageContent.getImageURL(forWidth: Double(imageSize.width), height: Double(imageSize.height)) ?? imageContent.imageThumbURL + let imageDescription = imageContent.imageDescription + + guard !usingImageCache else { + if let cachedImage = dataStore.cacheController.imageCache.cachedImage(withURL: imageThumbnailURL) { + let entry = PictureOfTheDayEntry(date: Date(), isCurrent: isCurrent, kind: .entry, contentDate: contentDate, contentURL: contentURL, imageURL: imageThumbnailURL, image: cachedImage.staticImage, imageDescription: imageDescription) + completion(entry) + } else { + completion(sampleEntry) + } + return + } + + dataStore.cacheController.imageCache.fetchImage(withURL: imageThumbnailURL, failure: { _ in + completion(sampleEntry) + }, success: { fetchedImage in + self.fetchImageLicense(from: dataStore, canonicalPageTitle: canonicalPageTitle) { license in + let entry = PictureOfTheDayEntry(date: Date(), isCurrent: isCurrent, kind: .entry, contentDate: contentDate, contentURL: contentURL, imageURL: imageThumbnailURL, image: fetchedImage.image.staticImage, imageDescription: imageDescription, licenseCode: license?.code) + completion(entry) + } + }) + } + + static var imageInfoFetcher: MWKImageInfoFetcher? + private static func fetchImageLicense(from dataStore: MWKDataStore, canonicalPageTitle: String, _ completion: @escaping (MWKLicense?) -> Void) { + + guard let siteURL = NSURL.wmf_wikimediaCommons() else { + completion(nil) + return + } + let fetcher = MWKImageInfoFetcher(dataStore: dataStore) + imageInfoFetcher = fetcher // needs to be retained to complete the fetch + fetcher.fetchGalleryInfo(forImage: canonicalPageTitle, fromSiteURL: siteURL, failure: { _ in + self.imageInfoFetcher = nil + DispatchQueue.main.async { + completion(nil) + } + }, success: { imageInfo in + self.imageInfoFetcher = nil + guard let imageInfo = imageInfo as? MWKImageInfo, let license = imageInfo.license else { + DispatchQueue.main.async { + completion(nil) + } + return + } + DispatchQueue.main.async { + completion(license) + } + }) + } + +} + +// MARK: - Model + +struct PictureOfTheDayEntry: TimelineEntry { + + // MARK: Nested Types + + struct LicenseImage: Identifiable { + var id: String + var image: UIImage // the system encodes this entry and it crashes if this is a SwiftUI.Image + } + + enum Kind { + case entry + case placeholder + case sample + } + + // MARK: Properties + + let date: Date // for Timeline Entry + var isCurrent: Bool = false + let kind: Kind + var contentDate: Date? = nil + var contentURL: URL? = nil + var imageURL: URL? = nil + var image: UIImage? + var imageDescription: String? = nil + var licenseCode: String? = nil // the system encodes this entry, avoiding bringing in the whole MWKLicense object and the Mantle dependency + + // MARK: License Image Parsing + + var licenseImages: [LicenseImage] { + var licenseImages: [LicenseImage] = [] + let licenseCodes: [String] = licenseCode?.components(separatedBy: "-") ?? ["generic"] + + for license in licenseCodes { + guard let image = UIImage(named: "license-\(license)") else { + continue + } + licenseImages.append(LicenseImage(id: license, image: image)) + } + + return licenseImages + } + + // MARK: - Scale Entry Image + + func scalingImageTo(targetSize: CGSize) -> PictureOfTheDayEntry { + var entry = self + entry.image = entry.image?.scaleImageToFit(targetSize: targetSize) + return entry + } + +} + +// MARK: - TimelineProvider + +struct PictureOfTheDayProvider: TimelineProvider { + + // MARK: Nested Types + + public typealias Entry = PictureOfTheDayEntry + + // MARK: Properties + + private let targetImageSize = CGSize(width: 1000, height: 1000) + + // MARK: TimelineProvider + + func placeholder(in: Context) -> PictureOfTheDayEntry { + return PictureOfTheDayData.placeholderEntry + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { + PictureOfTheDayData.fetchLatestAvailablePictureEntry(for: context.imageSize) { entry in + let currentDate = Date() + let nextUpdate: Date + if entry.kind == .entry && entry.isCurrent { + nextUpdate = currentDate.randomDateShortlyAfterMidnight() ?? currentDate + } else { + let components = DateComponents(hour: 2) + nextUpdate = Calendar.current.date(byAdding: components, to: currentDate) ?? currentDate + } + let timeline = Timeline(entries: [entry.scalingImageTo(targetSize: targetImageSize)], policy: .after(nextUpdate)) + completion(timeline) + } + } + + func getSnapshot(in context: Context, completion: @escaping (PictureOfTheDayEntry) -> Void) { + PictureOfTheDayData.fetchLatestAvailablePictureEntry(for: context.imageSize, usingCache: context.isPreview) { entry in + completion(entry.scalingImageTo(targetSize: targetImageSize)) + } + } + +} + +// MARK: - Views + +struct PictureOfTheDayView: View { + @Environment(\.widgetFamily) private var family + var entry: PictureOfTheDayProvider.Entry + + @ViewBuilder + var body: some View { + GeometryReader { proxy in + switch family { + case .systemLarge: + VStack(spacing: 0) { + image + .frame(width: proxy.size.width, height: proxy.size.height * 0.77) + .overlay(PictureOfTheDayOverlayView(entry: entry), alignment: .bottomLeading) + description + .frame(width: proxy.size.width, height: proxy.size.height * 0.23) + .background(Color(red: 34/255.0, green: 34/255.0, blue: 34/255.0)) + } + default: + image + .frame(width: proxy.size.width, height: proxy.size.height, alignment: .center) + .overlay(PictureOfTheDayOverlayView(entry: entry), alignment: .bottomLeading) + } + } + .widgetURL(entry.contentURL) + } + + // MARK: View Components + + @ViewBuilder + var image: some View { + if let image = entry.image { + Image(uiImage: image).resizable().scaledToFill().clipped() + } else { + Rectangle() + .foregroundColor(Color(.systemFill)) + .scaledToFill() + } + } + + var description: some View { + let padding: CGFloat = 16 + + return VStack { + Spacer().frame(height: padding) + GeometryReader { proxy in + Text(entry.imageDescription ?? "") + .font(.caption) + .fontWeight(.medium) + .frame(width: proxy.size.width, alignment: .leading) + .lineLimit(3) + .lineSpacing(2) + .multilineTextAlignment(.leading) + .foregroundColor(.white) + } + Spacer(minLength: padding) + } + .padding([.leading, .trailing], padding) + } +} + +struct PictureOfTheDayOverlayView: View { + var entry: PictureOfTheDayEntry + + var body: some View { + content + .background( + Rectangle() + .foregroundColor(.black) + .mask(LinearGradient(gradient: Gradient(colors: [.clear, .black]), startPoint: .center, endPoint: .bottom)) + .opacity(0.35) + ) + } + + // MARK: - View Components + + var content: some View { + VStack(alignment: .leading) { + HStack(alignment: .top) { + Spacer() + Image("W") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 16, alignment: .trailing) + .foregroundColor(.white) + .padding(EdgeInsets(top: 16, leading: 0, bottom: 0, trailing: 16)) + .readableShadow() + } + Spacer() + HStack(alignment: .top, spacing: 1) { + ForEach(entry.licenseImages) { licenseImage in + Image(uiImage: licenseImage.image) + .resizable() + .aspectRatio(contentMode: .fit) + } + } + .frame(height: 14) + .readableShadow() + .padding(EdgeInsets(top: 0, leading: 16, bottom: 16, trailing: 45)) + } + .foregroundColor(.white) + } +} + +// MARK: - Preview + +struct PictureOfTheDayWidget_Previews: PreviewProvider { + static var previews: some View { + PictureOfTheDayView(entry: PictureOfTheDayData.placeholderEntry) + .previewContext(WidgetPreviewContext(family: .systemLarge)) + } +} + +extension TimelineProviderContext { + var imageSize: CGSize { + let maxDisplayScale = environmentVariants.displayScale?.max() ?? 2 + return CGSize(width: displaySize.width * maxDisplayScale, height: displaySize.height * maxDisplayScale) + } +} diff --git a/Apps/Wikipedia/Widgets/Widgets/TopReadWidget+LocalizedStrings.swift b/Apps/Wikipedia/Widgets/Widgets/TopReadWidget+LocalizedStrings.swift new file mode 100644 index 0000000..3304175 --- /dev/null +++ b/Apps/Wikipedia/Widgets/Widgets/TopReadWidget+LocalizedStrings.swift @@ -0,0 +1,12 @@ +import Foundation +import WMF + +extension TopReadWidget { + + enum LocalizedStrings { + static let widgetTitle = WMFLocalizedString("top-read-widget-title", value:"Top read", comment: "Text for title of Top read widget.") + static let widgetDescription = WMFLocalizedString("top-read-widget-description", value:"Learn what the world is reading today on Wikipedia.", comment: "Text for description of top read widget displayed when adding to home screen.") + static let readersCountFormat = WMFLocalizedString("top-read-widget-readers-count", value:"%1$@ readers", comment: "Text for displaying the number of readers an article has in the top read widget. %1$@ is replaced with the number of readers.") + } + +} diff --git a/Apps/Wikipedia/Widgets/Widgets/TopReadWidget.swift b/Apps/Wikipedia/Widgets/Widgets/TopReadWidget.swift new file mode 100644 index 0000000..9585f8b --- /dev/null +++ b/Apps/Wikipedia/Widgets/Widgets/TopReadWidget.swift @@ -0,0 +1,405 @@ +import SwiftUI +import WidgetKit +import WMF + +// MARK: - Widget + +struct TopReadWidget: Widget { + private let kind: String = WidgetController.SupportedWidget.topRead.identifier + + public var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: TopReadProvider(), content: { entry in + TopReadView(entry: entry) + }) + .configurationDisplayName(LocalizedStrings.widgetTitle) + .description(LocalizedStrings.widgetDescription) + .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) + } +} + +// MARK: - Data + +final class TopReadData { + + // MARK: Properties + + static let shared = TopReadData() + + let maximumRankedArticles = 4 + + var placeholder: TopReadEntry { + return TopReadEntry(date: Date()) + } + + func fetchLatestAvailableTopRead(usingCache: Bool = false, completion userCompletion: @escaping (TopReadEntry) -> Void) { + let widgetController = WidgetController.shared + widgetController.startWidgetUpdateTask(userCompletion) { (dataStore, widgetUpdateTaskCompletion) in + widgetController.fetchNewestWidgetContentGroup(with: .topRead, in: dataStore, isNetworkFetchAllowed: !usingCache) { (contentGroup) in + guard let contentGroup = contentGroup else { + widgetUpdateTaskCompletion(self.placeholder) + return + } + self.assembleTopReadFromContentGroup(contentGroup, with: dataStore, usingImageCache: usingCache, completion: widgetUpdateTaskCompletion) + } + } + } + + // MARK: Private + + private func assembleTopReadFromContentGroup(_ topRead: WMFContentGroup, with dataStore: MWKDataStore, usingImageCache: Bool = false, completion: @escaping (TopReadEntry) -> Void) { + guard let articlePreviews = topRead.contentPreview as? [WMFFeedTopReadArticlePreview] else { + completion(placeholder) + return + } + + // The WMFContentGroup can only be accessed synchronously + // re-accessing it from the main queue or another queue might lead to unexpected behavior + let layoutDirection: LayoutDirection = topRead.isRTL ? .rightToLeft : .leftToRight + let groupURL = topRead.url + let isCurrent = topRead.isForToday // even though the top read data is from yesterday, the content group is for today + var rankedElements: [TopReadEntry.RankedElement] = [] + + for articlePreview in articlePreviews { + if let fetchedArticle = dataStore.fetchArticle(with: articlePreview.articleURL), let viewCounts = fetchedArticle.pageViewsSortedByDate { + let title = fetchedArticle.displayTitle ?? articlePreview.displayTitle + let description = fetchedArticle.wikidataDescription ?? articlePreview.wikidataDescription ?? fetchedArticle.snippet ?? articlePreview.snippet ?? "" + let rankedElement = TopReadEntry.RankedElement(title: title, description: description, articleURL: articlePreview.articleURL, thumbnailURL: articlePreview.thumbnailURL, viewCounts: viewCounts) + rankedElements.append(rankedElement) + } + } + + rankedElements = Array(rankedElements.prefix(maximumRankedArticles)) + + let group = DispatchGroup() + + for (index, element) in rankedElements.enumerated() { + group.enter() + guard let thumbnailURL = element.thumbnailURL else { + group.leave() + continue + } + + let fetcher = dataStore.cacheController.imageCache + + if usingImageCache { + if let cachedImage = fetcher.cachedImage(withURL: thumbnailURL) { + rankedElements[index].image = cachedImage.staticImage + } + group.leave() + continue + } + + fetcher.fetchImage(withURL: thumbnailURL, failure: { _ in + group.leave() + }, success: { fetchedImage in + rankedElements[index].image = fetchedImage.image.staticImage + group.leave() + }) + } + + group.notify(queue: .main) { + completion(TopReadEntry(date: Date(), rankedElements: rankedElements, groupURL: groupURL, isCurrent: isCurrent, contentLayoutDirection: layoutDirection)) + } + } + +} + +// MARK: - Model + +struct TopReadEntry: TimelineEntry { + struct RankedElement: Identifiable { + var id: String = UUID().uuidString + + let title: String + let description: String + var articleURL: URL? = nil + var image: UIImage? = nil + var thumbnailURL: URL? = nil + let viewCounts: [NSNumber] + } + + let date: Date // for Timeline Entry + var rankedElements: [RankedElement] = Array(repeating: RankedElement.init(title: "–", description: "–", image: nil, viewCounts: [.init(floatLiteral: 0)]), count: 4) + var groupURL: URL? = nil + var isCurrent: Bool = false + var contentLayoutDirection: LayoutDirection = .leftToRight +} + +// MARK: - TimelineProvider + +struct TopReadProvider: TimelineProvider { + + // MARK: Nested Types + + public typealias Entry = TopReadEntry + + // MARK: Properties + + private let dataStore = TopReadData.shared + + // MARK: TimelineProvider + + func placeholder(in: Context) -> TopReadEntry { + return dataStore.placeholder + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { + dataStore.fetchLatestAvailableTopRead { entry in + let isError = entry.groupURL == nil || !entry.isCurrent + let nextUpdate: Date + let currentDate = Date() + if !isError { + nextUpdate = currentDate.randomDateShortlyAfterMidnight() ?? currentDate + } else { + let components = DateComponents(hour: 2) + nextUpdate = Calendar.current.date(byAdding: components, to: currentDate) ?? currentDate + } + completion(Timeline(entries: [entry], policy: .after(nextUpdate))) + } + } + + func getSnapshot(in context: Context, completion: @escaping (TopReadEntry) -> Void) { + dataStore.fetchLatestAvailableTopRead(usingCache: context.isPreview) { (entry) in + completion(entry) + } + } + +} + +// MARK: - Views + +struct TopReadView: View { + @Environment(\.widgetFamily) private var family + @Environment(\.colorScheme) private var colorScheme + @Environment(\.sizeCategory) private var sizeCategory + + var entry: TopReadProvider.Entry? + + var readersTextColor: Color { + colorScheme == .light + ? Theme.light.colors.rankGradientEnd.asColor + : Theme.dark.colors.rankGradientEnd.asColor + } + + @ViewBuilder + var body: some View { + GeometryReader { proxy in + switch family { + case .systemMedium: + rowBasedWidget(.systemMedium) + .widgetURL(entry?.groupURL) + case .systemLarge: + rowBasedWidget(.systemLarge) + .widgetURL(entry?.groupURL) + default: + smallWidget + .frame(width: proxy.size.width, height: proxy.size.height, alignment: .center) + .overlay(TopReadOverlayView(rankedElement: entry?.rankedElements.first)) + .widgetURL(entry?.rankedElements.first?.articleURL) + } + } + .environment(\.layoutDirection, entry?.contentLayoutDirection ?? .leftToRight) + .flipsForRightToLeftLayoutDirection(true) + } + + // MARK: View Components + + @ViewBuilder + var smallWidget: some View { + if let image = entry?.rankedElements.first?.image { + Image(uiImage: image).resizable().scaledToFill() + } else { + Rectangle() + .foregroundColor(colorScheme == .dark ? Color.black : Color.white) + } + } + + @ViewBuilder + func rowBasedWidget(_ family: WidgetFamily) -> some View { + let showSparkline = sizeCategory > .extraLarge ? false : (family == .systemLarge ? true : false) + let rowCount = family == .systemLarge ? 4 : 2 + + VStack(alignment: .leading, spacing: 8) { + Text(TopReadWidget.LocalizedStrings.widgetTitle) + .font(.subheadline) + .fontWeight(.bold) + ForEach(entry?.rankedElements.indices.prefix(rowCount) ?? 0..<0, id: \.self) { elementIndex in + if let articleURL = entry?.rankedElements[elementIndex].articleURL { + Link(destination: articleURL, label: { + elementRow(elementIndex, rowCount: rowCount, showSparkline: showSparkline) + }) + } else { + elementRow(elementIndex, rowCount: rowCount, showSparkline: showSparkline) + } + } + } + .padding(16) + } + + @ViewBuilder + func elementRow(_ index: Int, rowCount: Int, showSparkline: Bool = false) -> some View { + let rankColor = colorScheme == .light ? Theme.light.colors.rankGradient.color(at: CGFloat(index)/CGFloat(rowCount)).asColor : Theme.dark.colors.rankGradient.color(at: CGFloat(index)/CGFloat(rowCount)).asColor + GeometryReader { proxy in + HStack(alignment: .center) { + Circle() + .strokeBorder(rankColor, lineWidth: 1) + .frame(width: 22, height: 22, alignment: .leading) + .overlay( + Text("\(NumberFormatter.localizedThousandsStringFromNumber(NSNumber(value: index + 1)))") + .font(.footnote) + .fontWeight(.light) + .foregroundColor(rankColor) + ) + .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 7)) + VStack(alignment: .leading, spacing: 5) { + Text("\(entry?.rankedElements[index].title ?? "–")") + .font(.subheadline) + .fontWeight(.semibold) + .foregroundColor(Color(.label)) + if showSparkline { + Text("\(entry?.rankedElements[index].description ?? "–")") + .lineLimit(2) + .font(.caption) + .foregroundColor(Color(.secondaryLabel)) + Sparkline(style: .compactWithViewCount, timeSeries: entry?.rankedElements[index].viewCounts) + .cornerRadius(4) + .frame(height: proxy.size.height / 3.0, alignment: .leading) + } else { + Text("\(numberOfReadersTextOrEmptyForViewCount(entry?.rankedElements[index].viewCounts.last))") + .font(.caption) + .fontWeight(.medium) + .lineLimit(2) + .foregroundColor(readersTextColor) + } + } + Spacer() + elementImageOrEmptyView(index) + .frame(width: proxy.size.height / 1.1, height: proxy.size.height / 1.1, alignment: .trailing) + .mask( + RoundedRectangle(cornerRadius: 5, style: .continuous) + ) + } + } + } + + @ViewBuilder + func elementImageOrEmptyView(_ elementIndex: Int) -> some View { + if let image = entry?.rankedElements[elementIndex].image { + Image(uiImage: image) + .resizable() + .aspectRatio(contentMode: .fill) + } else { + EmptyView() + } + } + + // MARK: Private + + private func numberOfReadersTextOrEmptyForViewCount(_ viewCount: NSNumber?) -> String { + guard let viewCount = viewCount else { + return "–" + } + + let formattedCount = NumberFormatter.localizedThousandsStringFromNumber(viewCount) + return String.localizedStringWithFormat(TopReadWidget.LocalizedStrings.readersCountFormat, formattedCount) + } +} + +struct TopReadOverlayView: View { + @Environment(\.colorScheme) var colorScheme + + var rankedElement: TopReadEntry.RankedElement? + + var isExpandedStyle: Bool { + return rankedElement?.image == nil + } + + var readersForegroundColor: Color { + colorScheme == .light + ? Theme.light.colors.rankGradientEnd.asColor + : Theme.dark.colors.rankGradientEnd.asColor + } + + var primaryTextColor: Color { + isExpandedStyle + ? colorScheme == .dark ? Color.white : Color.black + : .white + } + + private var currentNumberOfReadersTextOrEmpty: String { + guard let currentViewCount = rankedElement?.viewCounts.last else { + return "–" + } + + let formattedCount = NumberFormatter.localizedThousandsStringFromNumber(currentViewCount) + return String.localizedStringWithFormat(TopReadWidget.LocalizedStrings.readersCountFormat, formattedCount) + } + + var body: some View { + if isExpandedStyle { + content + } else { + content + .background( + Rectangle() + .foregroundColor(.black) + .mask(LinearGradient(gradient: Gradient(colors: [.clear, .black]), startPoint: .center, endPoint: .bottom)) + .opacity(0.35) + ) + } + } + + // MARK: View Components + + var content: some View { + VStack(alignment: .leading) { + if isExpandedStyle { + Text(currentNumberOfReadersTextOrEmpty) + .fontWeight(.medium) + .lineLimit(nil) + .font(.subheadline) + .foregroundColor(readersForegroundColor) + .padding(EdgeInsets(top: 16, leading: 16, bottom: 0, trailing: 0)) + } + sparkline(expanded: isExpandedStyle) + Spacer() + description() + } + .foregroundColor(.white) + } + + func sparkline(expanded: Bool) -> some View { + HStack(alignment: .top) { + Spacer() + if expanded { + Sparkline(style: .expanded, timeSeries: rankedElement?.viewCounts) + .padding(EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 16)) + } else { + Sparkline(style: .compact, timeSeries: rankedElement?.viewCounts) + .cornerRadius(4) + .frame(height: 20, alignment: .trailing) + .padding(EdgeInsets(top: 16, leading: 0, bottom: 0, trailing: 16)) + // TODO: Apply shadow just to final content – not children views as well + // .clipped() + // .readableShadow(intensity: 0.60) + } + } + } + + func description() -> some View { + VStack(alignment: .leading, spacing: 5) { + Text(TopReadWidget.LocalizedStrings.widgetTitle) + .font(.caption2) + .fontWeight(.bold) + .aspectRatio(contentMode: .fit) + .foregroundColor(primaryTextColor) + .readableShadow(intensity: isExpandedStyle ? 0 : 0.8) + Text("\(rankedElement?.title ?? "–")") + .lineLimit(nil) + .font(.headline) + .foregroundColor(primaryTextColor) + .readableShadow(intensity: isExpandedStyle ? 0 : 0.8) + } + .padding(EdgeInsets(top: 0, leading: 16, bottom: 16, trailing: 16)) + } +} diff --git a/Apps/Wikipedia/Widgets/WidgetsExtension.entitlements b/Apps/Wikipedia/Widgets/WidgetsExtension.entitlements new file mode 100644 index 0000000..e3d4cc9 --- /dev/null +++ b/Apps/Wikipedia/Widgets/WidgetsExtension.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.application-groups + + group.org.wikimedia.wikipedia + group.org.wikimedia.wikipedia.alpha + group.org.wikimedia.wikipedia.beta + + + diff --git a/Apps/Wikipedia/Wikipedia Stickers/Info.plist b/Apps/Wikipedia/Wikipedia Stickers/Info.plist new file mode 100644 index 0000000..3c57210 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Info.plist @@ -0,0 +1,35 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Wikipedia Stickers + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 7.3.0 + CFBundleVersion + 0 + UIRequiredDeviceCapabilities + + arm64 + + NSExtension + + NSExtensionPointIdentifier + com.apple.message-payload-provider + NSExtensionPrincipalClass + StickerBrowserViewController + + + diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Anatomical heart.sticker/Anatomical heart.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Anatomical heart.sticker/Anatomical heart.png new file mode 100644 index 0000000..a3c388d Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Anatomical heart.sticker/Anatomical heart.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Anatomical heart.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Anatomical heart.sticker/Contents.json new file mode 100644 index 0000000..aacd87c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Anatomical heart.sticker/Contents.json @@ -0,0 +1,10 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "accessibility-label" : "Anatomical heart", + "filename" : "Anatomical heart.png" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut animated.sticker/Astronaut_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut animated.sticker/Astronaut_2.gif new file mode 100644 index 0000000..d92b431 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut animated.sticker/Astronaut_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut animated.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut animated.sticker/Contents.json new file mode 100644 index 0000000..6d5d44d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut animated.sticker/Contents.json @@ -0,0 +1,10 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "accessibility-label" : "", + "filename" : "Astronaut_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut.sticker/Astronaut.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut.sticker/Astronaut.png new file mode 100644 index 0000000..0fcbac3 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut.sticker/Astronaut.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut.sticker/Contents.json new file mode 100644 index 0000000..dce4e9a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Astronaut.sticker/Contents.json @@ -0,0 +1,10 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "accessibility-label" : "Astronaut", + "filename" : "Astronaut.png" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Blackhole.sticker/Blackhole.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Blackhole.sticker/Blackhole.png new file mode 100644 index 0000000..381da23 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Blackhole.sticker/Blackhole.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Blackhole.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Blackhole.sticker/Contents.json new file mode 100644 index 0000000..49c062a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Blackhole.sticker/Contents.json @@ -0,0 +1,10 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "accessibility-label" : "Black hole", + "filename" : "Blackhole.png" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Books.sticker/Books_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Books.sticker/Books_2.gif new file mode 100644 index 0000000..1f2ec39 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Books.sticker/Books_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Books.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Books.sticker/Contents.json new file mode 100644 index 0000000..6378a02 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Books.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "Books_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/BrainFood.sticker/BrainFood_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/BrainFood.sticker/BrainFood_2.gif new file mode 100644 index 0000000..39d5475 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/BrainFood.sticker/BrainFood_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/BrainFood.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/BrainFood.sticker/Contents.json new file mode 100644 index 0000000..ca57c61 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/BrainFood.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "BrainFood_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Butterfly.sticker/Butterfly_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Butterfly.sticker/Butterfly_2.gif new file mode 100644 index 0000000..60d38a2 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Butterfly.sticker/Butterfly_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Butterfly.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Butterfly.sticker/Contents.json new file mode 100644 index 0000000..80f0e3d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Butterfly.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "Butterfly_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/CatLaptop.sticker/CatLaptop_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/CatLaptop.sticker/CatLaptop_2.gif new file mode 100644 index 0000000..d237b8a Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/CatLaptop.sticker/CatLaptop_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/CatLaptop.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/CatLaptop.sticker/Contents.json new file mode 100644 index 0000000..939907e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/CatLaptop.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "CatLaptop_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed .sticker/Citation needed .png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed .sticker/Citation needed .png new file mode 100644 index 0000000..b7fa003 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed .sticker/Citation needed .png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed .sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed .sticker/Contents.json new file mode 100644 index 0000000..3102d55 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed .sticker/Contents.json @@ -0,0 +1,10 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "accessibility-label" : "Citation needed", + "filename" : "Citation needed .png" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed animated.sticker/Citation_Needed_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed animated.sticker/Citation_Needed_2.gif new file mode 100644 index 0000000..0db776e Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed animated.sticker/Citation_Needed_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed animated.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed animated.sticker/Contents.json new file mode 100644 index 0000000..ee3df23 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Citation needed animated.sticker/Contents.json @@ -0,0 +1,10 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "accessibility-label" : "", + "filename" : "Citation_Needed_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Contents.json new file mode 100644 index 0000000..3c4d18b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Contents.json @@ -0,0 +1,107 @@ +{ + "stickers" : [ + { + "filename" : "Teamwork.sticker" + }, + { + "filename" : "BrainFood.sticker" + }, + { + "filename" : "Dragon.sticker" + }, + { + "filename" : "Thug.sticker" + }, + { + "filename" : "What.sticker" + }, + { + "filename" : "RabbitHole.sticker" + }, + { + "filename" : "Wordmark.sticker" + }, + { + "filename" : "Idea.sticker" + }, + { + "filename" : "Astronaut animated.sticker" + }, + { + "filename" : "Books.sticker" + }, + { + "filename" : "Butterfly.sticker" + }, + { + "filename" : "CatLaptop.sticker" + }, + { + "filename" : "Citation needed animated.sticker" + }, + { + "filename" : "Facts.sticker" + }, + { + "filename" : "Heart eyes.sticker" + }, + { + "filename" : "Help lol.sticker" + }, + { + "filename" : "Math meme.sticker" + }, + { + "filename" : "Note taking.sticker" + }, + { + "filename" : "Science.sticker" + }, + { + "filename" : "Tea dark skin tone.sticker" + }, + { + "filename" : "Tea light skin tone.sticker" + }, + { + "filename" : "Newspaper.sticker" + }, + { + "filename" : "World.sticker" + }, + { + "filename" : "Citation needed .sticker" + }, + { + "filename" : "Astronaut.sticker" + }, + { + "filename" : "Rabbit hole.sticker" + }, + { + "filename" : "Blackhole.sticker" + }, + { + "filename" : "International space station.sticker" + }, + { + "filename" : "Anatomical heart.sticker" + }, + { + "filename" : "Spacedog.sticker" + }, + { + "filename" : "Scientific blackhole.sticker" + }, + { + "filename" : "Schrodingers cat.sticker" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "grid-size" : "regular" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Dragon.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Dragon.sticker/Contents.json new file mode 100644 index 0000000..8818753 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Dragon.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "Dragon_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Dragon.sticker/Dragon_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Dragon.sticker/Dragon_2.gif new file mode 100644 index 0000000..c87168a Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Dragon.sticker/Dragon_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Facts.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Facts.sticker/Contents.json new file mode 100644 index 0000000..b44acb5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Facts.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "Facts_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Facts.sticker/Facts_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Facts.sticker/Facts_2.gif new file mode 100644 index 0000000..098df56 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Facts.sticker/Facts_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Heart eyes.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Heart eyes.sticker/Contents.json new file mode 100644 index 0000000..e0f65b9 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Heart eyes.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "HeartEyes_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Heart eyes.sticker/HeartEyes_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Heart eyes.sticker/HeartEyes_2.gif new file mode 100644 index 0000000..0927b8a Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Heart eyes.sticker/HeartEyes_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Help lol.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Help lol.sticker/Contents.json new file mode 100644 index 0000000..2f789a4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Help lol.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "HelpLol_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Help lol.sticker/HelpLol_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Help lol.sticker/HelpLol_2.gif new file mode 100644 index 0000000..bca364e Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Help lol.sticker/HelpLol_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Idea.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Idea.sticker/Contents.json new file mode 100644 index 0000000..8bd07b7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Idea.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "idea_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Idea.sticker/idea_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Idea.sticker/idea_2.gif new file mode 100644 index 0000000..9d72cdb Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Idea.sticker/idea_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/International space station.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/International space station.sticker/Contents.json new file mode 100644 index 0000000..401bbd0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/International space station.sticker/Contents.json @@ -0,0 +1,10 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "accessibility-label" : "International space station", + "filename" : "International space station.png" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/International space station.sticker/International space station.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/International space station.sticker/International space station.png new file mode 100644 index 0000000..fd06bda Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/International space station.sticker/International space station.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Math meme.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Math meme.sticker/Contents.json new file mode 100644 index 0000000..6c74985 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Math meme.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "MathMeme_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Math meme.sticker/MathMeme_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Math meme.sticker/MathMeme_2.gif new file mode 100644 index 0000000..69e67dc Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Math meme.sticker/MathMeme_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Newspaper.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Newspaper.sticker/Contents.json new file mode 100644 index 0000000..de08490 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Newspaper.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "Newspaper_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Newspaper.sticker/Newspaper_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Newspaper.sticker/Newspaper_2.gif new file mode 100644 index 0000000..d6e57e8 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Newspaper.sticker/Newspaper_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Note taking.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Note taking.sticker/Contents.json new file mode 100644 index 0000000..38b3115 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Note taking.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "Note_Taking_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Note taking.sticker/Note_Taking_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Note taking.sticker/Note_Taking_2.gif new file mode 100644 index 0000000..438cb64 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Note taking.sticker/Note_Taking_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Rabbit hole.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Rabbit hole.sticker/Contents.json new file mode 100644 index 0000000..eadba4b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Rabbit hole.sticker/Contents.json @@ -0,0 +1,10 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "accessibility-label" : "Rabbit hole", + "filename" : "Rabbit hole.png" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Rabbit hole.sticker/Rabbit hole.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Rabbit hole.sticker/Rabbit hole.png new file mode 100644 index 0000000..685066b Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Rabbit hole.sticker/Rabbit hole.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/RabbitHole.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/RabbitHole.sticker/Contents.json new file mode 100644 index 0000000..8ab27b6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/RabbitHole.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "RabbitHole_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/RabbitHole.sticker/RabbitHole_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/RabbitHole.sticker/RabbitHole_2.gif new file mode 100644 index 0000000..33fe88c Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/RabbitHole.sticker/RabbitHole_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Schrodingers cat.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Schrodingers cat.sticker/Contents.json new file mode 100644 index 0000000..e7fe117 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Schrodingers cat.sticker/Contents.json @@ -0,0 +1,10 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "accessibility-label" : "Schrödinger's cat", + "filename" : "Schrodinger's cat.png" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Schrodingers cat.sticker/Schrodinger's cat.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Schrodingers cat.sticker/Schrodinger's cat.png new file mode 100644 index 0000000..a3e6aaf Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Schrodingers cat.sticker/Schrodinger's cat.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Science.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Science.sticker/Contents.json new file mode 100644 index 0000000..6e92048 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Science.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "Science_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Science.sticker/Science_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Science.sticker/Science_2.gif new file mode 100644 index 0000000..59f4ef9 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Science.sticker/Science_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Scientific blackhole.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Scientific blackhole.sticker/Contents.json new file mode 100644 index 0000000..bdaba30 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Scientific blackhole.sticker/Contents.json @@ -0,0 +1,10 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "accessibility-label" : "Scientific black hole", + "filename" : "Scientific blackhole.png" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Scientific blackhole.sticker/Scientific blackhole.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Scientific blackhole.sticker/Scientific blackhole.png new file mode 100644 index 0000000..aae7328 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Scientific blackhole.sticker/Scientific blackhole.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Spacedog.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Spacedog.sticker/Contents.json new file mode 100644 index 0000000..21accc0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Spacedog.sticker/Contents.json @@ -0,0 +1,10 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "accessibility-label" : "Space dog", + "filename" : "Spacedog.png" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Spacedog.sticker/Spacedog.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Spacedog.sticker/Spacedog.png new file mode 100644 index 0000000..af633c2 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Spacedog.sticker/Spacedog.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea dark skin tone.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea dark skin tone.sticker/Contents.json new file mode 100644 index 0000000..028c22c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea dark skin tone.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "Tea1_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea dark skin tone.sticker/Tea1_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea dark skin tone.sticker/Tea1_2.gif new file mode 100644 index 0000000..ba0575f Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea dark skin tone.sticker/Tea1_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea light skin tone.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea light skin tone.sticker/Contents.json new file mode 100644 index 0000000..7864fd4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea light skin tone.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "Tea2_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea light skin tone.sticker/Tea2_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea light skin tone.sticker/Tea2_2.gif new file mode 100644 index 0000000..a9a9d90 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Tea light skin tone.sticker/Tea2_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Teamwork.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Teamwork.sticker/Contents.json new file mode 100644 index 0000000..159bf5b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Teamwork.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "Teamwork_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Teamwork.sticker/Teamwork_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Teamwork.sticker/Teamwork_2.gif new file mode 100644 index 0000000..0f26fac Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Teamwork.sticker/Teamwork_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Thug.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Thug.sticker/Contents.json new file mode 100644 index 0000000..7bb71aa --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Thug.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "Thug_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Thug.sticker/Thug_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Thug.sticker/Thug_2.gif new file mode 100644 index 0000000..b6c6eb1 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Thug.sticker/Thug_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/What.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/What.sticker/Contents.json new file mode 100644 index 0000000..10d87dc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/What.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "What_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/What.sticker/What_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/What.sticker/What_2.gif new file mode 100644 index 0000000..b4aa947 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/What.sticker/What_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Wordmark.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Wordmark.sticker/Contents.json new file mode 100644 index 0000000..aec17cc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Wordmark.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "wordmark_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Wordmark.sticker/wordmark_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Wordmark.sticker/wordmark_2.gif new file mode 100644 index 0000000..3cb6759 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/Wordmark.sticker/wordmark_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/World.sticker/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/World.sticker/Contents.json new file mode 100644 index 0000000..53aafee --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/World.sticker/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "filename" : "World_2.gif" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/World.sticker/World_2.gif b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/World.sticker/World_2.gif new file mode 100644 index 0000000..d2e75e5 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/Sticker Pack.stickerpack/World.sticker/World_2.gif differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/1024x768.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/1024x768.png new file mode 100644 index 0000000..167a86a Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/1024x768.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/120x90.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/120x90.png new file mode 100644 index 0000000..2c310d6 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/120x90.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/134x100.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/134x100.png new file mode 100644 index 0000000..eb3ab8b Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/134x100.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/148x110.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/148x110.png new file mode 100644 index 0000000..a2e69bb Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/148x110.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/180x135.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/180x135.png new file mode 100644 index 0000000..8a56907 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/180x135.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/54x40.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/54x40.png new file mode 100644 index 0000000..08b612b Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/54x40.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/64x48.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/64x48.png new file mode 100644 index 0000000..04c40ff Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/64x48.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/81x60.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/81x60.png new file mode 100644 index 0000000..327b828 Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/81x60.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/96x72.png b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/96x72.png new file mode 100644 index 0000000..ddff3de Binary files /dev/null and b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/96x72.png differ diff --git a/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/Contents.json b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/Contents.json new file mode 100644 index 0000000..1792a2a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia Stickers/Stickers.xcassets/iMessage App Icon.stickersiconset/Contents.json @@ -0,0 +1,67 @@ +{ + "images" : [ + { + "size" : "60x45", + "idiom" : "iphone", + "filename" : "120x90.png", + "scale" : "2x" + }, + { + "size" : "60x45", + "idiom" : "iphone", + "filename" : "180x135.png", + "scale" : "3x" + }, + { + "size" : "67x50", + "idiom" : "ipad", + "filename" : "134x100.png", + "scale" : "2x" + }, + { + "size" : "74x55", + "idiom" : "ipad", + "filename" : "148x110.png", + "scale" : "2x" + }, + { + "size" : "27x20", + "idiom" : "universal", + "filename" : "54x40.png", + "scale" : "2x", + "platform" : "ios" + }, + { + "size" : "27x20", + "idiom" : "universal", + "filename" : "81x60.png", + "scale" : "3x", + "platform" : "ios" + }, + { + "size" : "32x24", + "idiom" : "universal", + "filename" : "64x48.png", + "scale" : "2x", + "platform" : "ios" + }, + { + "size" : "32x24", + "idiom" : "universal", + "filename" : "96x72.png", + "scale" : "3x", + "platform" : "ios" + }, + { + "size" : "1024x768", + "idiom" : "ios-marketing", + "filename" : "1024x768.png", + "scale" : "1x", + "platform" : "ios" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/project.pbxproj b/Apps/Wikipedia/Wikipedia.xcodeproj/project.pbxproj new file mode 100644 index 0000000..0ff95de --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/project.pbxproj @@ -0,0 +1,20228 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 00021DE324D48EFD00476F97 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00021DE224D48EFD00476F97 /* WidgetKit.framework */; }; + 00021DE524D48EFD00476F97 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00021DE424D48EFD00476F97 /* SwiftUI.framework */; }; + 00021DE824D48EFD00476F97 /* Widgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00021DE724D48EFD00476F97 /* Widgets.swift */; }; + 00021DEA24D48EFE00476F97 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 00021DE924D48EFE00476F97 /* Assets.xcassets */; }; + 00021DEE24D48EFE00476F97 /* WidgetsExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 00021DE124D48EFD00476F97 /* WidgetsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 00021E0424D4A42A00476F97 /* PictureOfTheDayWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00021E0324D4A42A00476F97 /* PictureOfTheDayWidget.swift */; }; + 00097D5C29660FF2000B3514 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006D273424D8BAFB00947551 /* View+Extensions.swift */; }; + 00097D5D29660FF3000B3514 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006D273424D8BAFB00947551 /* View+Extensions.swift */; }; + 00097D5E29660FF3000B3514 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006D273424D8BAFB00947551 /* View+Extensions.swift */; }; + 00097D5F29660FF4000B3514 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006D273424D8BAFB00947551 /* View+Extensions.swift */; }; + 0010F93927A49C7700D77848 /* HorizontalSpacerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0010F93827A49C7700D77848 /* HorizontalSpacerView.swift */; }; + 0010F93A27A49C7700D77848 /* HorizontalSpacerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0010F93827A49C7700D77848 /* HorizontalSpacerView.swift */; }; + 0010F93B27A49C7700D77848 /* HorizontalSpacerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0010F93827A49C7700D77848 /* HorizontalSpacerView.swift */; }; + 0010F93C27A49C7700D77848 /* HorizontalSpacerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0010F93827A49C7700D77848 /* HorizontalSpacerView.swift */; }; + 0015712C27D92F6B00F1EB26 /* RetryBlockTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0015712B27D92F6B00F1EB26 /* RetryBlockTask.swift */; }; + 0022DD2925829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0022DD2825829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift */; }; + 0022DD2A25829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0022DD2825829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift */; }; + 0022DD2B25829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0022DD2825829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift */; }; + 0022DD2C25829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0022DD2825829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift */; }; + 002AB870250BEFBE00ADAC87 /* PictureOfTheDayWidget+LocalizedStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 002AB86F250BEFBE00ADAC87 /* PictureOfTheDayWidget+LocalizedStrings.swift */; }; + 0030592627DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0030592527DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift */; }; + 0030592727DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0030592527DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift */; }; + 0030592827DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0030592527DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift */; }; + 0030592927DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0030592527DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift */; }; + 0033D79924F818EC00CAB5B3 /* TopReadWidget+LocalizedStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0033D79724F818EB00CAB5B3 /* TopReadWidget+LocalizedStrings.swift */; }; + 0033D79A24F818EC00CAB5B3 /* TopReadWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0033D79824F818EC00CAB5B3 /* TopReadWidget.swift */; }; + 0033D79D24F8193900CAB5B3 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0033D79B24F8193900CAB5B3 /* UIColor+Extensions.swift */; }; + 0033D79E24F8193900CAB5B3 /* CGPoint+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0033D79C24F8193900CAB5B3 /* CGPoint+Extensions.swift */; }; + 0033D7A124F8199300CAB5B3 /* Sparkline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0033D7A024F8199300CAB5B3 /* Sparkline.swift */; }; + 0036C8B3282C2AAA00EADB35 /* Notification+NotificationsCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0036C8B2282C2AAA00EADB35 /* Notification+NotificationsCenter.swift */; }; + 003AD72E2979C512005BDB90 /* EditNoticesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003AD72D2979C512005BDB90 /* EditNoticesViewModel.swift */; }; + 003AD72F2979C512005BDB90 /* EditNoticesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003AD72D2979C512005BDB90 /* EditNoticesViewModel.swift */; }; + 003AD7302979C512005BDB90 /* EditNoticesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003AD72D2979C512005BDB90 /* EditNoticesViewModel.swift */; }; + 003AD7312979C512005BDB90 /* EditNoticesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003AD72D2979C512005BDB90 /* EditNoticesViewModel.swift */; }; + 003CD3E928EF7C77000158E4 /* TalkPageFindInPageSearchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003CD3E828EF7C77000158E4 /* TalkPageFindInPageSearchController.swift */; }; + 003CD3EA28EF7C77000158E4 /* TalkPageFindInPageSearchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003CD3E828EF7C77000158E4 /* TalkPageFindInPageSearchController.swift */; }; + 003CD3EB28EF7C77000158E4 /* TalkPageFindInPageSearchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003CD3E828EF7C77000158E4 /* TalkPageFindInPageSearchController.swift */; }; + 003CD3EC28EF7C77000158E4 /* TalkPageFindInPageSearchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003CD3E828EF7C77000158E4 /* TalkPageFindInPageSearchController.swift */; }; + 0042806C25E6E395004945B3 /* FLAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042804025E6E395004945B3 /* FLAnimatedImage.m */; }; + 0042806D25E6E395004945B3 /* FLAnimatedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042804125E6E395004945B3 /* FLAnimatedImageView.m */; }; + 0042806E25E6E395004945B3 /* FLAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042804225E6E395004945B3 /* FLAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042806F25E6E395004945B3 /* FLAnimatedImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042804325E6E395004945B3 /* FLAnimatedImageView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042807125E6E395004945B3 /* NSValueTransformer+MTLPredefinedTransformerAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042804625E6E395004945B3 /* NSValueTransformer+MTLPredefinedTransformerAdditions.m */; }; + 0042807225E6E395004945B3 /* NSError+MTLModelException.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042804725E6E395004945B3 /* NSError+MTLModelException.m */; }; + 0042807325E6E395004945B3 /* NSDictionary+MTLJSONKeyPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042804825E6E395004945B3 /* NSDictionary+MTLJSONKeyPath.h */; }; + 0042807425E6E395004945B3 /* MTLEXTRuntimeExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042804B25E6E395004945B3 /* MTLEXTRuntimeExtensions.h */; }; + 0042807525E6E395004945B3 /* MTLEXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042804C25E6E395004945B3 /* MTLEXTScope.h */; }; + 0042807625E6E395004945B3 /* MTLMetamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042804D25E6E395004945B3 /* MTLMetamacros.h */; }; + 0042807725E6E395004945B3 /* MTLEXTKeyPathCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042804E25E6E395004945B3 /* MTLEXTKeyPathCoding.h */; }; + 0042807825E6E395004945B3 /* MTLEXTScope.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042804F25E6E395004945B3 /* MTLEXTScope.m */; }; + 0042807925E6E395004945B3 /* MTLEXTRuntimeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042805025E6E395004945B3 /* MTLEXTRuntimeExtensions.m */; }; + 0042807A25E6E395004945B3 /* MTLJSONAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042805125E6E395004945B3 /* MTLJSONAdapter.m */; }; + 0042807B25E6E395004945B3 /* MTLModel+NSCoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042805225E6E395004945B3 /* MTLModel+NSCoding.m */; }; + 0042807C25E6E395004945B3 /* NSObject+MTLComparisonAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805425E6E395004945B3 /* NSObject+MTLComparisonAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042807D25E6E395004945B3 /* MTLValueTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805525E6E395004945B3 /* MTLValueTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042807E25E6E395004945B3 /* MTLTransformerErrorHandling.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805625E6E395004945B3 /* MTLTransformerErrorHandling.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042807F25E6E395004945B3 /* NSValueTransformer+MTLInversionAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805725E6E395004945B3 /* NSValueTransformer+MTLInversionAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042808025E6E395004945B3 /* NSDictionary+MTLManipulationAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805825E6E395004945B3 /* NSDictionary+MTLManipulationAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042808125E6E395004945B3 /* Mantle.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805925E6E395004945B3 /* Mantle.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042808225E6E395004945B3 /* NSValueTransformer+MTLPredefinedTransformerAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805A25E6E395004945B3 /* NSValueTransformer+MTLPredefinedTransformerAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042808325E6E395004945B3 /* MTLJSONAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805B25E6E395004945B3 /* MTLJSONAdapter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042808425E6E395004945B3 /* MTLModel+NSCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805C25E6E395004945B3 /* MTLModel+NSCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042808525E6E395004945B3 /* MTLModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805D25E6E395004945B3 /* MTLModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042808625E6E395004945B3 /* NSDictionary+MTLMappingAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805E25E6E395004945B3 /* NSDictionary+MTLMappingAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042808725E6E395004945B3 /* NSArray+MTLManipulationAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042805F25E6E395004945B3 /* NSArray+MTLManipulationAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0042808825E6E395004945B3 /* NSArray+MTLManipulationAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042806025E6E395004945B3 /* NSArray+MTLManipulationAdditions.m */; }; + 0042808925E6E395004945B3 /* MTLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042806125E6E395004945B3 /* MTLModel.m */; }; + 0042808A25E6E395004945B3 /* NSDictionary+MTLMappingAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042806225E6E395004945B3 /* NSDictionary+MTLMappingAdditions.m */; }; + 0042808B25E6E395004945B3 /* MTLReflection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042806325E6E395004945B3 /* MTLReflection.h */; }; + 0042808C25E6E395004945B3 /* NSError+MTLModelException.h in Headers */ = {isa = PBXBuildFile; fileRef = 0042806425E6E395004945B3 /* NSError+MTLModelException.h */; }; + 0042808D25E6E395004945B3 /* MTLValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042806525E6E395004945B3 /* MTLValueTransformer.m */; }; + 0042808E25E6E395004945B3 /* NSObject+MTLComparisonAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042806625E6E395004945B3 /* NSObject+MTLComparisonAdditions.m */; }; + 0042808F25E6E395004945B3 /* NSDictionary+MTLJSONKeyPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042806725E6E395004945B3 /* NSDictionary+MTLJSONKeyPath.m */; }; + 0042809025E6E395004945B3 /* NSValueTransformer+MTLInversionAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042806825E6E395004945B3 /* NSValueTransformer+MTLInversionAdditions.m */; }; + 0042809125E6E395004945B3 /* MTLTransformerErrorHandling.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042806925E6E395004945B3 /* MTLTransformerErrorHandling.m */; }; + 0042809225E6E395004945B3 /* MTLReflection.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042806A25E6E395004945B3 /* MTLReflection.m */; }; + 0042809325E6E395004945B3 /* NSDictionary+MTLManipulationAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042806B25E6E395004945B3 /* NSDictionary+MTLManipulationAdditions.m */; }; + 0042811525E6E841004945B3 /* NYTPhotoViewer.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 004280F825E6E841004945B3 /* NYTPhotoViewer.bundle */; }; + 0042811625E6E841004945B3 /* NYTPhotoViewer.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 004280F825E6E841004945B3 /* NYTPhotoViewer.bundle */; }; + 0042811725E6E841004945B3 /* NYTPhotoViewer.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 004280F825E6E841004945B3 /* NYTPhotoViewer.bundle */; }; + 0042811825E6E841004945B3 /* NYTPhotoViewer.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 004280F825E6E841004945B3 /* NYTPhotoViewer.bundle */; }; + 0042811925E6E841004945B3 /* NYTPhotosOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FA25E6E841004945B3 /* NYTPhotosOverlayView.m */; }; + 0042811A25E6E841004945B3 /* NYTPhotosOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FA25E6E841004945B3 /* NYTPhotosOverlayView.m */; }; + 0042811B25E6E841004945B3 /* NYTPhotosOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FA25E6E841004945B3 /* NYTPhotosOverlayView.m */; }; + 0042811C25E6E841004945B3 /* NYTPhotosOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FA25E6E841004945B3 /* NYTPhotosOverlayView.m */; }; + 0042811D25E6E841004945B3 /* NYTPhotoTransitionAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FB25E6E841004945B3 /* NYTPhotoTransitionAnimator.m */; }; + 0042811E25E6E841004945B3 /* NYTPhotoTransitionAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FB25E6E841004945B3 /* NYTPhotoTransitionAnimator.m */; }; + 0042811F25E6E841004945B3 /* NYTPhotoTransitionAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FB25E6E841004945B3 /* NYTPhotoTransitionAnimator.m */; }; + 0042812025E6E841004945B3 /* NYTPhotoTransitionAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FB25E6E841004945B3 /* NYTPhotoTransitionAnimator.m */; }; + 0042812125E6E841004945B3 /* NSBundle+NYTPhotoViewer.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FD25E6E841004945B3 /* NSBundle+NYTPhotoViewer.m */; }; + 0042812225E6E841004945B3 /* NSBundle+NYTPhotoViewer.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FD25E6E841004945B3 /* NSBundle+NYTPhotoViewer.m */; }; + 0042812325E6E841004945B3 /* NSBundle+NYTPhotoViewer.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FD25E6E841004945B3 /* NSBundle+NYTPhotoViewer.m */; }; + 0042812425E6E841004945B3 /* NSBundle+NYTPhotoViewer.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FD25E6E841004945B3 /* NSBundle+NYTPhotoViewer.m */; }; + 0042812525E6E841004945B3 /* NYTPhotoTransitionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FF25E6E841004945B3 /* NYTPhotoTransitionController.m */; }; + 0042812625E6E841004945B3 /* NYTPhotoTransitionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FF25E6E841004945B3 /* NYTPhotoTransitionController.m */; }; + 0042812725E6E841004945B3 /* NYTPhotoTransitionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FF25E6E841004945B3 /* NYTPhotoTransitionController.m */; }; + 0042812825E6E841004945B3 /* NYTPhotoTransitionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 004280FF25E6E841004945B3 /* NYTPhotoTransitionController.m */; }; + 0042812925E6E841004945B3 /* NYTPhotoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810025E6E841004945B3 /* NYTPhotoViewController.m */; }; + 0042812A25E6E841004945B3 /* NYTPhotoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810025E6E841004945B3 /* NYTPhotoViewController.m */; }; + 0042812B25E6E841004945B3 /* NYTPhotoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810025E6E841004945B3 /* NYTPhotoViewController.m */; }; + 0042812C25E6E841004945B3 /* NYTPhotoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810025E6E841004945B3 /* NYTPhotoViewController.m */; }; + 0042812D25E6E841004945B3 /* NYTPhotoCaptionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810225E6E841004945B3 /* NYTPhotoCaptionView.m */; }; + 0042812E25E6E841004945B3 /* NYTPhotoCaptionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810225E6E841004945B3 /* NYTPhotoCaptionView.m */; }; + 0042812F25E6E841004945B3 /* NYTPhotoCaptionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810225E6E841004945B3 /* NYTPhotoCaptionView.m */; }; + 0042813025E6E841004945B3 /* NYTPhotoCaptionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810225E6E841004945B3 /* NYTPhotoCaptionView.m */; }; + 0042813125E6E841004945B3 /* NYTPhotosDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810425E6E841004945B3 /* NYTPhotosDataSource.m */; }; + 0042813225E6E841004945B3 /* NYTPhotosDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810425E6E841004945B3 /* NYTPhotosDataSource.m */; }; + 0042813325E6E841004945B3 /* NYTPhotosDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810425E6E841004945B3 /* NYTPhotosDataSource.m */; }; + 0042813425E6E841004945B3 /* NYTPhotosDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810425E6E841004945B3 /* NYTPhotosDataSource.m */; }; + 0042813525E6E841004945B3 /* NYTPhotoDismissalInteractionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810725E6E841004945B3 /* NYTPhotoDismissalInteractionController.m */; }; + 0042813625E6E841004945B3 /* NYTPhotoDismissalInteractionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810725E6E841004945B3 /* NYTPhotoDismissalInteractionController.m */; }; + 0042813725E6E841004945B3 /* NYTPhotoDismissalInteractionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810725E6E841004945B3 /* NYTPhotoDismissalInteractionController.m */; }; + 0042813825E6E841004945B3 /* NYTPhotoDismissalInteractionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810725E6E841004945B3 /* NYTPhotoDismissalInteractionController.m */; }; + 0042813925E6E841004945B3 /* NYTScalingImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810C25E6E841004945B3 /* NYTScalingImageView.m */; }; + 0042813A25E6E841004945B3 /* NYTScalingImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810C25E6E841004945B3 /* NYTScalingImageView.m */; }; + 0042813B25E6E841004945B3 /* NYTScalingImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810C25E6E841004945B3 /* NYTScalingImageView.m */; }; + 0042813C25E6E841004945B3 /* NYTScalingImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042810C25E6E841004945B3 /* NYTScalingImageView.m */; }; + 0042813D25E6E841004945B3 /* NYTPhotosViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042811325E6E841004945B3 /* NYTPhotosViewController.m */; }; + 0042813E25E6E841004945B3 /* NYTPhotosViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042811325E6E841004945B3 /* NYTPhotosViewController.m */; }; + 0042813F25E6E841004945B3 /* NYTPhotosViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042811325E6E841004945B3 /* NYTPhotosViewController.m */; }; + 0042814025E6E841004945B3 /* NYTPhotosViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042811325E6E841004945B3 /* NYTPhotosViewController.m */; }; + 004281B225E6EFC4004945B3 /* LSStubRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042817125E6EFC4004945B3 /* LSStubRequest.m */; }; + 004281B325E6EFC4004945B3 /* LSStubResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042817225E6EFC4004945B3 /* LSStubResponse.m */; }; + 004281B425E6EFC4004945B3 /* LSNocilla.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042817525E6EFC4004945B3 /* LSNocilla.m */; }; + 004281B525E6EFC4004945B3 /* LSHTTPRequestDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042817825E6EFC4004945B3 /* LSHTTPRequestDiff.m */; }; + 004281B625E6EFC4004945B3 /* LSHTTPClientHook.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042817C25E6EFC4004945B3 /* LSHTTPClientHook.m */; }; + 004281B725E6EFC4004945B3 /* NSURLRequest+LSHTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042817F25E6EFC4004945B3 /* NSURLRequest+LSHTTPRequest.m */; }; + 004281B825E6EFC4004945B3 /* LSNSURLHook.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042818425E6EFC4004945B3 /* LSNSURLHook.m */; }; + 004281B925E6EFC4004945B3 /* NSURLRequest+DSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042818525E6EFC4004945B3 /* NSURLRequest+DSL.m */; }; + 004281BA25E6EFC4004945B3 /* LSHTTPStubURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042818625E6EFC4004945B3 /* LSHTTPStubURLProtocol.m */; }; + 004281BB25E6EFC4004945B3 /* ASIHTTPRequestStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042818925E6EFC4004945B3 /* ASIHTTPRequestStub.m */; }; + 004281BC25E6EFC4004945B3 /* LSASIHTTPRequestHook.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042818B25E6EFC4004945B3 /* LSASIHTTPRequestHook.m */; }; + 004281BD25E6EFC4004945B3 /* LSASIHTTPRequestAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042818C25E6EFC4004945B3 /* LSASIHTTPRequestAdapter.m */; }; + 004281BE25E6EFC4004945B3 /* LSNSURLSessionHook.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042819025E6EFC4004945B3 /* LSNSURLSessionHook.m */; }; + 004281BF25E6EFC4004945B3 /* NSRegularExpression+Matcheable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042819A25E6EFC4004945B3 /* NSRegularExpression+Matcheable.m */; }; + 004281C025E6EFC4004945B3 /* NSString+Matcheable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042819C25E6EFC4004945B3 /* NSString+Matcheable.m */; }; + 004281C125E6EFC4004945B3 /* NSData+Matcheable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042819D25E6EFC4004945B3 /* NSData+Matcheable.m */; }; + 004281C225E6EFC4004945B3 /* LSDataMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042819E25E6EFC4004945B3 /* LSDataMatcher.m */; }; + 004281C325E6EFC4004945B3 /* LSMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0042819F25E6EFC4004945B3 /* LSMatcher.m */; }; + 004281C425E6EFC4004945B3 /* LSRegexMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 004281A025E6EFC4004945B3 /* LSRegexMatcher.m */; }; + 004281C525E6EFC4004945B3 /* LSStringMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 004281A225E6EFC4004945B3 /* LSStringMatcher.m */; }; + 004281C625E6EFC4004945B3 /* NSString+Nocilla.m in Sources */ = {isa = PBXBuildFile; fileRef = 004281A825E6EFC4004945B3 /* NSString+Nocilla.m */; }; + 004281C725E6EFC4004945B3 /* NSData+Nocilla.m in Sources */ = {isa = PBXBuildFile; fileRef = 004281A925E6EFC4004945B3 /* NSData+Nocilla.m */; }; + 004281C825E6EFC4004945B3 /* LSHTTPRequestDSLRepresentation.m in Sources */ = {isa = PBXBuildFile; fileRef = 004281AC25E6EFC4004945B3 /* LSHTTPRequestDSLRepresentation.m */; }; + 004281C925E6EFC4004945B3 /* LSStubResponseDSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 004281AE25E6EFC4004945B3 /* LSStubResponseDSL.m */; }; + 004281CA25E6EFC4004945B3 /* LSStubRequestDSL.m in Sources */ = {isa = PBXBuildFile; fileRef = 004281AF25E6EFC4004945B3 /* LSStubRequestDSL.m */; }; + 00474A2A28DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00474A2928DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift */; }; + 00474A2B28DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00474A2928DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift */; }; + 00474A2C28DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00474A2928DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift */; }; + 00474A2D28DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00474A2928DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift */; }; + 00474A2F28DD1B13002E3C09 /* TalkPageCoffeeRollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00474A2E28DD1B13002E3C09 /* TalkPageCoffeeRollView.swift */; }; + 00474A3028DD1B13002E3C09 /* TalkPageCoffeeRollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00474A2E28DD1B13002E3C09 /* TalkPageCoffeeRollView.swift */; }; + 00474A3128DD1B13002E3C09 /* TalkPageCoffeeRollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00474A2E28DD1B13002E3C09 /* TalkPageCoffeeRollView.swift */; }; + 00474A3228DD1B13002E3C09 /* TalkPageCoffeeRollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00474A2E28DD1B13002E3C09 /* TalkPageCoffeeRollView.swift */; }; + 00550D2626B1E7DB0055C496 /* Featured Article Widget Preview Content.json in Resources */ = {isa = PBXBuildFile; fileRef = 00550D2526B1E7DB0055C496 /* Featured Article Widget Preview Content.json */; }; + 005E004128DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E004028DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift */; }; + 005E004228DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E004028DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift */; }; + 005E004328DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E004028DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift */; }; + 005E004428DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E004028DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift */; }; + 0062597324DE0A2500C95037 /* WidgetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0062597224DE0A2500C95037 /* WidgetController.swift */; }; + 006694FC265D9F2900E23AE4 /* WidgetSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006694FB265D9F2900E23AE4 /* WidgetSettings.swift */; }; + 006694FE265D9F3A00E23AE4 /* WidgetCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006694FD265D9F3A00E23AE4 /* WidgetCache.swift */; }; + 00669500265DA01000E23AE4 /* WidgetContentFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006694FF265DA01000E23AE4 /* WidgetContentFetcher.swift */; }; + 00669505265DA3D300E23AE4 /* FeaturedArticleWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00669504265DA3D300E23AE4 /* FeaturedArticleWidget.swift */; }; + 00669507265DAB7800E23AE4 /* FeaturedArticleWidget+LocalizedStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00669506265DAB7800E23AE4 /* FeaturedArticleWidget+LocalizedStrings.swift */; }; + 0066BE30265EC4A900512BE8 /* WidgetFeaturedContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0066BE2F265EC4A900512BE8 /* WidgetFeaturedContent.swift */; }; + 006ABEE82901E8F600722DF8 /* VanishAccountWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006ABEE72901E8F600722DF8 /* VanishAccountWarningView.swift */; }; + 006ABEE92901E8F600722DF8 /* VanishAccountWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006ABEE72901E8F600722DF8 /* VanishAccountWarningView.swift */; }; + 006ABEEA2901E8F600722DF8 /* VanishAccountWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006ABEE72901E8F600722DF8 /* VanishAccountWarningView.swift */; }; + 006ABEEB2901E8F600722DF8 /* VanishAccountWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006ABEE72901E8F600722DF8 /* VanishAccountWarningView.swift */; }; + 006ABEED2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006ABEEC2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift */; }; + 006ABEEE2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006ABEEC2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift */; }; + 006ABEEF2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006ABEEC2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift */; }; + 006ABEF02901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006ABEEC2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift */; }; + 006D273524D8BAFB00947551 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006D273424D8BAFB00947551 /* View+Extensions.swift */; }; + 006D273724D8D8D100947551 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 006D273624D8D8D100947551 /* Date+Extensions.swift */; }; + 0072990628AC44F100DCD2E6 /* TalkPageCellTopicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072990528AC44F100DCD2E6 /* TalkPageCellTopicView.swift */; }; + 0072990728AC44F100DCD2E6 /* TalkPageCellTopicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072990528AC44F100DCD2E6 /* TalkPageCellTopicView.swift */; }; + 0072990828AC44F100DCD2E6 /* TalkPageCellTopicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072990528AC44F100DCD2E6 /* TalkPageCellTopicView.swift */; }; + 0072990928AC44F100DCD2E6 /* TalkPageCellTopicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072990528AC44F100DCD2E6 /* TalkPageCellTopicView.swift */; }; + 0072990B28AC455500DCD2E6 /* TalkPageCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072990A28AC455500DCD2E6 /* TalkPageCellViewModel.swift */; }; + 0072990C28AC455500DCD2E6 /* TalkPageCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072990A28AC455500DCD2E6 /* TalkPageCellViewModel.swift */; }; + 0072990D28AC455500DCD2E6 /* TalkPageCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072990A28AC455500DCD2E6 /* TalkPageCellViewModel.swift */; }; + 0072990E28AC455500DCD2E6 /* TalkPageCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072990A28AC455500DCD2E6 /* TalkPageCellViewModel.swift */; }; + 0072991528AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991428AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift */; }; + 0072991628AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991428AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift */; }; + 0072991728AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991428AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift */; }; + 0072991828AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991428AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift */; }; + 0072991A28AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991928AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift */; }; + 0072991B28AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991928AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift */; }; + 0072991C28AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991928AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift */; }; + 0072991D28AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991928AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift */; }; + 0072991F28AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991E28AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift */; }; + 0072992028AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991E28AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift */; }; + 0072992128AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991E28AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift */; }; + 0072992228AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0072991E28AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift */; }; + 007B5FC526FA40F100180FF8 /* RemoteNotificationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007B5FC426FA40F000180FF8 /* RemoteNotificationType.swift */; }; + 007CCF0126D5A10200D5EA7C /* NotificationsCenterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0026D5A10100D5EA7C /* NotificationsCenterViewController.swift */; }; + 007CCF0226D5A10200D5EA7C /* NotificationsCenterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0026D5A10100D5EA7C /* NotificationsCenterViewController.swift */; }; + 007CCF0326D5A10200D5EA7C /* NotificationsCenterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0026D5A10100D5EA7C /* NotificationsCenterViewController.swift */; }; + 007CCF0426D5A10200D5EA7C /* NotificationsCenterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0026D5A10100D5EA7C /* NotificationsCenterViewController.swift */; }; + 007CCF0726D5A17200D5EA7C /* NotificationsCenterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0626D5A17200D5EA7C /* NotificationsCenterView.swift */; }; + 007CCF0826D5A17200D5EA7C /* NotificationsCenterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0626D5A17200D5EA7C /* NotificationsCenterView.swift */; }; + 007CCF0926D5A17200D5EA7C /* NotificationsCenterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0626D5A17200D5EA7C /* NotificationsCenterView.swift */; }; + 007CCF0A26D5A17200D5EA7C /* NotificationsCenterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0626D5A17200D5EA7C /* NotificationsCenterView.swift */; }; + 007CCF0C26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0B26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift */; }; + 007CCF0D26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0B26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift */; }; + 007CCF0E26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0B26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift */; }; + 007CCF0F26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF0B26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift */; }; + 007CCF1126D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF1026D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift */; }; + 007CCF1226D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF1026D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift */; }; + 007CCF1326D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF1026D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift */; }; + 007CCF1426D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007CCF1026D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift */; }; + 007F5C6D275AA74200E4B02C /* StackedImageLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007F5C6C275AA74200E4B02C /* StackedImageLabelView.swift */; }; + 007F5C6E275AA74200E4B02C /* StackedImageLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007F5C6C275AA74200E4B02C /* StackedImageLabelView.swift */; }; + 007F5C6F275AA74200E4B02C /* StackedImageLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007F5C6C275AA74200E4B02C /* StackedImageLabelView.swift */; }; + 007F5C70275AA74200E4B02C /* StackedImageLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007F5C6C275AA74200E4B02C /* StackedImageLabelView.swift */; }; + 00841DE524477805003CF74A /* AppTabBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE891452445150B0058B642 /* AppTabBarDelegate.swift */; }; + 00841DE724477806003CF74A /* AppTabBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE891452445150B0058B642 /* AppTabBarDelegate.swift */; }; + 009B8358298091BC00AABEA3 /* EditNoticesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009B8357298091BC00AABEA3 /* EditNoticesViewController.swift */; }; + 009B8359298091BC00AABEA3 /* EditNoticesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009B8357298091BC00AABEA3 /* EditNoticesViewController.swift */; }; + 009B835A298091BC00AABEA3 /* EditNoticesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009B8357298091BC00AABEA3 /* EditNoticesViewController.swift */; }; + 009B835B298091BC00AABEA3 /* EditNoticesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009B8357298091BC00AABEA3 /* EditNoticesViewController.swift */; }; + 009B835D298091CD00AABEA3 /* EditNoticesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009B835C298091CD00AABEA3 /* EditNoticesView.swift */; }; + 009B835E298091CD00AABEA3 /* EditNoticesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009B835C298091CD00AABEA3 /* EditNoticesView.swift */; }; + 009B835F298091CD00AABEA3 /* EditNoticesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009B835C298091CD00AABEA3 /* EditNoticesView.swift */; }; + 009B8360298091CD00AABEA3 /* EditNoticesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009B835C298091CD00AABEA3 /* EditNoticesView.swift */; }; + 009C8EC229071E720056A3AC /* NSString+Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009C8EC129071E720056A3AC /* NSString+Range.swift */; }; + 009C8EC329071E720056A3AC /* NSString+Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009C8EC129071E720056A3AC /* NSString+Range.swift */; }; + 009C8EC429071E720056A3AC /* NSString+Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009C8EC129071E720056A3AC /* NSString+Range.swift */; }; + 009C8EC529071E720056A3AC /* NSString+Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009C8EC129071E720056A3AC /* NSString+Range.swift */; }; + 00A7946B245CA4E60063BA18 /* ArticleSurveyTimerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A7946A245CA4E60063BA18 /* ArticleSurveyTimerController.swift */; }; + 00A7946C245CA4E60063BA18 /* ArticleSurveyTimerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A7946A245CA4E60063BA18 /* ArticleSurveyTimerController.swift */; }; + 00A7946D245CA4E60063BA18 /* ArticleSurveyTimerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A7946A245CA4E60063BA18 /* ArticleSurveyTimerController.swift */; }; + 00A7946E245CA4E60063BA18 /* ArticleSurveyTimerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A7946A245CA4E60063BA18 /* ArticleSurveyTimerController.swift */; }; + 00A8F58626BDD5E700175B8E /* WidgetSampleContentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A8F58526BDD5E700175B8E /* WidgetSampleContentTests.swift */; }; + 00A8F58826BDD88700175B8E /* Featured Article Widget Preview Content.json in Resources */ = {isa = PBXBuildFile; fileRef = 00550D2526B1E7DB0055C496 /* Featured Article Widget Preview Content.json */; }; + 00A988082829D92B006D800B /* PushNotificationContentIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A988072829D92B006D800B /* PushNotificationContentIdentifier.swift */; }; + 00A988092829D92B006D800B /* PushNotificationContentIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A988072829D92B006D800B /* PushNotificationContentIdentifier.swift */; }; + 00AA5AA7276BF29E005295B0 /* StatusTextBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00AA5AA6276BF29E005295B0 /* StatusTextBarButtonItem.swift */; }; + 00AA5AA8276BF29E005295B0 /* StatusTextBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00AA5AA6276BF29E005295B0 /* StatusTextBarButtonItem.swift */; }; + 00AA5AA9276BF29E005295B0 /* StatusTextBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00AA5AA6276BF29E005295B0 /* StatusTextBarButtonItem.swift */; }; + 00AA5AAA276BF29E005295B0 /* StatusTextBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00AA5AA6276BF29E005295B0 /* StatusTextBarButtonItem.swift */; }; + 00AA5AAC276BF2AE005295B0 /* TextBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00AA5AAB276BF2AE005295B0 /* TextBarButtonItem.swift */; }; + 00AA5AAD276BF2AE005295B0 /* TextBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00AA5AAB276BF2AE005295B0 /* TextBarButtonItem.swift */; }; + 00AA5AAE276BF2AE005295B0 /* TextBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00AA5AAB276BF2AE005295B0 /* TextBarButtonItem.swift */; }; + 00AA5AAF276BF2AE005295B0 /* TextBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00AA5AAB276BF2AE005295B0 /* TextBarButtonItem.swift */; }; + 00AB75BD24D4E8FB0041056A /* WMF.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; }; + 00B0B3D02978745400DD7893 /* EditNoticesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B0B3CF2978745400DD7893 /* EditNoticesFetcher.swift */; }; + 00B0B3D12978745400DD7893 /* EditNoticesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B0B3CF2978745400DD7893 /* EditNoticesFetcher.swift */; }; + 00B0B3D22978745400DD7893 /* EditNoticesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B0B3CF2978745400DD7893 /* EditNoticesFetcher.swift */; }; + 00B0B3D32978745400DD7893 /* EditNoticesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B0B3CF2978745400DD7893 /* EditNoticesFetcher.swift */; }; + 00B16E8E293AACC200EF847F /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B16E8D293AACC200EF847F /* UIImage+Extensions.swift */; }; + 00BCB71826DEE04D002C3F72 /* InsetLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB71726DEE04D002C3F72 /* InsetLabelView.swift */; }; + 00BCB71926DEE11B002C3F72 /* InsetLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB71726DEE04D002C3F72 /* InsetLabelView.swift */; }; + 00BCB71A26DEE11C002C3F72 /* InsetLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB71726DEE04D002C3F72 /* InsetLabelView.swift */; }; + 00BCB71B26DEE11C002C3F72 /* InsetLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB71726DEE04D002C3F72 /* InsetLabelView.swift */; }; + 00BCB71D26DEE1C7002C3F72 /* VerticalSpacerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB71C26DEE1C7002C3F72 /* VerticalSpacerView.swift */; }; + 00BCB71E26DEE1C7002C3F72 /* VerticalSpacerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB71C26DEE1C7002C3F72 /* VerticalSpacerView.swift */; }; + 00BCB71F26DEE1C7002C3F72 /* VerticalSpacerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB71C26DEE1C7002C3F72 /* VerticalSpacerView.swift */; }; + 00BCB72026DEE1C7002C3F72 /* VerticalSpacerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB71C26DEE1C7002C3F72 /* VerticalSpacerView.swift */; }; + 00BCB72226DEEB1C002C3F72 /* RoundedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB72126DEEB1C002C3F72 /* RoundedImageView.swift */; }; + 00BCB72326DEEB1C002C3F72 /* RoundedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB72126DEEB1C002C3F72 /* RoundedImageView.swift */; }; + 00BCB72426DEEB1C002C3F72 /* RoundedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB72126DEEB1C002C3F72 /* RoundedImageView.swift */; }; + 00BCB72526DEEB1C002C3F72 /* RoundedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BCB72126DEEB1C002C3F72 /* RoundedImageView.swift */; }; + 00CB6898288B0CD3002EBB0A /* TalkPageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CB6897288B0CD3002EBB0A /* TalkPageHeaderView.swift */; }; + 00CB6899288B0CD3002EBB0A /* TalkPageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CB6897288B0CD3002EBB0A /* TalkPageHeaderView.swift */; }; + 00CB689A288B0CD3002EBB0A /* TalkPageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CB6897288B0CD3002EBB0A /* TalkPageHeaderView.swift */; }; + 00CB689B288B0CD3002EBB0A /* TalkPageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CB6897288B0CD3002EBB0A /* TalkPageHeaderView.swift */; }; + 00CF2EA027DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CF2E9F27DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift */; }; + 00CF2EA127DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CF2E9F27DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift */; }; + 00CF2EA227DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CF2E9F27DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift */; }; + 00CF2EA327DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00CF2E9F27DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift */; }; + 00D1F58F28885BA300127169 /* TalkPageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D1F58E28885BA300127169 /* TalkPageViewModel.swift */; }; + 00D1F59028885BA300127169 /* TalkPageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D1F58E28885BA300127169 /* TalkPageViewModel.swift */; }; + 00D1F59128885BA300127169 /* TalkPageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D1F58E28885BA300127169 /* TalkPageViewModel.swift */; }; + 00D1F59228885BA300127169 /* TalkPageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D1F58E28885BA300127169 /* TalkPageViewModel.swift */; }; + 00D280F7247EFFFE006BEE23 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D280F6247EFFFE006BEE23 /* Date+Extensions.swift */; }; + 00D280F8247EFFFE006BEE23 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D280F6247EFFFE006BEE23 /* Date+Extensions.swift */; }; + 00D280F9247EFFFE006BEE23 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D280F6247EFFFE006BEE23 /* Date+Extensions.swift */; }; + 00D280FA247EFFFE006BEE23 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D280F6247EFFFE006BEE23 /* Date+Extensions.swift */; }; + 00D280FC247F019C006BEE23 /* Date+ExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D280FB247F019C006BEE23 /* Date+ExtensionTests.swift */; }; + 00D46DA52889B7F50015DE9B /* TalkPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D46DA42889B7F50015DE9B /* TalkPageView.swift */; }; + 00D46DA62889B7F50015DE9B /* TalkPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D46DA42889B7F50015DE9B /* TalkPageView.swift */; }; + 00D46DA72889B7F50015DE9B /* TalkPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D46DA42889B7F50015DE9B /* TalkPageView.swift */; }; + 00D46DA82889B7F50015DE9B /* TalkPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D46DA42889B7F50015DE9B /* TalkPageView.swift */; }; + 00D46DAA2889B9250015DE9B /* TalkPageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D46DA92889B9250015DE9B /* TalkPageCell.swift */; }; + 00D46DAB2889B9250015DE9B /* TalkPageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D46DA92889B9250015DE9B /* TalkPageCell.swift */; }; + 00D46DAC2889B9250015DE9B /* TalkPageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D46DA92889B9250015DE9B /* TalkPageCell.swift */; }; + 00D46DAD2889B9250015DE9B /* TalkPageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D46DA92889B9250015DE9B /* TalkPageCell.swift */; }; + 00D4B1B4282996A2008C705C /* EchoModelVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D4B1B3282996A2008C705C /* EchoModelVersion.swift */; }; + 00D9276B29511E95004ECBEA /* PageHistoryCountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D9276A29511E95004ECBEA /* PageHistoryCountsView.swift */; }; + 00D9276C29511E95004ECBEA /* PageHistoryCountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D9276A29511E95004ECBEA /* PageHistoryCountsView.swift */; }; + 00D9276D29511E95004ECBEA /* PageHistoryCountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D9276A29511E95004ECBEA /* PageHistoryCountsView.swift */; }; + 00D9276E29511E95004ECBEA /* PageHistoryCountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D9276A29511E95004ECBEA /* PageHistoryCountsView.swift */; }; + 00DEE61928AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00DEE61828AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift */; }; + 00DEE61A28AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00DEE61828AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift */; }; + 00DEE61B28AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00DEE61828AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift */; }; + 00DEE61C28AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00DEE61828AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift */; }; + 00E2EA8926E28A9700B1A741 /* NotificationsCenterCellStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E2EA8826E28A9700B1A741 /* NotificationsCenterCellStyle.swift */; }; + 00E2EA8A26E28A9700B1A741 /* NotificationsCenterCellStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E2EA8826E28A9700B1A741 /* NotificationsCenterCellStyle.swift */; }; + 00E2EA8B26E28A9700B1A741 /* NotificationsCenterCellStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E2EA8826E28A9700B1A741 /* NotificationsCenterCellStyle.swift */; }; + 00E2EA8C26E28A9700B1A741 /* NotificationsCenterCellStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E2EA8826E28A9700B1A741 /* NotificationsCenterCellStyle.swift */; }; + 00E2EA8E26E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E2EA8D26E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift */; }; + 00E2EA8F26E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E2EA8D26E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift */; }; + 00E2EA9026E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E2EA8D26E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift */; }; + 00E2EA9126E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E2EA8D26E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift */; }; + 00E5B39F28EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E5B39E28EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift */; }; + 00E5B3A028EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E5B39E28EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift */; }; + 00E5B3A128EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E5B39E28EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift */; }; + 00E5B3A228EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E5B39E28EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift */; }; + 00E5B3A428EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E5B3A328EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift */; }; + 00E5B3A528EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E5B3A328EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift */; }; + 00E5B3A628EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E5B3A328EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift */; }; + 00E5B3A728EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E5B3A328EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift */; }; + 00E75B5D27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B5C27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift */; }; + 00E75B5E27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B5C27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift */; }; + 00E75B5F27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B5C27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift */; }; + 00E75B6027EB874A00A45B78 /* NotificationsCenterDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B5C27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift */; }; + 00E75B6227EB87DC00A45B78 /* NotificationsCenterDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6127EB87DC00A45B78 /* NotificationsCenterDetailView.swift */; }; + 00E75B6327EB87DC00A45B78 /* NotificationsCenterDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6127EB87DC00A45B78 /* NotificationsCenterDetailView.swift */; }; + 00E75B6427EB87DC00A45B78 /* NotificationsCenterDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6127EB87DC00A45B78 /* NotificationsCenterDetailView.swift */; }; + 00E75B6527EB87DC00A45B78 /* NotificationsCenterDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6127EB87DC00A45B78 /* NotificationsCenterDetailView.swift */; }; + 00E75B6727EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6627EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift */; }; + 00E75B6827EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6627EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift */; }; + 00E75B6927EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6627EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift */; }; + 00E75B6A27EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6627EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift */; }; + 00E75B6C27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6B27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift */; }; + 00E75B6D27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6B27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift */; }; + 00E75B6E27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6B27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift */; }; + 00E75B6F27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B6B27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift */; }; + 00E75B7127EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B7027EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift */; }; + 00E75B7227EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B7027EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift */; }; + 00E75B7327EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B7027EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift */; }; + 00E75B7427EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B7027EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift */; }; + 00E75B7627EB946D00A45B78 /* ReusableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B7527EB946D00A45B78 /* ReusableCell.swift */; }; + 00E75B7727EB946D00A45B78 /* ReusableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B7527EB946D00A45B78 /* ReusableCell.swift */; }; + 00E75B7827EB946D00A45B78 /* ReusableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B7527EB946D00A45B78 /* ReusableCell.swift */; }; + 00E75B7927EB946D00A45B78 /* ReusableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E75B7527EB946D00A45B78 /* ReusableCell.swift */; }; + 00EACEC628E39D470054DDB4 /* TalkPageEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EACEC528E39D470054DDB4 /* TalkPageEmptyView.swift */; }; + 00EACEC728E39D470054DDB4 /* TalkPageEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EACEC528E39D470054DDB4 /* TalkPageEmptyView.swift */; }; + 00EACEC828E39D470054DDB4 /* TalkPageEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EACEC528E39D470054DDB4 /* TalkPageEmptyView.swift */; }; + 00EACEC928E39D470054DDB4 /* TalkPageEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EACEC528E39D470054DDB4 /* TalkPageEmptyView.swift */; }; + 00EBB7C727D6878E002025AC /* BarButtonImageStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EBB7C627D6878E002025AC /* BarButtonImageStyle.swift */; }; + 00EBB7C827D6878E002025AC /* BarButtonImageStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EBB7C627D6878E002025AC /* BarButtonImageStyle.swift */; }; + 00EBB7C927D6878E002025AC /* BarButtonImageStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EBB7C627D6878E002025AC /* BarButtonImageStyle.swift */; }; + 00EBB7CA27D6878E002025AC /* BarButtonImageStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EBB7C627D6878E002025AC /* BarButtonImageStyle.swift */; }; + 00EBB7CC27D6A86A002025AC /* SettingsPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EBB7CB27D6A86A002025AC /* SettingsPresentationDelegate.swift */; }; + 00EBB7CD27D6A86A002025AC /* SettingsPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EBB7CB27D6A86A002025AC /* SettingsPresentationDelegate.swift */; }; + 00EBB7CE27D6A86A002025AC /* SettingsPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EBB7CB27D6A86A002025AC /* SettingsPresentationDelegate.swift */; }; + 00EBB7CF27D6A86A002025AC /* SettingsPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EBB7CB27D6A86A002025AC /* SettingsPresentationDelegate.swift */; }; + 00F5AED027C6C80C006390A8 /* PushNotificationsSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F5AECF27C6C80C006390A8 /* PushNotificationsSettingsViewController.swift */; }; + 00F5AED127C6C80C006390A8 /* PushNotificationsSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F5AECF27C6C80C006390A8 /* PushNotificationsSettingsViewController.swift */; }; + 00F5AED227C6C80C006390A8 /* PushNotificationsSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F5AECF27C6C80C006390A8 /* PushNotificationsSettingsViewController.swift */; }; + 00F5AED327C6C80C006390A8 /* PushNotificationsSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F5AECF27C6C80C006390A8 /* PushNotificationsSettingsViewController.swift */; }; + 00FCB2BE26D8398700F5A47A /* NotificationsCenterCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCB2BD26D8398700F5A47A /* NotificationsCenterCell.swift */; }; + 00FCB2BF26D8398700F5A47A /* NotificationsCenterCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCB2BD26D8398700F5A47A /* NotificationsCenterCell.swift */; }; + 00FCB2C026D8398700F5A47A /* NotificationsCenterCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCB2BD26D8398700F5A47A /* NotificationsCenterCell.swift */; }; + 00FCB2C126D8398700F5A47A /* NotificationsCenterCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCB2BD26D8398700F5A47A /* NotificationsCenterCell.swift */; }; + 00FCB2C326D839A500F5A47A /* NotificationsCenterCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCB2C226D839A500F5A47A /* NotificationsCenterCellViewModel.swift */; }; + 00FCB2C426D839A500F5A47A /* NotificationsCenterCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCB2C226D839A500F5A47A /* NotificationsCenterCellViewModel.swift */; }; + 00FCB2C526D839A500F5A47A /* NotificationsCenterCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCB2C226D839A500F5A47A /* NotificationsCenterCellViewModel.swift */; }; + 00FCB2C626D839A500F5A47A /* NotificationsCenterCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCB2C226D839A500F5A47A /* NotificationsCenterCellViewModel.swift */; }; + 00FCCBC5290082C200C9ECD2 /* TalkPageFindInPageState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBC4290082C200C9ECD2 /* TalkPageFindInPageState.swift */; }; + 00FCCBC6290082C200C9ECD2 /* TalkPageFindInPageState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBC4290082C200C9ECD2 /* TalkPageFindInPageState.swift */; }; + 00FCCBC7290082C200C9ECD2 /* TalkPageFindInPageState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBC4290082C200C9ECD2 /* TalkPageFindInPageState.swift */; }; + 00FCCBC8290082C200C9ECD2 /* TalkPageFindInPageState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBC4290082C200C9ECD2 /* TalkPageFindInPageState.swift */; }; + 00FCCBCA2900848300C9ECD2 /* TalkPageViewController+FindInPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBC92900848300C9ECD2 /* TalkPageViewController+FindInPage.swift */; }; + 00FCCBCB2900848300C9ECD2 /* TalkPageViewController+FindInPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBC92900848300C9ECD2 /* TalkPageViewController+FindInPage.swift */; }; + 00FCCBCC2900848300C9ECD2 /* TalkPageViewController+FindInPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBC92900848300C9ECD2 /* TalkPageViewController+FindInPage.swift */; }; + 00FCCBCD2900848300C9ECD2 /* TalkPageViewController+FindInPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBC92900848300C9ECD2 /* TalkPageViewController+FindInPage.swift */; }; + 00FCCBCF2900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBCE2900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift */; }; + 00FCCBD02900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBCE2900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift */; }; + 00FCCBD12900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBCE2900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift */; }; + 00FCCBD22900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FCCBCE2900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift */; }; + 041EFC371996A1F800B2CB28 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 041EFC361996A1F800B2CB28 /* MapKit.framework */; }; + 0E281A331DC263DE00FA1AB1 /* WMFLegacyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E281A321DC263DE00FA1AB1 /* WMFLegacyReference.swift */; }; + 0E36C2271AE0B59D00C58CFF /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D4991453181D51DE00E6073C /* Images.xcassets */; }; + 0E4A34721CBBFCD400A400F6 /* WMFImageGalleryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E4A34711CBBFCD400A400F6 /* WMFImageGalleryViewController.m */; }; + 0E4D071D1CC5526200AE968B /* WMFLanguageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E4D071C1CC5526200AE968B /* WMFLanguageCell.xib */; }; + 0E69CD5B1C8773410095918B /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E69CD5A1C8773410095918B /* Launch Screen.storyboard */; }; + 0E728D1A1DAEE2B50074EB4B /* WMFFeedDayResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E19B99D1DA7CAC200239F3A /* WMFFeedDayResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D1B1DAEE2B50074EB4B /* WMFFeedDayResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E19B99E1DA7CAC200239F3A /* WMFFeedDayResponse.m */; }; + 0E728D1C1DAEE2B50074EB4B /* WMFFeedTopReadResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E19B9A31DA7CE4400239F3A /* WMFFeedTopReadResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D1D1DAEE2B50074EB4B /* WMFFeedTopReadResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E19B9A41DA7CE4400239F3A /* WMFFeedTopReadResponse.m */; }; + 0E728D1E1DAEE2B50074EB4B /* WMFFeedArticlePreview.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E19B9A01DA7CB8200239F3A /* WMFFeedArticlePreview.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D1F1DAEE2B50074EB4B /* WMFFeedArticlePreview.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E19B9A11DA7CB8200239F3A /* WMFFeedArticlePreview.m */; }; + 0E728D201DAEE2B50074EB4B /* WMFFeedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E19B9A61DA7D52A00239F3A /* WMFFeedImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D211DAEE2B50074EB4B /* WMFFeedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E19B9A71DA7D52A00239F3A /* WMFFeedImage.m */; }; + 0E728D221DAEE2B50074EB4B /* WMFFeedNewsStory.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E19B9A91DA7D77600239F3A /* WMFFeedNewsStory.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D231DAEE2B50074EB4B /* WMFFeedNewsStory.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E19B9AA1DA7D77600239F3A /* WMFFeedNewsStory.m */; }; + 0E728D241DAEE2B50074EB4B /* WMFFeedContentFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E19B9AD1DA7DC9D00239F3A /* WMFFeedContentFetcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D251DAEE2B50074EB4B /* WMFFeedContentFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E19B9AE1DA7DC9D00239F3A /* WMFFeedContentFetcher.m */; }; + 0E728D281DAEE8FF0074EB4B /* WMFContentSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E3C5D371D664BFC00C95BA1 /* WMFContentSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D2B1DAEE8FF0074EB4B /* WMFRelatedPagesContentSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E3C5D381D664CBF00C95BA1 /* WMFRelatedPagesContentSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D2C1DAEE8FF0074EB4B /* WMFRelatedPagesContentSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3C5D391D664CBF00C95BA1 /* WMFRelatedPagesContentSource.m */; }; + 0E728D2F1DAEE8FF0074EB4B /* WMFNearbyContentSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E9880601DA2C7CF0058D7F2 /* WMFNearbyContentSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D301DAEE8FF0074EB4B /* WMFNearbyContentSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E9880611DA2C7CF0058D7F2 /* WMFNearbyContentSource.m */; }; + 0E728D311DAEE8FF0074EB4B /* WMFContinueReadingContentSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E9880631DA303070058D7F2 /* WMFContinueReadingContentSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D321DAEE8FF0074EB4B /* WMFContinueReadingContentSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E9880641DA303070058D7F2 /* WMFContinueReadingContentSource.m */; }; + 0E728D331DAEE8FF0074EB4B /* WMFFeedContentSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E19B9B01DA80C4900239F3A /* WMFFeedContentSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D341DAEE8FF0074EB4B /* WMFFeedContentSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E19B9B11DA80C4900239F3A /* WMFFeedContentSource.m */; }; + 0E728D351DAEE8FF0074EB4B /* WMFRandomContentSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E19B9B41DAC574E00239F3A /* WMFRandomContentSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D361DAEE8FF0074EB4B /* WMFRandomContentSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E19B9B51DAC574E00239F3A /* WMFRandomContentSource.m */; }; + 0E728D371DAEEAD60074EB4B /* MWKLocationSearchResult.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807C81C0CF04A0065EBC0 /* MWKLocationSearchResult.m */; }; + 0E728D381DAEEAD60074EB4B /* WMFLocationSearchResults.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802FF1C0CD5000065EBC0 /* WMFLocationSearchResults.m */; }; + 0E728D391DAEEAD60074EB4B /* WMFLocationSearchFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802FD1C0CD5000065EBC0 /* WMFLocationSearchFetcher.m */; }; + 0E728D3A1DAEEADB0074EB4B /* MWKLocationSearchResult.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807C71C0CF04A0065EBC0 /* MWKLocationSearchResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D3B1DAEEADB0074EB4B /* WMFLocationSearchResults.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E802FE1C0CD5000065EBC0 /* WMFLocationSearchResults.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D3C1DAEEADB0074EB4B /* WMFLocationSearchFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E802FC1C0CD5000065EBC0 /* WMFLocationSearchFetcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D451DAEEE880074EB4B /* CLLocation+WMFBearing.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804721C0CE0B40065EBC0 /* CLLocation+WMFBearing.m */; }; + 0E728D461DAEEE880074EB4B /* NSString+WMFDistance.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804A91C0CE0B40065EBC0 /* NSString+WMFDistance.m */; }; + 0E728D471DAEEE880074EB4B /* CLLocation+WMFComparison.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCFC44B1C84BAE0009D3613 /* CLLocation+WMFComparison.m */; }; + 0E728D4A1DAEEE910074EB4B /* CLLocation+WMFBearing.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804711C0CE0B40065EBC0 /* CLLocation+WMFBearing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D4B1DAEEE910074EB4B /* NSString+WMFDistance.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804A81C0CE0B40065EBC0 /* NSString+WMFDistance.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E728D4C1DAEEE910074EB4B /* CLLocation+WMFComparison.h in Headers */ = {isa = PBXBuildFile; fileRef = BCCFC44A1C84BAE0009D3613 /* CLLocation+WMFComparison.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E8380651D64989F0076EDE4 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E8380641D64989F0076EDE4 /* NotificationCenter.framework */; }; + 0E83806C1D64989F0076EDE4 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E83806A1D64989F0076EDE4 /* MainInterface.storyboard */; }; + 0E8380701D64989F0076EDE4 /* ContinueReadingWidget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0E8380631D64989F0076EDE4 /* ContinueReadingWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 0E8768361DDE002C00B8CACD /* WMFAnnouncementsContentSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E8768341DDE002C00B8CACD /* WMFAnnouncementsContentSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E8768371DDE002C00B8CACD /* WMFAnnouncementsContentSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E8768351DDE002C00B8CACD /* WMFAnnouncementsContentSource.m */; }; + 0E87683A1DDE00D600B8CACD /* WMFAnnouncementsFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E8768381DDE00D600B8CACD /* WMFAnnouncementsFetcher.h */; }; + 0E87683B1DDE00D600B8CACD /* WMFAnnouncementsFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E8768391DDE00D600B8CACD /* WMFAnnouncementsFetcher.m */; }; + 0E87683F1DDE012300B8CACD /* WMFAnnouncement.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E87683D1DDE012300B8CACD /* WMFAnnouncement.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E8768401DDE012300B8CACD /* WMFAnnouncement.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E87683E1DDE012300B8CACD /* WMFAnnouncement.m */; }; + 0E8DC0951C74F0D700622CBD /* WMFDailyStatsLoggingFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DC0941C74F0D700622CBD /* WMFDailyStatsLoggingFunnel.m */; }; + 0E9B9E321CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E9B9E2F1CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.xib */; }; + 0E9B9E331CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E9B9E301CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.m */; }; + 0EBCA7481C176389004F1FD9 /* WMFAlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBCA7471C176389004F1FD9 /* WMFAlertManager.swift */; }; + 0EC044791C7917860033D773 /* WMFArticleTextActivitySource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EC044781C7917860033D773 /* WMFArticleTextActivitySource.m */; }; + 0EC0447B1C796FEF0033D773 /* WMFImageTextActivitySource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC0447A1C796FEF0033D773 /* WMFImageTextActivitySource.swift */; }; + 0EC0447F1C797DC20033D773 /* WMFImageURLActivitySource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC0447E1C797DC20033D773 /* WMFImageURLActivitySource.swift */; }; + 0ED2E9FA1CB2B3B300D1C844 /* UIVIewController+WMFCommonRotationSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED2E9F91CB2B3B300D1C844 /* UIVIewController+WMFCommonRotationSupport.swift */; }; + 0EE2438D1DC9889B00066CBD /* WMFTableHeaderFooterLabelView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EE2438C1DC9889B00066CBD /* WMFTableHeaderFooterLabelView.m */; }; + 0EE2438F1DC988AA00066CBD /* WMFTableHeaderFooterLabelView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0EE2438E1DC988AA00066CBD /* WMFTableHeaderFooterLabelView.xib */; }; + 0EE489031D4ADCA00088505C /* UIViewController+WMFScrollToTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE489021D4ADCA00088505C /* UIViewController+WMFScrollToTop.swift */; }; + 0EF2249A1CC5536200FDF78E /* WMFLanguageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF224991CC5536200FDF78E /* WMFLanguageCell.m */; }; + 0EF5BB6D1C110C2100DE75E1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF5BB6C1C110C2100DE75E1 /* AppDelegate.m */; }; + 0EF8634E1C19E02700006D2D /* WMFEmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0EF8634D1C19E02700006D2D /* WMFEmptyView.xib */; }; + 0EF863511C19E4F100006D2D /* WMFEmptyView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF863501C19E4F100006D2D /* WMFEmptyView.m */; }; + 19A175F095F5197BA20EA8BA /* NSUserActivity+WMFExtensionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 19A172FA6AE61E76FCEF4259 /* NSUserActivity+WMFExtensionsTest.m */; }; + 41CCB67421CC1F9700206B47 /* SavedArticlesCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41CCB67321CC1F9700206B47 /* SavedArticlesCollectionViewController.swift */; }; + 41CCB67521CC1F9700206B47 /* SavedArticlesCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41CCB67321CC1F9700206B47 /* SavedArticlesCollectionViewController.swift */; }; + 41CCB67621CC1F9700206B47 /* SavedArticlesCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41CCB67321CC1F9700206B47 /* SavedArticlesCollectionViewController.swift */; }; + 41CCB67721CC1F9700206B47 /* SavedArticlesCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41CCB67321CC1F9700206B47 /* SavedArticlesCollectionViewController.swift */; }; + 41FCAA3621C844CB001D8411 /* ReadingListEntryCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41FCAA3521C844CB001D8411 /* ReadingListEntryCollectionViewController.swift */; }; + 41FCAA3721C844CB001D8411 /* ReadingListEntryCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41FCAA3521C844CB001D8411 /* ReadingListEntryCollectionViewController.swift */; }; + 41FCAA3821C844CB001D8411 /* ReadingListEntryCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41FCAA3521C844CB001D8411 /* ReadingListEntryCollectionViewController.swift */; }; + 41FCAA3921C844CB001D8411 /* ReadingListEntryCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41FCAA3521C844CB001D8411 /* ReadingListEntryCollectionViewController.swift */; }; + 46EB334829E1D204001D5EAF /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 46EB334729E1D204001D5EAF /* Shared */; }; + 533AB8AE259792A9003A43D9 /* wikipedia-language-variants.json in Resources */ = {isa = PBXBuildFile; fileRef = 533AB8AD259792A9003A43D9 /* wikipedia-language-variants.json */; }; + 535F16D625CE11A300875AAD /* MWKDataStore+LanguageVariantMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535F16D525CE11A300875AAD /* MWKDataStore+LanguageVariantMigration.swift */; }; + 53A575FA2602C845009835E6 /* WMFAppViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A575F92602C845009835E6 /* WMFAppViewController+Extensions.swift */; }; + 53A575FB2602C845009835E6 /* WMFAppViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A575F92602C845009835E6 /* WMFAppViewController+Extensions.swift */; }; + 53A575FC2602C845009835E6 /* WMFAppViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A575F92602C845009835E6 /* WMFAppViewController+Extensions.swift */; }; + 53A575FD2602C845009835E6 /* WMFAppViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A575F92602C845009835E6 /* WMFAppViewController+Extensions.swift */; }; + 67059DB52260D034009811AA /* SchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67059DB42260D034009811AA /* SchemeHandler.swift */; }; + 67059DB62260D619009811AA /* SchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67059DB42260D034009811AA /* SchemeHandler.swift */; }; + 67059DB72260D61A009811AA /* SchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67059DB42260D034009811AA /* SchemeHandler.swift */; }; + 67059DB82260D61A009811AA /* SchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67059DB42260D034009811AA /* SchemeHandler.swift */; }; + 6706A21722925FD2004774E2 /* InfoBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6706A21622925FD2004774E2 /* InfoBannerView.swift */; }; + 6706A21922927D63004774E2 /* TalkPageHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6706A21822927D63004774E2 /* TalkPageHintViewController.swift */; }; + 6707C032237DBCEA0017E7B6 /* DiffRevisionTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6707C031237DBCEA0017E7B6 /* DiffRevisionTransition.swift */; }; + 6707C033237DBCEE0017E7B6 /* DiffRevisionTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6707C031237DBCEA0017E7B6 /* DiffRevisionTransition.swift */; }; + 6707C034237DBCEE0017E7B6 /* DiffRevisionTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6707C031237DBCEA0017E7B6 /* DiffRevisionTransition.swift */; }; + 6707C035237DBCEE0017E7B6 /* DiffRevisionTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6707C031237DBCEA0017E7B6 /* DiffRevisionTransition.swift */; }; + 6707C038237F0A6E0017E7B6 /* UIFont+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6707C037237F0A6E0017E7B6 /* UIFont+Extensions.swift */; }; + 6707C039237F0A6E0017E7B6 /* UIFont+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6707C037237F0A6E0017E7B6 /* UIFont+Extensions.swift */; }; + 6707C03A237F0A6E0017E7B6 /* UIFont+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6707C037237F0A6E0017E7B6 /* UIFont+Extensions.swift */; }; + 6707C03B237F0A6E0017E7B6 /* UIFont+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6707C037237F0A6E0017E7B6 /* UIFont+Extensions.swift */; }; + 670AF18D26BDE645005F76D0 /* OldTalkPageHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 672B127722A450F000CC85A5 /* OldTalkPageHeaderView.xib */; }; + 670AF18E26BDE646005F76D0 /* OldTalkPageHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 672B127722A450F000CC85A5 /* OldTalkPageHeaderView.xib */; }; + 670AF18F26BDE647005F76D0 /* OldTalkPageHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 672B127722A450F000CC85A5 /* OldTalkPageHeaderView.xib */; }; + 670AF19026BDE6E7005F76D0 /* EmptyViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 671F5E0A236B8CAF00111116 /* EmptyViewController.xib */; }; + 670AF19126BDE6E8005F76D0 /* EmptyViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 671F5E0A236B8CAF00111116 /* EmptyViewController.xib */; }; + 670AF19226BDE6E9005F76D0 /* EmptyViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 671F5E0A236B8CAF00111116 /* EmptyViewController.xib */; }; + 670AF1CE26CA188B005F76D0 /* RemoteNotificationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 670AF1CD26CA188B005F76D0 /* RemoteNotificationLinks.swift */; }; + 670AF1CF26CD74A6005F76D0 /* EchoSubscriptionFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 670AF19A26C1CA38005F76D0 /* EchoSubscriptionFetcher.swift */; }; + 670F765F22B0C10600D87545 /* FakeProgressLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 670F765E22B0C10600D87545 /* FakeProgressLoading.swift */; }; + 670F766022B0C48E00D87545 /* FakeProgressLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 670F765E22B0C10600D87545 /* FakeProgressLoading.swift */; }; + 670F766122B0C48F00D87545 /* FakeProgressLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 670F765E22B0C10600D87545 /* FakeProgressLoading.swift */; }; + 670F766222B0C49000D87545 /* FakeProgressLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 670F765E22B0C10600D87545 /* FakeProgressLoading.swift */; }; + 67112E3D275E603B007A9850 /* NotificationsCenterInboxViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67112E3C275E603B007A9850 /* NotificationsCenterInboxViewModel.swift */; }; + 67112E3E275E603B007A9850 /* NotificationsCenterInboxViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67112E3C275E603B007A9850 /* NotificationsCenterInboxViewModel.swift */; }; + 67112E3F275E603B007A9850 /* NotificationsCenterInboxViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67112E3C275E603B007A9850 /* NotificationsCenterInboxViewModel.swift */; }; + 67112E40275E603B007A9850 /* NotificationsCenterInboxViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67112E3C275E603B007A9850 /* NotificationsCenterInboxViewModel.swift */; }; + 67134A1728A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67134A1628A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift */; }; + 67134A1828A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67134A1628A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift */; }; + 67134A1928A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67134A1628A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift */; }; + 67134A1A28A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67134A1628A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift */; }; + 6713519D277285B7006C07D9 /* RemoteNotificationsRefreshDeadlineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6713519C277285B7006C07D9 /* RemoteNotificationsRefreshDeadlineController.swift */; }; + 67146032243B885E008CE885 /* SurveyAnnouncementsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67146031243B885E008CE885 /* SurveyAnnouncementsController.swift */; }; + 67146034243B8B4F008CE885 /* AnnouncementType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67146033243B8B4F008CE885 /* AnnouncementType.swift */; }; + 67146036243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67146035243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift */; }; + 67146037243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67146035243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift */; }; + 67146038243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67146035243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift */; }; + 67146039243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67146035243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift */; }; + 6714D6CB245A2B9700CE5A4A /* ArticleCacheReadingManualTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6714D6CA245A2B9700CE5A4A /* ArticleCacheReadingManualTests.swift */; }; + 6714D6CD245A2C1D00CE5A4A /* ArticleTestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6714D6CC245A2C1D00CE5A4A /* ArticleTestHelpers.swift */; }; + 671AC2562226FB9B005B37F8 /* ReadingThemesControlsProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671AC2552226FB9B005B37F8 /* ReadingThemesControlsProtocols.swift */; }; + 671DF9C125F2AE4E0011799E /* ArticleDescriptionControlling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9BE25F2AE4E0011799E /* ArticleDescriptionControlling.swift */; }; + 671DF9C225F2AE4E0011799E /* ArticleDescriptionControlling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9BE25F2AE4E0011799E /* ArticleDescriptionControlling.swift */; }; + 671DF9C325F2AE4E0011799E /* ArticleDescriptionControlling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9BE25F2AE4E0011799E /* ArticleDescriptionControlling.swift */; }; + 671DF9C425F2AE4E0011799E /* ArticleDescriptionControlling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9BE25F2AE4E0011799E /* ArticleDescriptionControlling.swift */; }; + 671DF9C525F2AE4E0011799E /* ShortDescriptionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9BF25F2AE4E0011799E /* ShortDescriptionController.swift */; }; + 671DF9C625F2AE4F0011799E /* ShortDescriptionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9BF25F2AE4E0011799E /* ShortDescriptionController.swift */; }; + 671DF9C725F2AE4F0011799E /* ShortDescriptionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9BF25F2AE4E0011799E /* ShortDescriptionController.swift */; }; + 671DF9C825F2AE4F0011799E /* ShortDescriptionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9BF25F2AE4E0011799E /* ShortDescriptionController.swift */; }; + 671DF9C925F2AE4F0011799E /* WikidataDescriptionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9C025F2AE4E0011799E /* WikidataDescriptionController.swift */; }; + 671DF9CA25F2AE4F0011799E /* WikidataDescriptionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9C025F2AE4E0011799E /* WikidataDescriptionController.swift */; }; + 671DF9CB25F2AE4F0011799E /* WikidataDescriptionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9C025F2AE4E0011799E /* WikidataDescriptionController.swift */; }; + 671DF9CC25F2AE4F0011799E /* WikidataDescriptionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9C025F2AE4E0011799E /* WikidataDescriptionController.swift */; }; + 671DF9D825F2B59A0011799E /* ShortDescriptionControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671DF9D725F2B59A0011799E /* ShortDescriptionControllerTests.swift */; }; + 671F5E002367EDC600111116 /* GlobalUserInfoFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A5E656236775C3007749FB /* GlobalUserInfoFetcher.swift */; }; + 671F5E012367EDC600111116 /* GlobalUserInfoFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A5E656236775C3007749FB /* GlobalUserInfoFetcher.swift */; }; + 671F5E022367EDC600111116 /* GlobalUserInfoFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A5E656236775C3007749FB /* GlobalUserInfoFetcher.swift */; }; + 671F5E0B236B8CAF00111116 /* EmptyViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 671F5E0A236B8CAF00111116 /* EmptyViewController.xib */; }; + 672034E327A2531F007DC24F /* RemoteNotificationsReauthenticateOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672034E227A2531F007DC24F /* RemoteNotificationsReauthenticateOperation.swift */; }; + 672034E527A2600C007DC24F /* RemoteNotificationsProjectOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672034E427A2600C007DC24F /* RemoteNotificationsProjectOperation.swift */; }; + 672285722540B56D0038E332 /* ReferenceBackLinksViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B85524B3B384005C2471 /* ReferenceBackLinksViewControllerDelegate.swift */; }; + 672285732540B56D0038E332 /* ReferenceBackLinksViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B85524B3B384005C2471 /* ReferenceBackLinksViewControllerDelegate.swift */; }; + 672286282540DB330038E332 /* AppTabBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE891452445150B0058B642 /* AppTabBarDelegate.swift */; }; + 6724288E23612AF000490629 /* DiffListContextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67CEF25E234FCA8100D5CA6C /* DiffListContextCell.xib */; }; + 6724288F23612AF000490629 /* DiffListContextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67CEF25E234FCA8100D5CA6C /* DiffListContextCell.xib */; }; + 6724289023612AF100490629 /* DiffListContextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67CEF25E234FCA8100D5CA6C /* DiffListContextCell.xib */; }; + 6724289223612AF500490629 /* DiffListUneditedCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67CEF2602350C29D00D5CA6C /* DiffListUneditedCell.xib */; }; + 6724289323612AF600490629 /* DiffListUneditedCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67CEF2602350C29D00D5CA6C /* DiffListUneditedCell.xib */; }; + 6724289423612AF700490629 /* DiffListUneditedCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67CEF2602350C29D00D5CA6C /* DiffListUneditedCell.xib */; }; + 672428972362113400490629 /* DiffFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672428962362113400490629 /* DiffFetcher.swift */; }; + 672428982362113900490629 /* DiffFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672428962362113400490629 /* DiffFetcher.swift */; }; + 672428992362113900490629 /* DiffFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672428962362113400490629 /* DiffFetcher.swift */; }; + 6724289A2362113A00490629 /* DiffFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672428962362113400490629 /* DiffFetcher.swift */; }; + 67282FBD24855B7B00B73E20 /* ArticleContextMenuPresenting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67282FBC24855B7B00B73E20 /* ArticleContextMenuPresenting.swift */; }; + 67282FBE24855B7B00B73E20 /* ArticleContextMenuPresenting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67282FBC24855B7B00B73E20 /* ArticleContextMenuPresenting.swift */; }; + 67282FBF24855B7B00B73E20 /* ArticleContextMenuPresenting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67282FBC24855B7B00B73E20 /* ArticleContextMenuPresenting.swift */; }; + 67282FC024855B7B00B73E20 /* ArticleContextMenuPresenting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67282FBC24855B7B00B73E20 /* ArticleContextMenuPresenting.swift */; }; + 672B127822A450F000CC85A5 /* OldTalkPageHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 672B127722A450F000CC85A5 /* OldTalkPageHeaderView.xib */; }; + 672C35EB22D8E7CA007B8D46 /* EmptyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672C35EA22D8E7C9007B8D46 /* EmptyViewController.swift */; }; + 672C35EC22D8E7D2007B8D46 /* EmptyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672C35EA22D8E7C9007B8D46 /* EmptyViewController.swift */; }; + 672C35ED22D8E7D2007B8D46 /* EmptyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672C35EA22D8E7C9007B8D46 /* EmptyViewController.swift */; }; + 672C35EE22D8E7D2007B8D46 /* EmptyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672C35EA22D8E7C9007B8D46 /* EmptyViewController.swift */; }; + 672D69A4273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672D69A3273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift */; }; + 672D69A5273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672D69A3273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift */; }; + 672D69A6273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672D69A3273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift */; }; + 672D69A7273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672D69A3273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift */; }; + 672D69A9273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672D69A8273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift */; }; + 672D69AA273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672D69A8273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift */; }; + 672D69AB273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672D69A8273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift */; }; + 672D69AC273ACAA200B123B3 /* UITabBarAppearance+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672D69A8273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift */; }; + 672F0558222F24FB00FB1084 /* IconBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672F0557222F24FB00FB1084 /* IconBarButtonItem.swift */; }; + 6730FD0E28998EFD000E5F40 /* TalkPageReplyComposeContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6730FD0D28998EFD000E5F40 /* TalkPageReplyComposeContentView.swift */; }; + 6730FD0F28998EFD000E5F40 /* TalkPageReplyComposeContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6730FD0D28998EFD000E5F40 /* TalkPageReplyComposeContentView.swift */; }; + 6730FD1028998EFD000E5F40 /* TalkPageReplyComposeContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6730FD0D28998EFD000E5F40 /* TalkPageReplyComposeContentView.swift */; }; + 6730FD1128998EFD000E5F40 /* TalkPageReplyComposeContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6730FD0D28998EFD000E5F40 /* TalkPageReplyComposeContentView.swift */; }; + 6734115422735788005B31DA /* OldTalkPageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0A7226A654D00537BC9 /* OldTalkPageFetcher.swift */; }; + 6734115522735789005B31DA /* OldTalkPageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0A7226A654D00537BC9 /* OldTalkPageFetcher.swift */; }; + 673411562273578A005B31DA /* OldTalkPageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0A7226A654D00537BC9 /* OldTalkPageFetcher.swift */; }; + 673411572273578A005B31DA /* OldTalkPageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0A7226A654D00537BC9 /* OldTalkPageFetcher.swift */; }; + 6734115922735832005B31DA /* OldTalkPagesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0A4226A64CB00537BC9 /* OldTalkPagesController.swift */; }; + 6734115A22735832005B31DA /* OldTalkPagesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0A4226A64CB00537BC9 /* OldTalkPagesController.swift */; }; + 6734115B22735833005B31DA /* OldTalkPagesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0A4226A64CB00537BC9 /* OldTalkPagesController.swift */; }; + 6734115C22735833005B31DA /* OldTalkPagesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0A4226A64CB00537BC9 /* OldTalkPagesController.swift */; }; + 6734116422739CA2005B31DA /* TalkPageLocalHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734116322739CA2005B31DA /* TalkPageLocalHandler.swift */; }; + 6734116522739CCB005B31DA /* TalkPageLocalHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734116322739CA2005B31DA /* TalkPageLocalHandler.swift */; }; + 6734116622739CCB005B31DA /* TalkPageLocalHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734116322739CA2005B31DA /* TalkPageLocalHandler.swift */; }; + 6734116722739CCC005B31DA /* TalkPageLocalHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734116322739CA2005B31DA /* TalkPageLocalHandler.swift */; }; + 6734117022773122005B31DA /* OldTalkPageReplyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734116F22773122005B31DA /* OldTalkPageReplyCell.swift */; }; + 6734EE7322976AE300F00B05 /* InfoBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6706A21622925FD2004774E2 /* InfoBannerView.swift */; }; + 6734EE7422976AE300F00B05 /* InfoBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6706A21622925FD2004774E2 /* InfoBannerView.swift */; }; + 6734EE7522976AE400F00B05 /* InfoBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6706A21622925FD2004774E2 /* InfoBannerView.swift */; }; + 6734EE7722976AED00F00B05 /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734F051227B634900BDDB94 /* ActionButton.swift */; }; + 6734EE7822976AED00F00B05 /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734F051227B634900BDDB94 /* ActionButton.swift */; }; + 6734EE7922976AED00F00B05 /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734F051227B634900BDDB94 /* ActionButton.swift */; }; + 6734EE7B22976BA300F00B05 /* TalkPageHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6706A21822927D63004774E2 /* TalkPageHintViewController.swift */; }; + 6734EE7C22976BA300F00B05 /* TalkPageHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6706A21822927D63004774E2 /* TalkPageHintViewController.swift */; }; + 6734EE7D22976BA300F00B05 /* TalkPageHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6706A21822927D63004774E2 /* TalkPageHintViewController.swift */; }; + 6734F052227B634900BDDB94 /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734F051227B634900BDDB94 /* ActionButton.swift */; }; + 673612F224FD7210002A1989 /* ArticleAsLivingDocViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673612F124FD7210002A1989 /* ArticleAsLivingDocViewModelTests.swift */; }; + 6739A182273061220063E0E0 /* RemoteNotificationsMarkAllAsReadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6739A181273061220063E0E0 /* RemoteNotificationsMarkAllAsReadOperation.swift */; }; + 673FC3D0273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673FC3CF273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift */; }; + 673FC3D1273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673FC3CF273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift */; }; + 673FC3D2273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673FC3CF273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift */; }; + 673FC3D3273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673FC3CF273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift */; }; + 6741245027E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6741244F27E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift */; }; + 6741245127E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6741244F27E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift */; }; + 6741245227E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6741244F27E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift */; }; + 6741245327E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6741244F27E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift */; }; + 6747117E250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747117D250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift */; }; + 6747117F250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747117D250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift */; }; + 67471180250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747117D250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift */; }; + 67471181250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747117D250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift */; }; + 674711832507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674711822507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift */; }; + 674711842507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674711822507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift */; }; + 674711852507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674711822507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift */; }; + 674711862507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674711822507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift */; }; + 6747118825072D1500287951 /* IconTitleBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747118725072D1500287951 /* IconTitleBadge.swift */; }; + 6747118925072D1500287951 /* IconTitleBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747118725072D1500287951 /* IconTitleBadge.swift */; }; + 6747118A25072D1500287951 /* IconTitleBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747118725072D1500287951 /* IconTitleBadge.swift */; }; + 6747118B25072D1500287951 /* IconTitleBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6747118725072D1500287951 /* IconTitleBadge.swift */; }; + 674E8AB92382DEFF0053D206 /* DiffTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674E8AB82382DEFF0053D206 /* DiffTransformer.swift */; }; + 674E8ABA2382DF020053D206 /* DiffTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674E8AB82382DEFF0053D206 /* DiffTransformer.swift */; }; + 674E8ABB2382DF030053D206 /* DiffTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674E8AB82382DEFF0053D206 /* DiffTransformer.swift */; }; + 674E8ABC2382DF030053D206 /* DiffTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674E8AB82382DEFF0053D206 /* DiffTransformer.swift */; }; + 675175DC276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675175DB276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift */; }; + 675175DD276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675175DB276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift */; }; + 675175DE276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675175DB276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift */; }; + 675175DF276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675175DB276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift */; }; + 67540CA924D221E3008B2894 /* LocationManagerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67540CA824D221E3008B2894 /* LocationManagerFactory.swift */; }; + 6754E44922773587005EEAD1 /* OldTalkPageReplyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734116F22773122005B31DA /* OldTalkPageReplyCell.swift */; }; + 6754E44A22773587005EEAD1 /* OldTalkPageReplyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734116F22773122005B31DA /* OldTalkPageReplyCell.swift */; }; + 6754E44B22773588005EEAD1 /* OldTalkPageReplyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734116F22773122005B31DA /* OldTalkPageReplyCell.swift */; }; + 675A7CFE227A3F7C0034D9D9 /* OldTalkPageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675A7CFD227A3F7C0034D9D9 /* OldTalkPageHeaderView.swift */; }; + 676070A2227CE60800A81F09 /* TalkPageReplyFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676070A1227CE60800A81F09 /* TalkPageReplyFooterView.swift */; }; + 676070A42280987C00A81F09 /* TalkPageTopicNewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 676070A32280987C00A81F09 /* TalkPageTopicNewViewController.xib */; }; + 676070A52280D72400A81F09 /* TalkPageTopicNewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 676070A32280987C00A81F09 /* TalkPageTopicNewViewController.xib */; }; + 676070A62280D72400A81F09 /* TalkPageTopicNewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 676070A32280987C00A81F09 /* TalkPageTopicNewViewController.xib */; }; + 676070A72280D72500A81F09 /* TalkPageTopicNewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 676070A32280987C00A81F09 /* TalkPageTopicNewViewController.xib */; }; + 6761AEDA2704BA3800E47BAD /* RemoteNotification+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AED82704BA3800E47BAD /* RemoteNotification+CoreDataClass.swift */; }; + 6761AEDF2704CF0000E47BAD /* WikimediaProject+RemoteNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEDE2704CF0000E47BAD /* WikimediaProject+RemoteNotifications.swift */; }; + 6761AEE12704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEE02704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift */; }; + 6761AEE22704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEE02704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift */; }; + 6761AEE32704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEE02704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift */; }; + 6761AEE42704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEE02704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift */; }; + 6761AEE62704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEE52704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift */; }; + 6761AEE72704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEE52704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift */; }; + 6761AEE82704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEE52704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift */; }; + 6761AEE92704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEE52704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift */; }; + 6761AEEB270613B400E47BAD /* SharedContainerCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEEA270613B400E47BAD /* SharedContainerCache.swift */; }; + 6761AEED2706247800E47BAD /* PushNotificationsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEEC2706247800E47BAD /* PushNotificationsSettings.swift */; }; + 6761AEEF2706249300E47BAD /* PushNotificationsCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEEE2706249300E47BAD /* PushNotificationsCache.swift */; }; + 6761AEF327065DE400E47BAD /* WMFNotificationsController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEF227065DE400E47BAD /* WMFNotificationsController+Extensions.swift */; }; + 6761AEF52707BE4200E47BAD /* RemoteNotificationsRefreshOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEF42707BE4200E47BAD /* RemoteNotificationsRefreshOperation.swift */; }; + 6761AEF72707E34F00E47BAD /* NotificationsCenterModelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEF62707E34F00E47BAD /* NotificationsCenterModelController.swift */; }; + 6761AEF82707E34F00E47BAD /* NotificationsCenterModelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEF62707E34F00E47BAD /* NotificationsCenterModelController.swift */; }; + 6761AEF92707E34F00E47BAD /* NotificationsCenterModelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEF62707E34F00E47BAD /* NotificationsCenterModelController.swift */; }; + 6761AEFA2707E34F00E47BAD /* NotificationsCenterModelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6761AEF62707E34F00E47BAD /* NotificationsCenterModelController.swift */; }; + 676C864B26D40AEB00A704C1 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 676C864426D40AEA00A704C1 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 676C867026D416FB00A704C1 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 676C864426D40AEA00A704C1 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 676C867326D4170100A704C1 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 676C864426D40AEA00A704C1 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 676C868126D4545300A704C1 /* WMF.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; }; + 676C869326D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676C869226D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift */; }; + 676C869426D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676C869226D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift */; }; + 676C869526D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676C869226D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift */; }; + 676C869626D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676C869226D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift */; }; + 676E813329380D8A00F15258 /* TalkPagesFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676E813229380D8A00F15258 /* TalkPagesFunnel.swift */; }; + 676E813429380D8A00F15258 /* TalkPagesFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676E813229380D8A00F15258 /* TalkPagesFunnel.swift */; }; + 676E813529380D8A00F15258 /* TalkPagesFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676E813229380D8A00F15258 /* TalkPagesFunnel.swift */; }; + 676E813629380D8A00F15258 /* TalkPagesFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676E813229380D8A00F15258 /* TalkPagesFunnel.swift */; }; + 676F39282745FB2000F4D33D /* NotificationsCenterFiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676F39272745FB1F00F4D33D /* NotificationsCenterFiltersViewModel.swift */; }; + 676F39292745FB2000F4D33D /* NotificationsCenterFiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676F39272745FB1F00F4D33D /* NotificationsCenterFiltersViewModel.swift */; }; + 676F392A2745FB2000F4D33D /* NotificationsCenterFiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676F39272745FB1F00F4D33D /* NotificationsCenterFiltersViewModel.swift */; }; + 676F392B2745FB2000F4D33D /* NotificationsCenterFiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676F39272745FB1F00F4D33D /* NotificationsCenterFiltersViewModel.swift */; }; + 6771298F24FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771298E24FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift */; }; + 6771299024FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771298E24FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift */; }; + 6771299124FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771298E24FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift */; }; + 6771299224FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771298E24FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift */; }; + 6771299424FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771299324FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift */; }; + 6771299524FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771299324FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift */; }; + 6771299624FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771299324FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift */; }; + 6771299724FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771299324FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift */; }; + 6771299D24FF8CC000E89CA5 /* ArticleAsLivingDocViewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798036524F94D0300D765AA /* ArticleAsLivingDocViewModels.swift */; }; + 677129A024FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771299F24FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift */; }; + 677129A124FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771299F24FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift */; }; + 677129A224FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771299F24FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift */; }; + 677129A324FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6771299F24FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift */; }; + 6771C9542509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6771C9532509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib */; }; + 6771C9552509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6771C9532509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib */; }; + 6771C9562509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6771C9532509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib */; }; + 6771C9572509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6771C9532509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib */; }; + 6773B1FE240F02E40022A70E /* PermanentlyPersistableURLCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6773B1FD240F02E40022A70E /* PermanentlyPersistableURLCache.swift */; }; + 6773B2022411D8600022A70E /* ArticleCacheDBWriter+SyncResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6773B2012411D8600022A70E /* ArticleCacheDBWriter+SyncResources.swift */; }; + 6773B2042411DCF50022A70E /* ArticleCacheResourceDBWriting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6773B2032411DCF50022A70E /* ArticleCacheResourceDBWriting.swift */; }; + 6779618D29245BF300C2A65F /* PageIDToURLFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6779618C29245BF300C2A65F /* PageIDToURLFetcher.swift */; }; + 6779618F29246BC900C2A65F /* NSUserActivity+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6779618E29246BC900C2A65F /* NSUserActivity+Extensions.swift */; }; + 6779D45123F60903002840CA /* CacheFileWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6779D45023F60903002840CA /* CacheFileWriter.swift */; }; + 6779D45323F6EC2D002840CA /* CacheFetching.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6779D45223F6EC2D002840CA /* CacheFetching.swift */; }; + 6779D45924007AF0002840CA /* MWKImageInfoFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806601C0CE9030065EBC0 /* MWKImageInfoFetcher.m */; }; + 6779D45A2400822B002840CA /* MWKImageInfoFetcher.h in Sources */ = {isa = PBXBuildFile; fileRef = B0E8065F1C0CE9030065EBC0 /* MWKImageInfoFetcher.h */; }; + 6779D45D24008654002840CA /* MWKImageInfoFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8065F1C0CE9030065EBC0 /* MWKImageInfoFetcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6780CF232967683800D45927 /* TalkPageArchivesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF222967683800D45927 /* TalkPageArchivesViewController.swift */; }; + 6780CF242967683800D45927 /* TalkPageArchivesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF222967683800D45927 /* TalkPageArchivesViewController.swift */; }; + 6780CF252967683800D45927 /* TalkPageArchivesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF222967683800D45927 /* TalkPageArchivesViewController.swift */; }; + 6780CF262967683800D45927 /* TalkPageArchivesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF222967683800D45927 /* TalkPageArchivesViewController.swift */; }; + 6780CF282967690200D45927 /* TalkPageArchivesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF272967690200D45927 /* TalkPageArchivesView.swift */; }; + 6780CF292967690200D45927 /* TalkPageArchivesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF272967690200D45927 /* TalkPageArchivesView.swift */; }; + 6780CF2A2967690200D45927 /* TalkPageArchivesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF272967690200D45927 /* TalkPageArchivesView.swift */; }; + 6780CF2B2967690200D45927 /* TalkPageArchivesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF272967690200D45927 /* TalkPageArchivesView.swift */; }; + 6780CF2D29676AB000D45927 /* ShiftingTopViewsContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF2C29676AB000D45927 /* ShiftingTopViewsContaining.swift */; }; + 6780CF2E29676AB000D45927 /* ShiftingTopViewsContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF2C29676AB000D45927 /* ShiftingTopViewsContaining.swift */; }; + 6780CF2F29676AB000D45927 /* ShiftingTopViewsContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF2C29676AB000D45927 /* ShiftingTopViewsContaining.swift */; }; + 6780CF3029676AB000D45927 /* ShiftingTopViewsContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF2C29676AB000D45927 /* ShiftingTopViewsContaining.swift */; }; + 6780CF3329676DE300D45927 /* ShiftingTopViewsStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF3229676DE300D45927 /* ShiftingTopViewsStack.swift */; }; + 6780CF3429676DE300D45927 /* ShiftingTopViewsStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF3229676DE300D45927 /* ShiftingTopViewsStack.swift */; }; + 6780CF3529676DE300D45927 /* ShiftingTopViewsStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF3229676DE300D45927 /* ShiftingTopViewsStack.swift */; }; + 6780CF3629676DE300D45927 /* ShiftingTopViewsStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780CF3229676DE300D45927 /* ShiftingTopViewsStack.swift */; }; + 6780D5B4237A1F490087A5D1 /* DiffResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = 6780D5B3237A1F480087A5D1 /* DiffResponse.json */; }; + 6780D5B5237A1F490087A5D1 /* DiffResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = 6780D5B3237A1F480087A5D1 /* DiffResponse.json */; }; + 6780D5B6237A1F490087A5D1 /* DiffResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = 6780D5B3237A1F480087A5D1 /* DiffResponse.json */; }; + 6780D5B7237A1F490087A5D1 /* DiffResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = 6780D5B3237A1F480087A5D1 /* DiffResponse.json */; }; + 6780D5BA237AF8A10087A5D1 /* DiffToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780D5B9237AF8A10087A5D1 /* DiffToolbarView.swift */; }; + 6780D5BB237AF8A10087A5D1 /* DiffToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780D5B9237AF8A10087A5D1 /* DiffToolbarView.swift */; }; + 6780D5BC237AF8A10087A5D1 /* DiffToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780D5B9237AF8A10087A5D1 /* DiffToolbarView.swift */; }; + 6780D5BD237AF8A10087A5D1 /* DiffToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6780D5B9237AF8A10087A5D1 /* DiffToolbarView.swift */; }; + 6780D5C0237AF8AE0087A5D1 /* DiffToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6780D5BF237AF8AE0087A5D1 /* DiffToolbarView.xib */; }; + 6780D5C1237AF8AE0087A5D1 /* DiffToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6780D5BF237AF8AE0087A5D1 /* DiffToolbarView.xib */; }; + 6780D5C2237AF8AE0087A5D1 /* DiffToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6780D5BF237AF8AE0087A5D1 /* DiffToolbarView.xib */; }; + 6780D5C3237AF8AE0087A5D1 /* DiffToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6780D5BF237AF8AE0087A5D1 /* DiffToolbarView.xib */; }; + 6780D76D2830A7A200265F10 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676C864626D40AEB00A704C1 /* NotificationService.swift */; }; + 6780D76E2832908E00265F10 /* Notification+NotificationsCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0036C8B2282C2AAA00EADB35 /* Notification+NotificationsCenter.swift */; }; + 6780D76F2832908F00265F10 /* Notification+NotificationsCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0036C8B2282C2AAA00EADB35 /* Notification+NotificationsCenter.swift */; }; + 6780D7702832908F00265F10 /* Notification+NotificationsCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0036C8B2282C2AAA00EADB35 /* Notification+NotificationsCenter.swift */; }; + 6782DB912343B6F9003FA21B /* DiffContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DB902343B6F9003FA21B /* DiffContainerViewController.swift */; }; + 6782DB922343B6F9003FA21B /* DiffContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DB902343B6F9003FA21B /* DiffContainerViewController.swift */; }; + 6782DB932343B6F9003FA21B /* DiffContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DB902343B6F9003FA21B /* DiffContainerViewController.swift */; }; + 6782DB942343B6F9003FA21B /* DiffContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DB902343B6F9003FA21B /* DiffContainerViewController.swift */; }; + 6782DB9D2343B7DB003FA21B /* DiffHeaderTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DB9C2343B7DB003FA21B /* DiffHeaderTitleView.swift */; }; + 6782DB9E2343B7DB003FA21B /* DiffHeaderTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DB9C2343B7DB003FA21B /* DiffHeaderTitleView.swift */; }; + 6782DB9F2343B7DB003FA21B /* DiffHeaderTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DB9C2343B7DB003FA21B /* DiffHeaderTitleView.swift */; }; + 6782DBA02343B7DB003FA21B /* DiffHeaderTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DB9C2343B7DB003FA21B /* DiffHeaderTitleView.swift */; }; + 6782DBA32343B7EE003FA21B /* DiffHeaderSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBA22343B7EE003FA21B /* DiffHeaderSummaryView.swift */; }; + 6782DBA42343B7EE003FA21B /* DiffHeaderSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBA22343B7EE003FA21B /* DiffHeaderSummaryView.swift */; }; + 6782DBA52343B7EE003FA21B /* DiffHeaderSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBA22343B7EE003FA21B /* DiffHeaderSummaryView.swift */; }; + 6782DBA62343B7EE003FA21B /* DiffHeaderSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBA22343B7EE003FA21B /* DiffHeaderSummaryView.swift */; }; + 6782DBA92343B7FC003FA21B /* DiffHeaderEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBA82343B7FC003FA21B /* DiffHeaderEditorView.swift */; }; + 6782DBAA2343B7FC003FA21B /* DiffHeaderEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBA82343B7FC003FA21B /* DiffHeaderEditorView.swift */; }; + 6782DBAB2343B7FC003FA21B /* DiffHeaderEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBA82343B7FC003FA21B /* DiffHeaderEditorView.swift */; }; + 6782DBAC2343B7FC003FA21B /* DiffHeaderEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBA82343B7FC003FA21B /* DiffHeaderEditorView.swift */; }; + 6782DBAF2343B812003FA21B /* DiffHeaderCompareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBAE2343B812003FA21B /* DiffHeaderCompareView.swift */; }; + 6782DBB02343B812003FA21B /* DiffHeaderCompareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBAE2343B812003FA21B /* DiffHeaderCompareView.swift */; }; + 6782DBB12343B812003FA21B /* DiffHeaderCompareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBAE2343B812003FA21B /* DiffHeaderCompareView.swift */; }; + 6782DBB22343B812003FA21B /* DiffHeaderCompareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBAE2343B812003FA21B /* DiffHeaderCompareView.swift */; }; + 6782DBBB2343B861003FA21B /* DiffListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBBA2343B861003FA21B /* DiffListViewController.swift */; }; + 6782DBBC2343B861003FA21B /* DiffListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBBA2343B861003FA21B /* DiffListViewController.swift */; }; + 6782DBBD2343B861003FA21B /* DiffListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBBA2343B861003FA21B /* DiffListViewController.swift */; }; + 6782DBBE2343B861003FA21B /* DiffListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBBA2343B861003FA21B /* DiffListViewController.swift */; }; + 6782DBC12343FDCA003FA21B /* DiffListChangeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBC02343FDCA003FA21B /* DiffListChangeCell.swift */; }; + 6782DBC22343FDCA003FA21B /* DiffListChangeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBC02343FDCA003FA21B /* DiffListChangeCell.swift */; }; + 6782DBC32343FDCA003FA21B /* DiffListChangeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBC02343FDCA003FA21B /* DiffListChangeCell.swift */; }; + 6782DBC42343FDCA003FA21B /* DiffListChangeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBC02343FDCA003FA21B /* DiffListChangeCell.swift */; }; + 6782DBC72343FDE4003FA21B /* DiffListContextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBC62343FDE4003FA21B /* DiffListContextCell.swift */; }; + 6782DBC82343FDE4003FA21B /* DiffListContextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBC62343FDE4003FA21B /* DiffListContextCell.swift */; }; + 6782DBC92343FDE4003FA21B /* DiffListContextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBC62343FDE4003FA21B /* DiffListContextCell.swift */; }; + 6782DBCA2343FDE4003FA21B /* DiffListContextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBC62343FDE4003FA21B /* DiffListContextCell.swift */; }; + 6782DBCD2343FDF2003FA21B /* DiffListUneditedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBCC2343FDF2003FA21B /* DiffListUneditedCell.swift */; }; + 6782DBCE2343FDF2003FA21B /* DiffListUneditedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBCC2343FDF2003FA21B /* DiffListUneditedCell.swift */; }; + 6782DBCF2343FDF2003FA21B /* DiffListUneditedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBCC2343FDF2003FA21B /* DiffListUneditedCell.swift */; }; + 6782DBD02343FDF2003FA21B /* DiffListUneditedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBCC2343FDF2003FA21B /* DiffListUneditedCell.swift */; }; + 6782DBD32343FE03003FA21B /* DiffListGroupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBD22343FE03003FA21B /* DiffListGroupViewModel.swift */; }; + 6782DBD42343FE03003FA21B /* DiffListGroupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBD22343FE03003FA21B /* DiffListGroupViewModel.swift */; }; + 6782DBD52343FE03003FA21B /* DiffListGroupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBD22343FE03003FA21B /* DiffListGroupViewModel.swift */; }; + 6782DBD62343FE03003FA21B /* DiffListGroupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBD22343FE03003FA21B /* DiffListGroupViewModel.swift */; }; + 6782DBD92344EC86003FA21B /* DiffHeaderViewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBD82344EC86003FA21B /* DiffHeaderViewModels.swift */; }; + 6782DBDA2344EC86003FA21B /* DiffHeaderViewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBD82344EC86003FA21B /* DiffHeaderViewModels.swift */; }; + 6782DBDB2344EC86003FA21B /* DiffHeaderViewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBD82344EC86003FA21B /* DiffHeaderViewModels.swift */; }; + 6782DBDC2344EC86003FA21B /* DiffHeaderViewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBD82344EC86003FA21B /* DiffHeaderViewModels.swift */; }; + 6782DBE42345377B003FA21B /* DiffHeaderSummaryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE32345377B003FA21B /* DiffHeaderSummaryView.xib */; }; + 6782DBE52345377B003FA21B /* DiffHeaderSummaryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE32345377B003FA21B /* DiffHeaderSummaryView.xib */; }; + 6782DBE62345377B003FA21B /* DiffHeaderSummaryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE32345377B003FA21B /* DiffHeaderSummaryView.xib */; }; + 6782DBE72345377B003FA21B /* DiffHeaderSummaryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE32345377B003FA21B /* DiffHeaderSummaryView.xib */; }; + 6782DBEA23453787003FA21B /* DiffHeaderEditorView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE923453787003FA21B /* DiffHeaderEditorView.xib */; }; + 6782DBEB23453787003FA21B /* DiffHeaderEditorView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE923453787003FA21B /* DiffHeaderEditorView.xib */; }; + 6782DBEC23453787003FA21B /* DiffHeaderEditorView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE923453787003FA21B /* DiffHeaderEditorView.xib */; }; + 6782DBED23453787003FA21B /* DiffHeaderEditorView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE923453787003FA21B /* DiffHeaderEditorView.xib */; }; + 6782DBF023453799003FA21B /* DiffHeaderCompareView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBEF23453799003FA21B /* DiffHeaderCompareView.xib */; }; + 6782DBF123453799003FA21B /* DiffHeaderCompareView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBEF23453799003FA21B /* DiffHeaderCompareView.xib */; }; + 6782DBF223453799003FA21B /* DiffHeaderCompareView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBEF23453799003FA21B /* DiffHeaderCompareView.xib */; }; + 6782DBF323453799003FA21B /* DiffHeaderCompareView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBEF23453799003FA21B /* DiffHeaderCompareView.xib */; }; + 6782DBF5234537CF003FA21B /* DiffHeaderExtendedView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE02345053C003FA21B /* DiffHeaderExtendedView.xib */; }; + 6782DBF6234537CF003FA21B /* DiffHeaderExtendedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBE12345054C003FA21B /* DiffHeaderExtendedView.swift */; }; + 6782DBF7234537CF003FA21B /* DiffHeaderTitleView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE22345370F003FA21B /* DiffHeaderTitleView.xib */; }; + 6782DBF8234537D0003FA21B /* DiffHeaderExtendedView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE02345053C003FA21B /* DiffHeaderExtendedView.xib */; }; + 6782DBF9234537D0003FA21B /* DiffHeaderExtendedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBE12345054C003FA21B /* DiffHeaderExtendedView.swift */; }; + 6782DBFA234537D0003FA21B /* DiffHeaderTitleView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE22345370F003FA21B /* DiffHeaderTitleView.xib */; }; + 6782DBFB234537D0003FA21B /* DiffHeaderExtendedView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE02345053C003FA21B /* DiffHeaderExtendedView.xib */; }; + 6782DBFC234537D0003FA21B /* DiffHeaderExtendedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBE12345054C003FA21B /* DiffHeaderExtendedView.swift */; }; + 6782DBFD234537D0003FA21B /* DiffHeaderTitleView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE22345370F003FA21B /* DiffHeaderTitleView.xib */; }; + 6782DBFE234537D0003FA21B /* DiffHeaderExtendedView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE02345053C003FA21B /* DiffHeaderExtendedView.xib */; }; + 6782DBFF234537D0003FA21B /* DiffHeaderExtendedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DBE12345054C003FA21B /* DiffHeaderExtendedView.swift */; }; + 6782DC00234537D0003FA21B /* DiffHeaderTitleView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DBE22345370F003FA21B /* DiffHeaderTitleView.xib */; }; + 6782DC0523453D6B003FA21B /* DiffHeaderCompareItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DC0423453D6B003FA21B /* DiffHeaderCompareItemView.xib */; }; + 6782DC0623453D6B003FA21B /* DiffHeaderCompareItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DC0423453D6B003FA21B /* DiffHeaderCompareItemView.xib */; }; + 6782DC0723453D6B003FA21B /* DiffHeaderCompareItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DC0423453D6B003FA21B /* DiffHeaderCompareItemView.xib */; }; + 6782DC0823453D6B003FA21B /* DiffHeaderCompareItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DC0423453D6B003FA21B /* DiffHeaderCompareItemView.xib */; }; + 6782DC0B23453D7D003FA21B /* DiffHeaderCompareItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DC0A23453D7D003FA21B /* DiffHeaderCompareItemView.swift */; }; + 6782DC0C23453D7D003FA21B /* DiffHeaderCompareItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DC0A23453D7D003FA21B /* DiffHeaderCompareItemView.swift */; }; + 6782DC0D23453D7D003FA21B /* DiffHeaderCompareItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DC0A23453D7D003FA21B /* DiffHeaderCompareItemView.swift */; }; + 6782DC0E23453D7D003FA21B /* DiffHeaderCompareItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DC0A23453D7D003FA21B /* DiffHeaderCompareItemView.swift */; }; + 6782DC112346920B003FA21B /* DiffContainerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DC102346920B003FA21B /* DiffContainerViewModel.swift */; }; + 6782DC122346920B003FA21B /* DiffContainerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DC102346920B003FA21B /* DiffContainerViewModel.swift */; }; + 6782DC132346920B003FA21B /* DiffContainerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DC102346920B003FA21B /* DiffContainerViewModel.swift */; }; + 6782DC142346920B003FA21B /* DiffContainerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6782DC102346920B003FA21B /* DiffContainerViewModel.swift */; }; + 6782DC172347EE59003FA21B /* DiffListChangeCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DC162347EE59003FA21B /* DiffListChangeCell.xib */; }; + 6782DC182347EE59003FA21B /* DiffListChangeCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DC162347EE59003FA21B /* DiffListChangeCell.xib */; }; + 6782DC192347EE59003FA21B /* DiffListChangeCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DC162347EE59003FA21B /* DiffListChangeCell.xib */; }; + 6782DC1A2347EE5A003FA21B /* DiffListChangeCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6782DC162347EE59003FA21B /* DiffListChangeCell.xib */; }; + 67861A14223C13820044F69D /* WMFFindAndReplaceKeyboardBar.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67E069072238A5A5008550AC /* WMFFindAndReplaceKeyboardBar.xib */; }; + 67861A15223C13820044F69D /* WMFFindAndReplaceKeyboardBar.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67E069072238A5A5008550AC /* WMFFindAndReplaceKeyboardBar.xib */; }; + 67861A16223C13820044F69D /* WMFFindAndReplaceKeyboardBar.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67E069072238A5A5008550AC /* WMFFindAndReplaceKeyboardBar.xib */; }; + 67861A18223C13940044F69D /* FocusNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E0691A223B32F1008550AC /* FocusNavigationView.swift */; }; + 67861A19223C13940044F69D /* FocusNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E0691A223B32F1008550AC /* FocusNavigationView.swift */; }; + 67861A1A223C13940044F69D /* FocusNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E0691A223B32F1008550AC /* FocusNavigationView.swift */; }; + 67861A1C223C13990044F69D /* FocusNavigationView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67E06918223B32DF008550AC /* FocusNavigationView.xib */; }; + 67861A1D223C13990044F69D /* FocusNavigationView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67E06918223B32DF008550AC /* FocusNavigationView.xib */; }; + 67861A1E223C13990044F69D /* FocusNavigationView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67E06918223B32DF008550AC /* FocusNavigationView.xib */; }; + 6789FA2E22E7790900E43842 /* TalkPage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6789FA2D22E7790900E43842 /* TalkPage+Extensions.swift */; }; + 6789FA2F22E7790900E43842 /* TalkPage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6789FA2D22E7790900E43842 /* TalkPage+Extensions.swift */; }; + 6789FA3022E7790900E43842 /* TalkPage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6789FA2D22E7790900E43842 /* TalkPage+Extensions.swift */; }; + 6789FA3122E7790900E43842 /* TalkPage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6789FA2D22E7790900E43842 /* TalkPage+Extensions.swift */; }; + 678C7C2A23BE67F0001AC4D5 /* CacheController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678C7C2923BE67F0001AC4D5 /* CacheController.swift */; }; + 678C7C2E23BE705C001AC4D5 /* CacheDBWriting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678C7C2D23BE705C001AC4D5 /* CacheDBWriting.swift */; }; + 678C7C3023BE7319001AC4D5 /* CacheDBWriterHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678C7C2F23BE7319001AC4D5 /* CacheDBWriterHelper.swift */; }; + 678C7C3423BE75F9001AC4D5 /* CacheFileWriterHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678C7C3323BE75F9001AC4D5 /* CacheFileWriterHelper.swift */; }; + 678C7C3623BE7779001AC4D5 /* FileManager+CacheExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678C7C3523BE7779001AC4D5 /* FileManager+CacheExtensions.swift */; }; + 678D29AC2729EAD20036C5D9 /* RemoteNotification+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D29AB2729EAD20036C5D9 /* RemoteNotification+CoreDataProperties.swift */; }; + 678D29AE2729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D29AD2729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift */; }; + 678D29AF2729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D29AD2729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift */; }; + 678D29B02729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D29AD2729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift */; }; + 678D29B12729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D29AD2729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift */; }; + 678D29B3272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D29B2272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift */; }; + 678D29B4272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D29B2272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift */; }; + 678D29B5272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D29B2272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift */; }; + 678D29B6272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D29B2272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift */; }; + 678D79E4235E592F006161FF /* DiffListChangeItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79E3235E592F006161FF /* DiffListChangeItemViewModel.swift */; }; + 678D79EB235E5959006161FF /* DiffListChangeItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79E3235E592F006161FF /* DiffListChangeItemViewModel.swift */; }; + 678D79EC235E595A006161FF /* DiffListChangeItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79E3235E592F006161FF /* DiffListChangeItemViewModel.swift */; }; + 678D79ED235E595A006161FF /* DiffListChangeItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79E3235E592F006161FF /* DiffListChangeItemViewModel.swift */; }; + 678D79F0235E5979006161FF /* DiffListChangeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79EF235E5979006161FF /* DiffListChangeViewModel.swift */; }; + 678D79F1235E5979006161FF /* DiffListChangeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79EF235E5979006161FF /* DiffListChangeViewModel.swift */; }; + 678D79F2235E5979006161FF /* DiffListChangeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79EF235E5979006161FF /* DiffListChangeViewModel.swift */; }; + 678D79F3235E5979006161FF /* DiffListChangeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79EF235E5979006161FF /* DiffListChangeViewModel.swift */; }; + 678D79F6235E599B006161FF /* DiffListContextViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79F5235E599B006161FF /* DiffListContextViewModel.swift */; }; + 678D79F7235E599B006161FF /* DiffListContextViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79F5235E599B006161FF /* DiffListContextViewModel.swift */; }; + 678D79F8235E599B006161FF /* DiffListContextViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79F5235E599B006161FF /* DiffListContextViewModel.swift */; }; + 678D79F9235E599B006161FF /* DiffListContextViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79F5235E599B006161FF /* DiffListContextViewModel.swift */; }; + 678D79FC235E59B2006161FF /* DiffListUneditedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79FB235E59B2006161FF /* DiffListUneditedViewModel.swift */; }; + 678D79FD235E59B2006161FF /* DiffListUneditedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79FB235E59B2006161FF /* DiffListUneditedViewModel.swift */; }; + 678D79FE235E59B2006161FF /* DiffListUneditedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79FB235E59B2006161FF /* DiffListUneditedViewModel.swift */; }; + 678D79FF235E59B2006161FF /* DiffListUneditedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678D79FB235E59B2006161FF /* DiffListUneditedViewModel.swift */; }; + 678E7E8126432F060005439C /* NavigationEventsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678E7E8026432F060005439C /* NavigationEventsFunnel.swift */; }; + 678E7E8226432F060005439C /* NavigationEventsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678E7E8026432F060005439C /* NavigationEventsFunnel.swift */; }; + 678E7E8326432F060005439C /* NavigationEventsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678E7E8026432F060005439C /* NavigationEventsFunnel.swift */; }; + 678E7E8426432F060005439C /* NavigationEventsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678E7E8026432F060005439C /* NavigationEventsFunnel.swift */; }; + 678F512A23A7EE5100CE5357 /* ArticleCacheDBWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678F511823A4B92000CE5357 /* ArticleCacheDBWriter.swift */; }; + 678F512B23A7EE6600CE5357 /* ArticleFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676A8A8223A4013D0084B967 /* ArticleFetcher.swift */; }; + 6790AAF322D6861500D442D6 /* TalkPageNetworkDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0AB226A6DCA00537BC9 /* TalkPageNetworkDataTests.swift */; }; + 6790AAF422D6861500D442D6 /* OldTalkPageFetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0AD226A74C200537BC9 /* OldTalkPageFetcherTests.swift */; }; + 6790AAF522D6861500D442D6 /* TalkPageControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734114F22700A95005B31DA /* TalkPageControllerTests.swift */; }; + 6790AAF622D6861500D442D6 /* TalkPageTestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734115122700C47005B31DA /* TalkPageTestHelpers.swift */; }; + 6790AAF722D6861500D442D6 /* TalkPageLocalHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6734116922739FD6005B31DA /* TalkPageLocalHandlerTests.swift */; }; + 679471DB275F245000621071 /* NotificationsCenterInboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679471DA275F245000621071 /* NotificationsCenterInboxView.swift */; }; + 679471DC275F245000621071 /* NotificationsCenterInboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679471DA275F245000621071 /* NotificationsCenterInboxView.swift */; }; + 679471DD275F245000621071 /* NotificationsCenterInboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679471DA275F245000621071 /* NotificationsCenterInboxView.swift */; }; + 679471DE275F245900621071 /* NotificationsCenterInboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679471DA275F245000621071 /* NotificationsCenterInboxView.swift */; }; + 6798037224F99AB200D765AA /* SignificantEventsModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798036A24F94D6700D765AA /* SignificantEventsModels.swift */; }; + 6798037324F99AB200D765AA /* SignificantEventsFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798035B24F94CE300D765AA /* SignificantEventsFetcher.swift */; }; + 6798331A22C174ED0073CE6F /* LinkOnlyTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798331922C174ED0073CE6F /* LinkOnlyTextView.swift */; }; + 6798331B22C174F00073CE6F /* LinkOnlyTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798331922C174ED0073CE6F /* LinkOnlyTextView.swift */; }; + 6798331C22C174F00073CE6F /* LinkOnlyTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798331922C174ED0073CE6F /* LinkOnlyTextView.swift */; }; + 6798331D22C174F00073CE6F /* LinkOnlyTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798331922C174ED0073CE6F /* LinkOnlyTextView.swift */; }; + 6798332922C3F28A0073CE6F /* UITextView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798332822C3F28A0073CE6F /* UITextView+Extensions.swift */; }; + 6798332A22C3F2940073CE6F /* UITextView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798332822C3F28A0073CE6F /* UITextView+Extensions.swift */; }; + 6798332B22C3F2950073CE6F /* UITextView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798332822C3F28A0073CE6F /* UITextView+Extensions.swift */; }; + 6798332C22C3F2950073CE6F /* UITextView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798332822C3F28A0073CE6F /* UITextView+Extensions.swift */; }; + 67985A542523D80000EBF353 /* ArticleAsLivingDocController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798036024F94CEE00D765AA /* ArticleAsLivingDocController.swift */; }; + 67985A552523D80000EBF353 /* ArticleAsLivingDocController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798036024F94CEE00D765AA /* ArticleAsLivingDocController.swift */; }; + 67985A562523D80000EBF353 /* ArticleAsLivingDocController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798036024F94CEE00D765AA /* ArticleAsLivingDocController.swift */; }; + 67985A572523D80100EBF353 /* ArticleAsLivingDocController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6798036024F94CEE00D765AA /* ArticleAsLivingDocController.swift */; }; + 67985A862524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67985A852524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift */; }; + 67985A872524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67985A852524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift */; }; + 67985A882524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67985A852524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift */; }; + 67985A892524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67985A852524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift */; }; + 679A23F92968D865008D7686 /* ShiftingTopViewsData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A23F82968D865008D7686 /* ShiftingTopViewsData.swift */; }; + 679A23FA2968D865008D7686 /* ShiftingTopViewsData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A23F82968D865008D7686 /* ShiftingTopViewsData.swift */; }; + 679A23FB2968D865008D7686 /* ShiftingTopViewsData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A23F82968D865008D7686 /* ShiftingTopViewsData.swift */; }; + 679A23FC2968D865008D7686 /* ShiftingTopViewsData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A23F82968D865008D7686 /* ShiftingTopViewsData.swift */; }; + 679A23FE2968DAB9008D7686 /* ShiftingTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A23FD2968DAB9008D7686 /* ShiftingTopView.swift */; }; + 679A23FF2968DAB9008D7686 /* ShiftingTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A23FD2968DAB9008D7686 /* ShiftingTopView.swift */; }; + 679A24002968DAB9008D7686 /* ShiftingTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A23FD2968DAB9008D7686 /* ShiftingTopView.swift */; }; + 679A24012968DAB9008D7686 /* ShiftingTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A23FD2968DAB9008D7686 /* ShiftingTopView.swift */; }; + 679A24032968DBFC008D7686 /* ShiftingNavigationBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A24022968DBFC008D7686 /* ShiftingNavigationBarView.swift */; }; + 679A24042968DBFC008D7686 /* ShiftingNavigationBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A24022968DBFC008D7686 /* ShiftingNavigationBarView.swift */; }; + 679A24052968DBFC008D7686 /* ShiftingNavigationBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A24022968DBFC008D7686 /* ShiftingNavigationBarView.swift */; }; + 679A24062968DBFC008D7686 /* ShiftingNavigationBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A24022968DBFC008D7686 /* ShiftingNavigationBarView.swift */; }; + 679A24082968E0D0008D7686 /* ShiftingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A24072968E0D0008D7686 /* ShiftingScrollView.swift */; }; + 679A24092968E0D0008D7686 /* ShiftingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A24072968E0D0008D7686 /* ShiftingScrollView.swift */; }; + 679A240A2968E0D0008D7686 /* ShiftingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A24072968E0D0008D7686 /* ShiftingScrollView.swift */; }; + 679A240B2968E0D0008D7686 /* ShiftingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679A24072968E0D0008D7686 /* ShiftingScrollView.swift */; }; + 679F0AA92456FADE00EF4A6A /* ArticleCacheReadingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679F0AA82456FADE00EF4A6A /* ArticleCacheReadingTests.swift */; }; + 679F0AAD24574AD400EF4A6A /* ArticleViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679F0AAC24574AD400EF4A6A /* ArticleViewControllerTests.swift */; }; + 679FA104242E651C0095F3C6 /* ArticleManualPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679FA103242E651C0095F3C6 /* ArticleManualPerformanceTests.swift */; }; + 67A5E657236775C3007749FB /* GlobalUserInfoFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A5E656236775C3007749FB /* GlobalUserInfoFetcher.swift */; }; + 67A6F13823BFB75300736539 /* ImageCacheDBWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A6F13723BFB75300736539 /* ImageCacheDBWriter.swift */; }; + 67A6F13A23BFEA0400736539 /* ImageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A6F13923BFEA0400736539 /* ImageFetcher.swift */; }; + 67A6F13E23BFEF4200736539 /* ArticleCacheController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A6F13D23BFEF4200736539 /* ArticleCacheController.swift */; }; + 67A6F14023BFF62300736539 /* ImageCacheController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A6F13F23BFF62200736539 /* ImageCacheController.swift */; }; + 67A770C8251BFE0400F94EF9 /* CocoaLumberjackSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 67A770C7251BFE0400F94EF9 /* CocoaLumberjackSwift */; }; + 67A7CA7528665CEF008D4BF6 /* HTTPStatusCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A7CA7428665CEF008D4BF6 /* HTTPStatusCode.swift */; }; + 67ADEE9623A2CFFB0000CAF7 /* ArticleWebMessagingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67ADEE9523A2CFFB0000CAF7 /* ArticleWebMessagingController.swift */; }; + 67ADEE9723A2CFFB0000CAF7 /* ArticleWebMessagingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67ADEE9523A2CFFB0000CAF7 /* ArticleWebMessagingController.swift */; }; + 67ADEE9823A2CFFB0000CAF7 /* ArticleWebMessagingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67ADEE9523A2CFFB0000CAF7 /* ArticleWebMessagingController.swift */; }; + 67ADEE9923A2CFFB0000CAF7 /* ArticleWebMessagingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67ADEE9523A2CFFB0000CAF7 /* ArticleWebMessagingController.swift */; }; + 67B5334128416C0D00C33E13 /* UserDataExportCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B5333B28416A3B00C33E13 /* UserDataExportCache.swift */; }; + 67B5334228416C0E00C33E13 /* UserDataExportCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B5333B28416A3B00C33E13 /* UserDataExportCache.swift */; }; + 67B5334328416C0E00C33E13 /* UserDataExportCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B5333B28416A3B00C33E13 /* UserDataExportCache.swift */; }; + 67B5334428416C0F00C33E13 /* UserDataExportCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B5333B28416A3B00C33E13 /* UserDataExportCache.swift */; }; + 67B64D572507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B64D562507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift */; }; + 67B64D582507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B64D562507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift */; }; + 67B64D592507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B64D562507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift */; }; + 67B64D5A2507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B64D562507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift */; }; + 67B64D5C2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B64D5B2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift */; }; + 67B64D5D2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B64D5B2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift */; }; + 67B64D5E2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B64D5B2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift */; }; + 67B64D5F2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B64D5B2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift */; }; + 67B7E77E2988777A00708A81 /* MediaWikiApiErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B7E7792988768C00708A81 /* MediaWikiApiErrors.swift */; }; + 67BEFFD528AD9DF000606B38 /* TalkPageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67BEFFD428AD9DF000606B38 /* TalkPageType.swift */; }; + 67BEFFD628AD9DF000606B38 /* TalkPageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67BEFFD428AD9DF000606B38 /* TalkPageType.swift */; }; + 67BEFFD728AD9DF000606B38 /* TalkPageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67BEFFD428AD9DF000606B38 /* TalkPageType.swift */; }; + 67BEFFD828AD9DF000606B38 /* TalkPageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67BEFFD428AD9DF000606B38 /* TalkPageType.swift */; }; + 67BEFFDE28AEDF5200606B38 /* WikimediaProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67BEFFD928AEDF3600606B38 /* WikimediaProject.swift */; }; + 67C1757628AD4D6000C5ABA4 /* TalkPageDataController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C1757528AD4D6000C5ABA4 /* TalkPageDataController.swift */; }; + 67C1757728AD4D6000C5ABA4 /* TalkPageDataController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C1757528AD4D6000C5ABA4 /* TalkPageDataController.swift */; }; + 67C1757828AD4D6000C5ABA4 /* TalkPageDataController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C1757528AD4D6000C5ABA4 /* TalkPageDataController.swift */; }; + 67C1757928AD4D6000C5ABA4 /* TalkPageDataController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C1757528AD4D6000C5ABA4 /* TalkPageDataController.swift */; }; + 67C6F74E27E2919B00B9C864 /* RemoteNotificationsModelController+TestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F74D27E2919A00B9C864 /* RemoteNotificationsModelController+TestExtensions.swift */; }; + 67C6F75027E293C700B9C864 /* NotificationsCenterViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F74F27E293C700B9C864 /* NotificationsCenterViewModelTests.swift */; }; + 67C6F76827E2E76E00B9C864 /* NotificationsCenterCellViewModelUserTalkMessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F76727E2E76E00B9C864 /* NotificationsCenterCellViewModelUserTalkMessageTests.swift */; }; + 67C6F77327E2E78800B9C864 /* NotificationsCenterCellViewModelWikidataConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F76927E2E77D00B9C864 /* NotificationsCenterCellViewModelWikidataConnectionTests.swift */; }; + 67C6F77427E2E78800B9C864 /* NotificationsCenterCellViewModelPageLinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F76A27E2E77E00B9C864 /* NotificationsCenterCellViewModelPageLinkTests.swift */; }; + 67C6F77527E2E78800B9C864 /* NotificationsCenterCellViewModelGenericTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F76B27E2E77E00B9C864 /* NotificationsCenterCellViewModelGenericTests.swift */; }; + 67C6F77627E2E78800B9C864 /* NotificationsCenterCellViewModelMentionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F76C27E2E77F00B9C864 /* NotificationsCenterCellViewModelMentionTests.swift */; }; + 67C6F77727E2E78800B9C864 /* NotificationsCenterCellViewModelEditMilestoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F76D27E2E78000B9C864 /* NotificationsCenterCellViewModelEditMilestoneTests.swift */; }; + 67C6F77827E2E78800B9C864 /* NotificationsCenterCellViewModelThanksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F76E27E2E78100B9C864 /* NotificationsCenterCellViewModelThanksTests.swift */; }; + 67C6F77927E2E78800B9C864 /* NotificationsCenterCellViewModelUserRightsChangeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F76F27E2E78300B9C864 /* NotificationsCenterCellViewModelUserRightsChangeTests.swift */; }; + 67C6F77A27E2E78800B9C864 /* NotificationsCenterCellViewModelEditRevertedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F77027E2E78400B9C864 /* NotificationsCenterCellViewModelEditRevertedTests.swift */; }; + 67C6F77B27E2E78800B9C864 /* NotificationsCenterCellViewModelLoginIssuesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F77127E2E78500B9C864 /* NotificationsCenterCellViewModelLoginIssuesTests.swift */; }; + 67C6F77C27E2E78800B9C864 /* NotificationsCenterCellViewModelWelcomeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F77227E2E78600B9C864 /* NotificationsCenterCellViewModelWelcomeTests.swift */; }; + 67C6F77E27E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F77D27E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift */; }; + 67C6F77F27E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F77D27E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift */; }; + 67C6F78027E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F77D27E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift */; }; + 67C6F78127E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F77D27E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift */; }; + 67C6F78327E8BC2E00B9C864 /* NotificationsCenterIconType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F78227E8BC2E00B9C864 /* NotificationsCenterIconType.swift */; }; + 67C6F78427E8BC2F00B9C864 /* NotificationsCenterIconType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F78227E8BC2E00B9C864 /* NotificationsCenterIconType.swift */; }; + 67C6F78527E8BC2F00B9C864 /* NotificationsCenterIconType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F78227E8BC2E00B9C864 /* NotificationsCenterIconType.swift */; }; + 67C6F78627E8BC2F00B9C864 /* NotificationsCenterIconType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F78227E8BC2E00B9C864 /* NotificationsCenterIconType.swift */; }; + 67C6F79227E8C03A00B9C864 /* NotificationsCenterAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79127E8C03900B9C864 /* NotificationsCenterAction.swift */; }; + 67C6F79327E8C03A00B9C864 /* NotificationsCenterAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79127E8C03900B9C864 /* NotificationsCenterAction.swift */; }; + 67C6F79427E8C03A00B9C864 /* NotificationsCenterAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79127E8C03900B9C864 /* NotificationsCenterAction.swift */; }; + 67C6F79527E8C03A00B9C864 /* NotificationsCenterAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79127E8C03900B9C864 /* NotificationsCenterAction.swift */; }; + 67C6F79727E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79627E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift */; }; + 67C6F79827E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79627E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift */; }; + 67C6F79927E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79627E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift */; }; + 67C6F79A27E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79627E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift */; }; + 67C6F79C27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79B27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift */; }; + 67C6F79D27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79B27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift */; }; + 67C6F79E27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79B27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift */; }; + 67C6F79F27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F79B27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift */; }; + 67C6F7A127E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7A027E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift */; }; + 67C6F7A227E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7A027E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift */; }; + 67C6F7A327E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7A027E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift */; }; + 67C6F7A427E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7A027E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift */; }; + 67C6F7A627E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7A527E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift */; }; + 67C6F7A727E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7A527E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift */; }; + 67C6F7A827E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7A527E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift */; }; + 67C6F7A927E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7A527E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift */; }; + 67C6F7AB27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7AA27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift */; }; + 67C6F7AC27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7AA27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift */; }; + 67C6F7AD27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7AA27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift */; }; + 67C6F7AE27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C6F7AA27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift */; }; + 67C78F7128B6DA1300AC207A /* SwiftUITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C78F7028B6DA1300AC207A /* SwiftUITextView.swift */; }; + 67C78F7228B6DA1400AC207A /* SwiftUITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C78F7028B6DA1300AC207A /* SwiftUITextView.swift */; }; + 67C78F7328B6DA1400AC207A /* SwiftUITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C78F7028B6DA1300AC207A /* SwiftUITextView.swift */; }; + 67C78F7428B6DA1400AC207A /* SwiftUITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C78F7028B6DA1300AC207A /* SwiftUITextView.swift */; }; + 67C78F7628B7407000AC207A /* VanishAccountFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C78F7528B7406E00AC207A /* VanishAccountFooterView.swift */; }; + 67C78F7728B7407000AC207A /* VanishAccountFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C78F7528B7406E00AC207A /* VanishAccountFooterView.swift */; }; + 67C78F7828B7407000AC207A /* VanishAccountFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C78F7528B7406E00AC207A /* VanishAccountFooterView.swift */; }; + 67C78F7928B7407100AC207A /* VanishAccountFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C78F7528B7406E00AC207A /* VanishAccountFooterView.swift */; }; + 67C9D58F28D3689F00629165 /* WMFLocalizedDateFormatStrings+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C9D58E28D3689F00629165 /* WMFLocalizedDateFormatStrings+Extensions.swift */; }; + 67C9D59128D36BDD00629165 /* WMFFeedNewsStory+LocalizedStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C9D59028D36BDD00629165 /* WMFFeedNewsStory+LocalizedStrings.swift */; }; + 67C9FBFF28C77F350065A530 /* TalkPageTopicComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C9FBFE28C77F350065A530 /* TalkPageTopicComposeViewController.swift */; }; + 67C9FC0028C77F350065A530 /* TalkPageTopicComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C9FBFE28C77F350065A530 /* TalkPageTopicComposeViewController.swift */; }; + 67C9FC0128C77F350065A530 /* TalkPageTopicComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C9FBFE28C77F350065A530 /* TalkPageTopicComposeViewController.swift */; }; + 67C9FC0228C77F350065A530 /* TalkPageTopicComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67C9FBFE28C77F350065A530 /* TalkPageTopicComposeViewController.swift */; }; + 67CCB348299155230032439D /* WMFItemSourceExcludingActivityTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB8237522970AE6A00FD629E /* WMFItemSourceExcludingActivityTypes.swift */; }; + 67CCB349299155250032439D /* WMFItemSourceExcludingActivityTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB8237522970AE6A00FD629E /* WMFItemSourceExcludingActivityTypes.swift */; }; + 67CCB34A299155250032439D /* WMFItemSourceExcludingActivityTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB8237522970AE6A00FD629E /* WMFItemSourceExcludingActivityTypes.swift */; }; + 67CE5D20222F70C0007B0A2C /* IconBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672F0557222F24FB00FB1084 /* IconBarButtonItem.swift */; }; + 67CE5D22222F70C0007B0A2C /* IconBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672F0557222F24FB00FB1084 /* IconBarButtonItem.swift */; }; + 67CE5D23222F70C0007B0A2C /* IconBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672F0557222F24FB00FB1084 /* IconBarButtonItem.swift */; }; + 67CEF25F234FCA8100D5CA6C /* DiffListContextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67CEF25E234FCA8100D5CA6C /* DiffListContextCell.xib */; }; + 67CEF2612350C29D00D5CA6C /* DiffListUneditedCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67CEF2602350C29D00D5CA6C /* DiffListUneditedCell.xib */; }; + 67CEF263235110F700D5CA6C /* DiffNetworkModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CEF262235110F700D5CA6C /* DiffNetworkModels.swift */; }; + 67CEF26A2351111D00D5CA6C /* DiffNetworkModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CEF262235110F700D5CA6C /* DiffNetworkModels.swift */; }; + 67CEF26B2351111D00D5CA6C /* DiffNetworkModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CEF262235110F700D5CA6C /* DiffNetworkModels.swift */; }; + 67CEF26C2351111D00D5CA6C /* DiffNetworkModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CEF262235110F700D5CA6C /* DiffNetworkModels.swift */; }; + 67CEF26F2351113000D5CA6C /* DiffController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CEF26E2351113000D5CA6C /* DiffController.swift */; }; + 67CEF2702351113000D5CA6C /* DiffController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CEF26E2351113000D5CA6C /* DiffController.swift */; }; + 67CEF2712351113000D5CA6C /* DiffController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CEF26E2351113000D5CA6C /* DiffController.swift */; }; + 67CEF2722351113000D5CA6C /* DiffController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CEF26E2351113000D5CA6C /* DiffController.swift */; }; + 67D3C453228CB54E001D5741 /* OldTalkPageReplyComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D3C452228CB54E001D5741 /* OldTalkPageReplyComposeView.swift */; }; + 67D3C454228CB54E001D5741 /* OldTalkPageReplyComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D3C452228CB54E001D5741 /* OldTalkPageReplyComposeView.swift */; }; + 67D3C455228CB54E001D5741 /* OldTalkPageReplyComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D3C452228CB54E001D5741 /* OldTalkPageReplyComposeView.swift */; }; + 67D3C456228CB54E001D5741 /* OldTalkPageReplyComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D3C452228CB54E001D5741 /* OldTalkPageReplyComposeView.swift */; }; + 67D6C00A240581ED005709B1 /* CacheItemMigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D6C009240581ED005709B1 /* CacheItemMigrationPolicy.swift */; }; + 67D6C00C24058714005709B1 /* CacheItemMappingModel.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = 67D6C00B24058714005709B1 /* CacheItemMappingModel.xcmappingmodel */; }; + 67D6C01C2405A4FB005709B1 /* CacheItem+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D6C01A2405A4FB005709B1 /* CacheItem+CoreDataClass.swift */; }; + 67D6C01D2405A4FB005709B1 /* CacheItem+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D6C01B2405A4FB005709B1 /* CacheItem+CoreDataProperties.swift */; }; + 67D6C0202405B3D2005709B1 /* CacheGroup+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D6C01E2405B3D2005709B1 /* CacheGroup+CoreDataClass.swift */; }; + 67D6C0212405B3D2005709B1 /* CacheGroup+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D6C01F2405B3D2005709B1 /* CacheGroup+CoreDataProperties.swift */; }; + 67D9D1F02970D88E00BFCD4F /* DisclosureButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1EF2970D88E00BFCD4F /* DisclosureButton.swift */; }; + 67D9D1F12970D88E00BFCD4F /* DisclosureButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1EF2970D88E00BFCD4F /* DisclosureButton.swift */; }; + 67D9D1F22970D88E00BFCD4F /* DisclosureButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1EF2970D88E00BFCD4F /* DisclosureButton.swift */; }; + 67D9D1F32970D88E00BFCD4F /* DisclosureButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1EF2970D88E00BFCD4F /* DisclosureButton.swift */; }; + 67D9D1F62970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1F52970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift */; }; + 67D9D1F72970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1F52970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift */; }; + 67D9D1F82970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1F52970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift */; }; + 67D9D1F92970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1F52970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift */; }; + 67D9D1FB29711CA700BFCD4F /* Loadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1FA29711CA700BFCD4F /* Loadable.swift */; }; + 67D9D1FC29711CA700BFCD4F /* Loadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1FA29711CA700BFCD4F /* Loadable.swift */; }; + 67D9D1FD29711CA700BFCD4F /* Loadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1FA29711CA700BFCD4F /* Loadable.swift */; }; + 67D9D1FE29711CA700BFCD4F /* Loadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67D9D1FA29711CA700BFCD4F /* Loadable.swift */; }; + 67DA31882720957B0035D40F /* RemoteNotificationsPagingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DA31872720957A0035D40F /* RemoteNotificationsPagingOperation.swift */; }; + 67DAEDA123CD1BC9003AA208 /* CacheGatekeeper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDA023CD1BC9003AA208 /* CacheGatekeeper.swift */; }; + 67DAEDA323CE24DA003AA208 /* SavedArticlesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDA223CE24DA003AA208 /* SavedArticlesFetcher.swift */; }; + 67DAEDA423CE24DA003AA208 /* SavedArticlesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDA223CE24DA003AA208 /* SavedArticlesFetcher.swift */; }; + 67DAEDA523CE24DA003AA208 /* SavedArticlesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDA223CE24DA003AA208 /* SavedArticlesFetcher.swift */; }; + 67DAEDA623CE24DA003AA208 /* SavedArticlesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDA223CE24DA003AA208 /* SavedArticlesFetcher.swift */; }; + 67DAEDD927E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDD827E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift */; }; + 67DAEDDA27E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDD827E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift */; }; + 67DAEDDB27E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDD827E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift */; }; + 67DAEDDC27E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDD827E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift */; }; + 67DAEDE827E8FB63005CF9B6 /* NotificationsCenterDetailViewModelWelcomeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDDD27E8FB5F005CF9B6 /* NotificationsCenterDetailViewModelWelcomeTests.swift */; }; + 67DAEDE927E8FB63005CF9B6 /* NotificationsCenterDetailViewModelLoginIssuesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDDE27E8FB5F005CF9B6 /* NotificationsCenterDetailViewModelLoginIssuesTests.swift */; }; + 67DAEDEA27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelGenericTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDDF27E8FB5F005CF9B6 /* NotificationsCenterDetailViewModelGenericTests.swift */; }; + 67DAEDEB27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelUserRightsChangeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDE027E8FB60005CF9B6 /* NotificationsCenterDetailViewModelUserRightsChangeTests.swift */; }; + 67DAEDEC27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelUserTalkMessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDE127E8FB60005CF9B6 /* NotificationsCenterDetailViewModelUserTalkMessageTests.swift */; }; + 67DAEDED27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelWikidataConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDE227E8FB60005CF9B6 /* NotificationsCenterDetailViewModelWikidataConnectionTests.swift */; }; + 67DAEDEE27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelEditRevertedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDE327E8FB61005CF9B6 /* NotificationsCenterDetailViewModelEditRevertedTests.swift */; }; + 67DAEDEF27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelThanksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDE427E8FB61005CF9B6 /* NotificationsCenterDetailViewModelThanksTests.swift */; }; + 67DAEDF027E8FB63005CF9B6 /* NotificationsCenterDetailViewModelMentionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDE527E8FB62005CF9B6 /* NotificationsCenterDetailViewModelMentionTests.swift */; }; + 67DAEDF127E8FB63005CF9B6 /* NotificationsCenterDetailViewModelEditMilestoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDE627E8FB62005CF9B6 /* NotificationsCenterDetailViewModelEditMilestoneTests.swift */; }; + 67DAEDF227E8FB63005CF9B6 /* NotificationsCenterDetailViewModelPageLinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DAEDE727E8FB62005CF9B6 /* NotificationsCenterDetailViewModelPageLinkTests.swift */; }; + 67DC5BE323A017CA00B03A84 /* ArticleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BE223A017CA00B03A84 /* ArticleViewController.swift */; }; + 67DC5BE423A017CA00B03A84 /* ArticleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BE223A017CA00B03A84 /* ArticleViewController.swift */; }; + 67DC5BE523A017CA00B03A84 /* ArticleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BE223A017CA00B03A84 /* ArticleViewController.swift */; }; + 67DC5BE623A017CA00B03A84 /* ArticleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BE223A017CA00B03A84 /* ArticleViewController.swift */; }; + 67DC5BE923A03FE700B03A84 /* ArticleToolbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BE823A03FE700B03A84 /* ArticleToolbarController.swift */; }; + 67DC5BEA23A03FE700B03A84 /* ArticleToolbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BE823A03FE700B03A84 /* ArticleToolbarController.swift */; }; + 67DC5BEB23A03FE700B03A84 /* ArticleToolbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BE823A03FE700B03A84 /* ArticleToolbarController.swift */; }; + 67DC5BEC23A03FE700B03A84 /* ArticleToolbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BE823A03FE700B03A84 /* ArticleToolbarController.swift */; }; + 67DC5BEF23A1427D00B03A84 /* ActionHandlerScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BEE23A1427C00B03A84 /* ActionHandlerScript.swift */; }; + 67DC5BF023A1427D00B03A84 /* ActionHandlerScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BEE23A1427C00B03A84 /* ActionHandlerScript.swift */; }; + 67DC5BF123A1427D00B03A84 /* ActionHandlerScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BEE23A1427C00B03A84 /* ActionHandlerScript.swift */; }; + 67DC5BF223A1427D00B03A84 /* ActionHandlerScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67DC5BEE23A1427C00B03A84 /* ActionHandlerScript.swift */; }; + 67DCB7A1278F8D6F0041272C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D801C8511EB8E131001FA294 /* InfoPlist.strings */; }; + 67DCB7A2278F8D700041272C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D801C8511EB8E131001FA294 /* InfoPlist.strings */; }; + 67DCB7A3278F8D700041272C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D801C8511EB8E131001FA294 /* InfoPlist.strings */; }; + 67DDD188250C1A27006C0F93 /* ThreeLineHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2090EE2500247100849774 /* ThreeLineHeaderView.swift */; }; + 67DDD189250C1A27006C0F93 /* ThreeLineHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2090EE2500247100849774 /* ThreeLineHeaderView.swift */; }; + 67DDD18A250C1A28006C0F93 /* ThreeLineHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2090EE2500247100849774 /* ThreeLineHeaderView.swift */; }; + 67E069062238A396008550AC /* FindAndReplaceKeyboardBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E069052238A396008550AC /* FindAndReplaceKeyboardBar.swift */; }; + 67E069082238A5A6008550AC /* WMFFindAndReplaceKeyboardBar.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67E069072238A5A5008550AC /* WMFFindAndReplaceKeyboardBar.xib */; }; + 67E0690A22399D1C008550AC /* ReadingThemesControlsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03F31CE0FB7700009083 /* ReadingThemesControlsViewController.swift */; }; + 67E0690B22399D1C008550AC /* ReadingThemesControlsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03F31CE0FB7700009083 /* ReadingThemesControlsViewController.swift */; }; + 67E0690C22399D1D008550AC /* ReadingThemesControlsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03F31CE0FB7700009083 /* ReadingThemesControlsViewController.swift */; }; + 67E0690E22399D2E008550AC /* ReadingThemesControlsProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671AC2552226FB9B005B37F8 /* ReadingThemesControlsProtocols.swift */; }; + 67E0690F22399D2E008550AC /* ReadingThemesControlsProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671AC2552226FB9B005B37F8 /* ReadingThemesControlsProtocols.swift */; }; + 67E0691022399D2F008550AC /* ReadingThemesControlsProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671AC2552226FB9B005B37F8 /* ReadingThemesControlsProtocols.swift */; }; + 67E069122239B33D008550AC /* FindAndReplaceKeyboardBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E069052238A396008550AC /* FindAndReplaceKeyboardBar.swift */; }; + 67E069132239B33D008550AC /* FindAndReplaceKeyboardBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E069052238A396008550AC /* FindAndReplaceKeyboardBar.swift */; }; + 67E069142239B33E008550AC /* FindAndReplaceKeyboardBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E069052238A396008550AC /* FindAndReplaceKeyboardBar.swift */; }; + 67E06919223B32DF008550AC /* FocusNavigationView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67E06918223B32DF008550AC /* FocusNavigationView.xib */; }; + 67E0691B223B32F1008550AC /* FocusNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E0691A223B32F1008550AC /* FocusNavigationView.swift */; }; + 67E2E48F250452E60070F12D /* ArticleAsLivingDocHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E2E48E250452E60070F12D /* ArticleAsLivingDocHeaderView.swift */; }; + 67E2E490250452E60070F12D /* ArticleAsLivingDocHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E2E48E250452E60070F12D /* ArticleAsLivingDocHeaderView.swift */; }; + 67E2E491250452E60070F12D /* ArticleAsLivingDocHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E2E48E250452E60070F12D /* ArticleAsLivingDocHeaderView.swift */; }; + 67E2E492250452E60070F12D /* ArticleAsLivingDocHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E2E48E250452E60070F12D /* ArticleAsLivingDocHeaderView.swift */; }; + 67E2E4982504E2130070F12D /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E2E4932504E1C70070F12D /* TimelineView.swift */; }; + 67E3992A24786E2100441831 /* ReadingListManualPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E3992924786E2100441831 /* ReadingListManualPerformanceTests.swift */; }; + 67E3992C24786E6D00441831 /* TalkPageManualPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E3992B24786E6D00441831 /* TalkPageManualPerformanceTests.swift */; }; + 67E466FA241BED770014149B /* EditHistoryCompareFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E466F9241BED770014149B /* EditHistoryCompareFunnel.swift */; }; + 67E466FB241BED800014149B /* EditHistoryCompareFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E466F9241BED770014149B /* EditHistoryCompareFunnel.swift */; }; + 67E466FC241BED800014149B /* EditHistoryCompareFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E466F9241BED770014149B /* EditHistoryCompareFunnel.swift */; }; + 67E466FD241BED810014149B /* EditHistoryCompareFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E466F9241BED770014149B /* EditHistoryCompareFunnel.swift */; }; + 67E50B2B27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E50B2A27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift */; }; + 67E50B2C27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E50B2A27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift */; }; + 67E50B2D27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E50B2A27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift */; }; + 67E50B2E27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E50B2A27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift */; }; + 67E5DA5C2761B0AB00CE827D /* NotificationsCenterFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E5DA5B2761B0AB00CE827D /* NotificationsCenterFilterView.swift */; }; + 67E5DA5D2761B0AB00CE827D /* NotificationsCenterFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E5DA5B2761B0AB00CE827D /* NotificationsCenterFilterView.swift */; }; + 67E5DA5E2761B0AB00CE827D /* NotificationsCenterFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E5DA5B2761B0AB00CE827D /* NotificationsCenterFilterView.swift */; }; + 67E5DA5F2761B0AB00CE827D /* NotificationsCenterFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E5DA5B2761B0AB00CE827D /* NotificationsCenterFilterView.swift */; }; + 67E5DA6B276416A600CE827D /* RemoteNotificationsRefreshCrossWikiOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E5DA6A276416A600CE827D /* RemoteNotificationsRefreshCrossWikiOperation.swift */; }; + 67E8B0742268DA8B00537BC9 /* OldTalkPageTopicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0732268DA8B00537BC9 /* OldTalkPageTopicCell.swift */; }; + 67E8B0762268DE4B00537BC9 /* TalkPageContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0752268DE4B00537BC9 /* TalkPageContainerViewController.swift */; }; + 67E8B07B226A57DE00537BC9 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E6C2267B79E0079DEEF /* AccountViewController.swift */; }; + 67E8B07C226A57DE00537BC9 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E6C2267B79E0079DEEF /* AccountViewController.swift */; }; + 67E8B07D226A57DF00537BC9 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E6C2267B79E0079DEEF /* AccountViewController.swift */; }; + 67E8B07F226A57E400537BC9 /* OldTalkPageTopicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0732268DA8B00537BC9 /* OldTalkPageTopicCell.swift */; }; + 67E8B082226A57E500537BC9 /* OldTalkPageTopicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0732268DA8B00537BC9 /* OldTalkPageTopicCell.swift */; }; + 67E8B083226A57E600537BC9 /* OldTalkPageTopicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0732268DA8B00537BC9 /* OldTalkPageTopicCell.swift */; }; + 67E8B084226A57E900537BC9 /* TalkPageContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0752268DE4B00537BC9 /* TalkPageContainerViewController.swift */; }; + 67E8B085226A57E900537BC9 /* TalkPageTopicListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E702267B8020079DEEF /* TalkPageTopicListViewController.swift */; }; + 67E8B086226A57E900537BC9 /* TalkPageTopicNewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E782267B9500079DEEF /* TalkPageTopicNewViewController.swift */; }; + 67E8B088226A57E900537BC9 /* TalkPageReplyListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E742267B9070079DEEF /* TalkPageReplyListViewController.swift */; }; + 67E8B08B226A57E900537BC9 /* TalkPageContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0752268DE4B00537BC9 /* TalkPageContainerViewController.swift */; }; + 67E8B08C226A57E900537BC9 /* TalkPageTopicListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E702267B8020079DEEF /* TalkPageTopicListViewController.swift */; }; + 67E8B08D226A57E900537BC9 /* TalkPageTopicNewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E782267B9500079DEEF /* TalkPageTopicNewViewController.swift */; }; + 67E8B08F226A57E900537BC9 /* TalkPageReplyListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E742267B9070079DEEF /* TalkPageReplyListViewController.swift */; }; + 67E8B092226A57EA00537BC9 /* TalkPageContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E8B0752268DE4B00537BC9 /* TalkPageContainerViewController.swift */; }; + 67E8B093226A57EA00537BC9 /* TalkPageTopicListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E702267B8020079DEEF /* TalkPageTopicListViewController.swift */; }; + 67E8B094226A57EA00537BC9 /* TalkPageTopicNewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E782267B9500079DEEF /* TalkPageTopicNewViewController.swift */; }; + 67E8B096226A57EA00537BC9 /* TalkPageReplyListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E742267B9070079DEEF /* TalkPageReplyListViewController.swift */; }; + 67E9A11C25536B6F00C5ED31 /* ABTestsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67E9A11B25536B6F00C5ED31 /* ABTestsController.swift */; }; + 67EA9E10228F0358008D9EFD /* OldTalkPageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675A7CFD227A3F7C0034D9D9 /* OldTalkPageHeaderView.swift */; }; + 67EA9E11228F0359008D9EFD /* OldTalkPageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675A7CFD227A3F7C0034D9D9 /* OldTalkPageHeaderView.swift */; }; + 67EA9E12228F0359008D9EFD /* OldTalkPageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675A7CFD227A3F7C0034D9D9 /* OldTalkPageHeaderView.swift */; }; + 67EA9E14228F035D008D9EFD /* TalkPageReplyFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676070A1227CE60800A81F09 /* TalkPageReplyFooterView.swift */; }; + 67EA9E15228F035E008D9EFD /* TalkPageReplyFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676070A1227CE60800A81F09 /* TalkPageReplyFooterView.swift */; }; + 67EA9E16228F035E008D9EFD /* TalkPageReplyFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676070A1227CE60800A81F09 /* TalkPageReplyFooterView.swift */; }; + 67ED8EB124F99FF400DD5D39 /* SignificantEventsFetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67ED8EB024F99FF400DD5D39 /* SignificantEventsFetcherTests.swift */; }; + 67F1375E23C986CD00512B61 /* CacheTaskTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F1375D23C986CD00512B61 /* CacheTaskTracking.swift */; }; + 67F1A180286F34A5000D0F74 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836BF56B2869EC2600B98321 /* FeatureFlags.swift */; }; + 67F73383273C163700D7D713 /* TimeInterval+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73382273C163700D7D713 /* TimeInterval+Extensions.swift */; }; + 67F73386273C1FBA00D7D713 /* NotificationServiceHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73385273C1FBA00D7D713 /* NotificationServiceHelperTests.swift */; }; + 67F73388273C26A000D7D713 /* NotificationServiceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73387273C26A000D7D713 /* NotificationServiceHelper.swift */; }; + 67F73E6D2267B79E0079DEEF /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E6C2267B79E0079DEEF /* AccountViewController.swift */; }; + 67F73E712267B8020079DEEF /* TalkPageTopicListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E702267B8020079DEEF /* TalkPageTopicListViewController.swift */; }; + 67F73E752267B9070079DEEF /* TalkPageReplyListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E742267B9070079DEEF /* TalkPageReplyListViewController.swift */; }; + 67F73E792267B9510079DEEF /* TalkPageTopicNewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F73E782267B9500079DEEF /* TalkPageTopicNewViewController.swift */; }; + 67F9AE4923AD2F38003D4F5E /* Array+SafeIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3EE1532267DC3800709CF6 /* Array+SafeIndex.swift */; }; + 67FBE335297056EB00A2E4AD /* TalkPageArchivesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FBE334297056EB00A2E4AD /* TalkPageArchivesFetcher.swift */; }; + 67FBE336297056EB00A2E4AD /* TalkPageArchivesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FBE334297056EB00A2E4AD /* TalkPageArchivesFetcher.swift */; }; + 67FBE337297056EB00A2E4AD /* TalkPageArchivesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FBE334297056EB00A2E4AD /* TalkPageArchivesFetcher.swift */; }; + 67FBE338297056EB00A2E4AD /* TalkPageArchivesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FBE334297056EB00A2E4AD /* TalkPageArchivesFetcher.swift */; }; + 67FBE33A29705FC200A2E4AD /* TalkPageArchivesItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FBE33929705FC200A2E4AD /* TalkPageArchivesItem.swift */; }; + 67FBE33B29705FC200A2E4AD /* TalkPageArchivesItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FBE33929705FC200A2E4AD /* TalkPageArchivesItem.swift */; }; + 67FBE33C29705FC200A2E4AD /* TalkPageArchivesItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FBE33929705FC200A2E4AD /* TalkPageArchivesItem.swift */; }; + 67FBE33D29705FC200A2E4AD /* TalkPageArchivesItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FBE33929705FC200A2E4AD /* TalkPageArchivesItem.swift */; }; + 67FF9C6B28076ADA000963D1 /* NSError+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FF9C6A28076ADA000963D1 /* NSError+Utilities.swift */; }; + 7004A5BA268CEE680029C46B /* MetricsClientBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7004A5B9268CEE680029C46B /* MetricsClientBridge.swift */; }; + 702096B9256C3D5700E27041 /* SamplingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 702096B8256C3D5700E27041 /* SamplingController.swift */; }; + 70B798142575714100C10BCA /* EventPlatformEvents.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 70B798122575714100C10BCA /* EventPlatformEvents.xcdatamodeld */; }; + 70B79820257577B800C10BCA /* StorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70B7981F257577B800C10BCA /* StorageManager.swift */; }; + 70B7982B25758E6D00C10BCA /* EPEventRecord+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70B7982A25758E6D00C10BCA /* EPEventRecord+CoreDataClass.swift */; }; + 70B7983625758EB800C10BCA /* EPEventRecord+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70B7983525758EB800C10BCA /* EPEventRecord+CoreDataProperties.swift */; }; + 7A00D177208FB72900A9C7BA /* BatchEditToolbarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A00D16C208FB61200A9C7BA /* BatchEditToolbarViewController.xib */; }; + 7A0161B41FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0161B31FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift */; }; + 7A0161B51FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0161B31FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift */; }; + 7A0161B61FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0161B31FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift */; }; + 7A0161B71FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0161B31FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift */; }; + 7A0161E01FE8B4CA00AEDC3D /* TagCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0161DF1FE8B4CA00AEDC3D /* TagCollectionViewCell.swift */; }; + 7A0161E11FE8B4CA00AEDC3D /* TagCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0161DF1FE8B4CA00AEDC3D /* TagCollectionViewCell.swift */; }; + 7A0161E21FE8B4CA00AEDC3D /* TagCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0161DF1FE8B4CA00AEDC3D /* TagCollectionViewCell.swift */; }; + 7A0161E31FE8B4CA00AEDC3D /* TagCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0161DF1FE8B4CA00AEDC3D /* TagCollectionViewCell.swift */; }; + 7A0312F72153C4990095C953 /* RemoteNotificationsModelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0312F62153C4990095C953 /* RemoteNotificationsModelController.swift */; }; + 7A0312F92153DEB30095C953 /* RemoteNotificationsAPIController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0312F82153DEB30095C953 /* RemoteNotificationsAPIController.swift */; }; + 7A0312FB215402FD0095C953 /* RemoteNotificationsImportOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0312FA215402FD0095C953 /* RemoteNotificationsImportOperation.swift */; }; + 7A0312FF215422960095C953 /* RemoteNotificationsMarkReadOrUnreadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0312FE215422960095C953 /* RemoteNotificationsMarkReadOrUnreadOperation.swift */; }; + 7A03130321542F5C0095C953 /* RemoteNotificationsOperationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A03130221542F5C0095C953 /* RemoteNotificationsOperationsController.swift */; }; + 7A06020E20EAAF5A00FBB71D /* ExploreFeedPreferencesUpdateCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A06020D20EAAF5A00FBB71D /* ExploreFeedPreferencesUpdateCoordinator.swift */; }; + 7A0CD24021DFA34100066F68 /* TextFormattingToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0CD23F21DFA34000066F68 /* TextFormattingToolbarView.swift */; }; + 7A0CD24121DFA34100066F68 /* TextFormattingToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0CD23F21DFA34000066F68 /* TextFormattingToolbarView.swift */; }; + 7A0CD24221DFA34100066F68 /* TextFormattingToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0CD23F21DFA34000066F68 /* TextFormattingToolbarView.swift */; }; + 7A0CD24321DFA34100066F68 /* TextFormattingToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0CD23F21DFA34000066F68 /* TextFormattingToolbarView.swift */; }; + 7A0DE50020CEEC760032AB57 /* ExploreFeedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0DE4FE20CEEC760032AB57 /* ExploreFeedSettingsViewController.swift */; }; + 7A0DE50120CEEC760032AB57 /* ExploreFeedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0DE4FE20CEEC760032AB57 /* ExploreFeedSettingsViewController.swift */; }; + 7A0DE50220CEEC760032AB57 /* ExploreFeedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0DE4FE20CEEC760032AB57 /* ExploreFeedSettingsViewController.swift */; }; + 7A0DE50320CEEC760032AB57 /* ExploreFeedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0DE4FE20CEEC760032AB57 /* ExploreFeedSettingsViewController.swift */; }; + 7A0F2589217221D10028871B /* RepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0F2588217221D10028871B /* RepeatingTimer.swift */; }; + 7A0FF2CC230343BA00E755D4 /* PageHistoryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0FF2CB230343BA00E755D4 /* PageHistoryCollectionViewCell.swift */; }; + 7A0FF2CD230343BA00E755D4 /* PageHistoryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0FF2CB230343BA00E755D4 /* PageHistoryCollectionViewCell.swift */; }; + 7A0FF2CE230343BA00E755D4 /* PageHistoryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0FF2CB230343BA00E755D4 /* PageHistoryCollectionViewCell.swift */; }; + 7A0FF2CF230343BA00E755D4 /* PageHistoryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0FF2CB230343BA00E755D4 /* PageHistoryCollectionViewCell.swift */; }; + 7A13A8992028BB3600F28254 /* ReadingListsAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A13A8982028BB3600F28254 /* ReadingListsAlertController.swift */; }; + 7A13A89A2028BB3600F28254 /* ReadingListsAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A13A8982028BB3600F28254 /* ReadingListsAlertController.swift */; }; + 7A13A89B2028BB3600F28254 /* ReadingListsAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A13A8982028BB3600F28254 /* ReadingListsAlertController.swift */; }; + 7A13A89C2028BB3600F28254 /* ReadingListsAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A13A8982028BB3600F28254 /* ReadingListsAlertController.swift */; }; + 7A1469BD220BBE44000A20F1 /* EditHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1469BC220BBE44000A20F1 /* EditHintViewController.swift */; }; + 7A1469BE220BBE44000A20F1 /* EditHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1469BC220BBE44000A20F1 /* EditHintViewController.swift */; }; + 7A1469BF220BBE44000A20F1 /* EditHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1469BC220BBE44000A20F1 /* EditHintViewController.swift */; }; + 7A1469C0220BBE44000A20F1 /* EditHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1469BC220BBE44000A20F1 /* EditHintViewController.swift */; }; + 7A1469C5220BC223000A20F1 /* EditHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1469C4220BC223000A20F1 /* EditHintController.swift */; }; + 7A1469C6220BC223000A20F1 /* EditHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1469C4220BC223000A20F1 /* EditHintController.swift */; }; + 7A1469C7220BC223000A20F1 /* EditHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1469C4220BC223000A20F1 /* EditHintController.swift */; }; + 7A1469C8220BC223000A20F1 /* EditHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1469C4220BC223000A20F1 /* EditHintController.swift */; }; + 7A16C4E6212D941C00F0D5EC /* SubSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A16C4E4212D941C00F0D5EC /* SubSettingsViewController.swift */; }; + 7A16C4E7212D941C00F0D5EC /* SubSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A16C4E4212D941C00F0D5EC /* SubSettingsViewController.swift */; }; + 7A16C4E8212D941C00F0D5EC /* SubSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A16C4E4212D941C00F0D5EC /* SubSettingsViewController.swift */; }; + 7A16C4E9212D941C00F0D5EC /* SubSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A16C4E4212D941C00F0D5EC /* SubSettingsViewController.swift */; }; + 7A16C4EB212D941C00F0D5EC /* SubSettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A16C4E5212D941C00F0D5EC /* SubSettingsViewController.xib */; }; + 7A16C4EC212D941C00F0D5EC /* SubSettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A16C4E5212D941C00F0D5EC /* SubSettingsViewController.xib */; }; + 7A16C4ED212D941C00F0D5EC /* SubSettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A16C4E5212D941C00F0D5EC /* SubSettingsViewController.xib */; }; + 7A16C4EE212D941C00F0D5EC /* SubSettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A16C4E5212D941C00F0D5EC /* SubSettingsViewController.xib */; }; + 7A196F5A21BF199500D9E4B5 /* SectionEditorWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A196F5921BF199500D9E4B5 /* SectionEditorWebView.swift */; }; + 7A196F5B21BF199500D9E4B5 /* SectionEditorWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A196F5921BF199500D9E4B5 /* SectionEditorWebView.swift */; }; + 7A196F5C21BF199500D9E4B5 /* SectionEditorWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A196F5921BF199500D9E4B5 /* SectionEditorWebView.swift */; }; + 7A196F5D21BF199500D9E4B5 /* SectionEditorWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A196F5921BF199500D9E4B5 /* SectionEditorWebView.swift */; }; + 7A19C64820DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A19C64720DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift */; }; + 7A19C64920DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A19C64720DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift */; }; + 7A19C64A20DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A19C64720DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift */; }; + 7A19C64B20DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A19C64720DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift */; }; + 7A1C498F227254EC00230ED2 /* InsertMediaSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1C498E227254EC00230ED2 /* InsertMediaSearchViewController.swift */; }; + 7A1C4990227254EC00230ED2 /* InsertMediaSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1C498E227254EC00230ED2 /* InsertMediaSearchViewController.swift */; }; + 7A1C4991227254EC00230ED2 /* InsertMediaSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1C498E227254EC00230ED2 /* InsertMediaSearchViewController.swift */; }; + 7A1C4992227254EC00230ED2 /* InsertMediaSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1C498E227254EC00230ED2 /* InsertMediaSearchViewController.swift */; }; + 7A1C4995227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1C4994227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift */; }; + 7A1C4996227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1C4994227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift */; }; + 7A1C4997227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1C4994227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift */; }; + 7A1C4998227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1C4994227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift */; }; + 7A203F0B1FDEDCDD00A229EC /* ReadingListHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A203F091FDEDCDD00A229EC /* ReadingListHintController.swift */; }; + 7A203F0C1FDEDCDD00A229EC /* ReadingListHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A203F091FDEDCDD00A229EC /* ReadingListHintController.swift */; }; + 7A203F0D1FDEDCDD00A229EC /* ReadingListHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A203F091FDEDCDD00A229EC /* ReadingListHintController.swift */; }; + 7A203F0E1FDEDCDD00A229EC /* ReadingListHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A203F091FDEDCDD00A229EC /* ReadingListHintController.swift */; }; + 7A20AE082057F39C005FB5DF /* UIView+Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A20AE072057F39C005FB5DF /* UIView+Identifier.swift */; }; + 7A20AE092057F39C005FB5DF /* UIView+Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A20AE072057F39C005FB5DF /* UIView+Identifier.swift */; }; + 7A20AE0A2057F39C005FB5DF /* UIView+Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A20AE072057F39C005FB5DF /* UIView+Identifier.swift */; }; + 7A20AE0B2057F39C005FB5DF /* UIView+Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A20AE072057F39C005FB5DF /* UIView+Identifier.swift */; }; + 7A23CECF211A24FC00441A79 /* FeedFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0501BBC2110ED8800020BFA /* FeedFunnel.swift */; }; + 7A23CED1211A24FD00441A79 /* FeedFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0501BBC2110ED8800020BFA /* FeedFunnel.swift */; }; + 7A23CED2211A24FE00441A79 /* FeedFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0501BBC2110ED8800020BFA /* FeedFunnel.swift */; }; + 7A23CED3211A24FF00441A79 /* FeedFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0501BBC2110ED8800020BFA /* FeedFunnel.swift */; }; + 7A2432BE1FCF401900FB4BA5 /* CreateReadingListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2432BC1FCF401900FB4BA5 /* CreateReadingListViewController.swift */; }; + 7A2432BF1FCF401900FB4BA5 /* CreateReadingListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2432BC1FCF401900FB4BA5 /* CreateReadingListViewController.swift */; }; + 7A2432C01FCF401900FB4BA5 /* CreateReadingListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2432BC1FCF401900FB4BA5 /* CreateReadingListViewController.swift */; }; + 7A2432C11FCF401900FB4BA5 /* CreateReadingListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2432BC1FCF401900FB4BA5 /* CreateReadingListViewController.swift */; }; + 7A2432C31FCF401900FB4BA5 /* CreateReadingListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A2432BD1FCF401900FB4BA5 /* CreateReadingListViewController.xib */; }; + 7A2432C41FCF401900FB4BA5 /* CreateReadingListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A2432BD1FCF401900FB4BA5 /* CreateReadingListViewController.xib */; }; + 7A2432C51FCF401900FB4BA5 /* CreateReadingListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A2432BD1FCF401900FB4BA5 /* CreateReadingListViewController.xib */; }; + 7A2432C61FCF401900FB4BA5 /* CreateReadingListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A2432BD1FCF401900FB4BA5 /* CreateReadingListViewController.xib */; }; + 7A2432ED1FCF469100FB4BA5 /* SetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2432EC1FCF469100FB4BA5 /* SetupView.swift */; }; + 7A25367721B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A25367521B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift */; }; + 7A25367821B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A25367521B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift */; }; + 7A25367921B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A25367521B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift */; }; + 7A25367A21B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A25367521B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift */; }; + 7A25367C21B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A25367621B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib */; }; + 7A25367D21B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A25367621B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib */; }; + 7A25367E21B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A25367621B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib */; }; + 7A25367F21B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A25367621B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib */; }; + 7A27E85221B19767001B2D21 /* TextStyleFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27E85121B19767001B2D21 /* TextStyleFormattingTableViewController.swift */; }; + 7A27E85321B19767001B2D21 /* TextStyleFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27E85121B19767001B2D21 /* TextStyleFormattingTableViewController.swift */; }; + 7A27E85421B19767001B2D21 /* TextStyleFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27E85121B19767001B2D21 /* TextStyleFormattingTableViewController.swift */; }; + 7A27E85521B19767001B2D21 /* TextStyleFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27E85121B19767001B2D21 /* TextStyleFormattingTableViewController.swift */; }; + 7A27EDA22279F5270010CB24 /* InsertLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27EDA12279F5270010CB24 /* InsertLinkViewController.swift */; }; + 7A27EDA32279F5270010CB24 /* InsertLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27EDA12279F5270010CB24 /* InsertLinkViewController.swift */; }; + 7A27EDA42279F5270010CB24 /* InsertLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27EDA12279F5270010CB24 /* InsertLinkViewController.swift */; }; + 7A27EDA52279F5270010CB24 /* InsertLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27EDA12279F5270010CB24 /* InsertLinkViewController.swift */; }; + 7A28126320D3F84A009B42B5 /* FeedCardSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28126120D3F84A009B42B5 /* FeedCardSettingsViewController.swift */; }; + 7A28126420D3F84A009B42B5 /* FeedCardSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28126120D3F84A009B42B5 /* FeedCardSettingsViewController.swift */; }; + 7A28126520D3F84A009B42B5 /* FeedCardSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28126120D3F84A009B42B5 /* FeedCardSettingsViewController.swift */; }; + 7A28126620D3F84A009B42B5 /* FeedCardSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28126120D3F84A009B42B5 /* FeedCardSettingsViewController.swift */; }; + 7A29A5C81F6C405900E8F42B /* HistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A29A5C71F6C405900E8F42B /* HistoryViewController.swift */; }; + 7A29A5C91F6C405900E8F42B /* HistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A29A5C71F6C405900E8F42B /* HistoryViewController.swift */; }; + 7A29A5CA1F6C405900E8F42B /* HistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A29A5C71F6C405900E8F42B /* HistoryViewController.swift */; }; + 7A29A5CB1F6C405900E8F42B /* HistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A29A5C71F6C405900E8F42B /* HistoryViewController.swift */; }; + 7A29A5CE1F6C49C600E8F42B /* CollectionViewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A29A5CD1F6C49C600E8F42B /* CollectionViewUpdater.swift */; }; + 7A29A5CF1F6C49C600E8F42B /* CollectionViewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A29A5CD1F6C49C600E8F42B /* CollectionViewUpdater.swift */; }; + 7A29A5D01F6C49C600E8F42B /* CollectionViewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A29A5CD1F6C49C600E8F42B /* CollectionViewUpdater.swift */; }; + 7A29A5D11F6C49C600E8F42B /* CollectionViewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A29A5CD1F6C49C600E8F42B /* CollectionViewUpdater.swift */; }; + 7A29A5D31F6C4B9C00E8F42B /* WMFChange.h in Headers */ = {isa = PBXBuildFile; fileRef = D8FEECCB1DE3729400B883F0 /* WMFChange.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7A2BB1D421F27AC5004C0FDF /* WKWebView+OffsetHack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2BB1D321F27AC5004C0FDF /* WKWebView+OffsetHack.swift */; }; + 7A2BB1D521F27AC5004C0FDF /* WKWebView+OffsetHack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2BB1D321F27AC5004C0FDF /* WKWebView+OffsetHack.swift */; }; + 7A2BB1D621F27AC5004C0FDF /* WKWebView+OffsetHack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2BB1D321F27AC5004C0FDF /* WKWebView+OffsetHack.swift */; }; + 7A2BB1D721F27AC5004C0FDF /* WKWebView+OffsetHack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2BB1D321F27AC5004C0FDF /* WKWebView+OffsetHack.swift */; }; + 7A2FE5562051757E00F92F8F /* EraseSavedArticlesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A2FE5552051757E00F92F8F /* EraseSavedArticlesView.xib */; }; + 7A2FE5572051757E00F92F8F /* EraseSavedArticlesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A2FE5552051757E00F92F8F /* EraseSavedArticlesView.xib */; }; + 7A2FE5582051757E00F92F8F /* EraseSavedArticlesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A2FE5552051757E00F92F8F /* EraseSavedArticlesView.xib */; }; + 7A2FE5592051757E00F92F8F /* EraseSavedArticlesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A2FE5552051757E00F92F8F /* EraseSavedArticlesView.xib */; }; + 7A2FE55C20517BAE00F92F8F /* EraseSavedArticlesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2FE55B20517BAE00F92F8F /* EraseSavedArticlesView.swift */; }; + 7A2FE55D20517BAE00F92F8F /* EraseSavedArticlesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2FE55B20517BAE00F92F8F /* EraseSavedArticlesView.swift */; }; + 7A2FE55E20517BAE00F92F8F /* EraseSavedArticlesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2FE55B20517BAE00F92F8F /* EraseSavedArticlesView.swift */; }; + 7A2FE55F20517BAE00F92F8F /* EraseSavedArticlesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2FE55B20517BAE00F92F8F /* EraseSavedArticlesView.swift */; }; + 7A3159CF206458B000143119 /* ReadingListAlertType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3159CE206458B000143119 /* ReadingListAlertType.swift */; }; + 7A32078821E40193009E1677 /* SectionEditorMenuItemsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A32078721E40193009E1677 /* SectionEditorMenuItemsController.swift */; }; + 7A32078921E40193009E1677 /* SectionEditorMenuItemsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A32078721E40193009E1677 /* SectionEditorMenuItemsController.swift */; }; + 7A32078A21E40193009E1677 /* SectionEditorMenuItemsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A32078721E40193009E1677 /* SectionEditorMenuItemsController.swift */; }; + 7A32078B21E40193009E1677 /* SectionEditorMenuItemsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A32078721E40193009E1677 /* SectionEditorMenuItemsController.swift */; }; + 7A35CB871FD82B6300AAF3B7 /* ReadingListDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A35CB861FD82B6300AAF3B7 /* ReadingListDetailViewController.swift */; }; + 7A35CB881FD82B6300AAF3B7 /* ReadingListDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A35CB861FD82B6300AAF3B7 /* ReadingListDetailViewController.swift */; }; + 7A35CB891FD82B6300AAF3B7 /* ReadingListDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A35CB861FD82B6300AAF3B7 /* ReadingListDetailViewController.swift */; }; + 7A35CB8A1FD82B6300AAF3B7 /* ReadingListDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A35CB861FD82B6300AAF3B7 /* ReadingListDetailViewController.swift */; }; + 7A393281236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A39327F236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift */; }; + 7A393282236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A39327F236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift */; }; + 7A393283236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A39327F236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift */; }; + 7A393284236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A39327F236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift */; }; + 7A393286236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A393280236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib */; }; + 7A393287236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A393280236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib */; }; + 7A393288236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A393280236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib */; }; + 7A393289236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A393280236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib */; }; + 7A3AD05820ADB1A900C92E04 /* WMFAuthenticationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0ED173E1E4CF3AF008B70AD /* WMFAuthenticationManager.swift */; }; + 7A3AD05920ADB1BD00C92E04 /* WMFAuthLoginInfoFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F92C7F1E3FFEA100B72802 /* WMFAuthLoginInfoFetcher.swift */; }; + 7A3AD05A20ADB1CD00C92E04 /* WMFAccountLogin.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F92C5F1E3A813500B72802 /* WMFAccountLogin.swift */; }; + 7A3AD05B20ADB1DF00C92E04 /* WMFCurrentlyLoggedInUserFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0ED173A1E497AE7008B70AD /* WMFCurrentlyLoggedInUserFetcher.swift */; }; + 7A3AD05C20ADB1F500C92E04 /* WMFKeychainCredentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = B066F0D11E4F00B100A199F8 /* WMFKeychainCredentials.swift */; }; + 7A420DB422A029780005689B /* EditFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A420DB322A029780005689B /* EditFunnel.swift */; }; + 7A420DB522A029780005689B /* EditFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A420DB322A029780005689B /* EditFunnel.swift */; }; + 7A420DB622A029780005689B /* EditFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A420DB322A029780005689B /* EditFunnel.swift */; }; + 7A420DB722A029780005689B /* EditFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A420DB322A029780005689B /* EditFunnel.swift */; }; + 7A45AB8020AB2A4C006A92F5 /* Dictionary+Equality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A45AB7F20AB2A4C006A92F5 /* Dictionary+Equality.swift */; }; + 7A48EA0E21B5C9B20083F3DC /* EditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A48EA0D21B5C9B20083F3DC /* EditToolbarView.swift */; }; + 7A48EA0F21B5C9B20083F3DC /* EditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A48EA0D21B5C9B20083F3DC /* EditToolbarView.swift */; }; + 7A48EA1021B5C9B20083F3DC /* EditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A48EA0D21B5C9B20083F3DC /* EditToolbarView.swift */; }; + 7A48EA1121B5C9B20083F3DC /* EditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A48EA0D21B5C9B20083F3DC /* EditToolbarView.swift */; }; + 7A49A20121231510005C574C /* CollectionViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49A20021231510005C574C /* CollectionViewFooter.swift */; }; + 7A49A20221231510005C574C /* CollectionViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49A20021231510005C574C /* CollectionViewFooter.swift */; }; + 7A49A20321231510005C574C /* CollectionViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49A20021231510005C574C /* CollectionViewFooter.swift */; }; + 7A49A20421231510005C574C /* CollectionViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49A20021231510005C574C /* CollectionViewFooter.swift */; }; + 7A4B333C2136EDED00C6C820 /* UnderlineButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4B333B2136EDED00C6C820 /* UnderlineButton.swift */; }; + 7A4B333D2136EDED00C6C820 /* UnderlineButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4B333B2136EDED00C6C820 /* UnderlineButton.swift */; }; + 7A4B333E2136EDED00C6C820 /* UnderlineButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4B333B2136EDED00C6C820 /* UnderlineButton.swift */; }; + 7A4B333F2136EDED00C6C820 /* UnderlineButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4B333B2136EDED00C6C820 /* UnderlineButton.swift */; }; + 7A4D227D21B1CD8600D889BD /* TextFormattingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4D227B21B1CD8600D889BD /* TextFormattingTableViewCell.swift */; }; + 7A4D227E21B1CD8600D889BD /* TextFormattingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4D227B21B1CD8600D889BD /* TextFormattingTableViewCell.swift */; }; + 7A4D227F21B1CD8600D889BD /* TextFormattingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4D227B21B1CD8600D889BD /* TextFormattingTableViewCell.swift */; }; + 7A4D228021B1CD8600D889BD /* TextFormattingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4D227B21B1CD8600D889BD /* TextFormattingTableViewCell.swift */; }; + 7A4FE53F1FA00AEF009FA199 /* ArticlePeekPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A77B7801F9FCB2500753FF5 /* ArticlePeekPreviewViewController.swift */; }; + 7A4FE5401FA00AEF009FA199 /* ArticlePeekPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A77B7801F9FCB2500753FF5 /* ArticlePeekPreviewViewController.swift */; }; + 7A4FE5411FA00AF0009FA199 /* ArticlePeekPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A77B7801F9FCB2500753FF5 /* ArticlePeekPreviewViewController.swift */; }; + 7A4FE5421FA00AF1009FA199 /* ArticlePeekPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A77B7801F9FCB2500753FF5 /* ArticlePeekPreviewViewController.swift */; }; + 7A52C01B2150389D00A3A4A1 /* RemoteNotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A52C01A2150389D00A3A4A1 /* RemoteNotificationsController.swift */; }; + 7A5357AB215552E7007998DC /* RemoteNotificationsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5357AA215552E7007998DC /* RemoteNotificationsOperation.swift */; }; + 7A5A0543225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5A0542225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift */; }; + 7A5A0544225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5A0542225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift */; }; + 7A5A0545225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5A0542225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift */; }; + 7A5A0546225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5A0542225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift */; }; + 7A5A2777206D288C004CC837 /* NSFileManager+DirectorySize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5A2776206D288C004CC837 /* NSFileManager+DirectorySize.swift */; }; + 7A5AB82C22940D8500B91C9C /* WMFHTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A5AB82B22940D8500B91C9C /* WMFHTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7A5AB82D2294121D00B91C9C /* WMFHTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A5AB82522940CE200B91C9C /* WMFHTMLElement.m */; }; + 7A610CB7220A30C900C266AE /* HintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A610CB6220A30C900C266AE /* HintViewController.swift */; }; + 7A610CB8220A30C900C266AE /* HintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A610CB6220A30C900C266AE /* HintViewController.swift */; }; + 7A610CB9220A30C900C266AE /* HintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A610CB6220A30C900C266AE /* HintViewController.swift */; }; + 7A610CBA220A30C900C266AE /* HintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A610CB6220A30C900C266AE /* HintViewController.swift */; }; + 7A610CBD220A582A00C266AE /* HintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A610CBC220A582A00C266AE /* HintController.swift */; }; + 7A610CBE220A582A00C266AE /* HintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A610CBC220A582A00C266AE /* HintController.swift */; }; + 7A610CBF220A582A00C266AE /* HintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A610CBC220A582A00C266AE /* HintController.swift */; }; + 7A610CC0220A582A00C266AE /* HintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A610CBC220A582A00C266AE /* HintController.swift */; }; + 7A630F71217A400200FC93FC /* Array+Chunked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A630F6B217A3FB100FC93FC /* Array+Chunked.swift */; }; + 7A65A5DC20ACFD98001170B8 /* WMFContentGroup+EventLogging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A07A46720AA3F5100F7B2BB /* WMFContentGroup+EventLogging.swift */; }; + 7A65A5DD20ACFDB6001170B8 /* EventLoggingStandardEventProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A79A39220A24A7C00F9BDF9 /* EventLoggingStandardEventProviding.swift */; }; + 7A6CA28E2289AF2200C7FD47 /* EditLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6CA28C2289AF2200C7FD47 /* EditLinkViewController.swift */; }; + 7A6CA28F2289AF2200C7FD47 /* EditLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6CA28C2289AF2200C7FD47 /* EditLinkViewController.swift */; }; + 7A6CA2902289AF2200C7FD47 /* EditLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6CA28C2289AF2200C7FD47 /* EditLinkViewController.swift */; }; + 7A6CA2912289AF2200C7FD47 /* EditLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6CA28C2289AF2200C7FD47 /* EditLinkViewController.swift */; }; + 7A6CA2932289AF2200C7FD47 /* EditLinkViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A6CA28D2289AF2200C7FD47 /* EditLinkViewController.xib */; }; + 7A6CA2942289AF2200C7FD47 /* EditLinkViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A6CA28D2289AF2200C7FD47 /* EditLinkViewController.xib */; }; + 7A6CA2952289AF2200C7FD47 /* EditLinkViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A6CA28D2289AF2200C7FD47 /* EditLinkViewController.xib */; }; + 7A6CA2962289AF2200C7FD47 /* EditLinkViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A6CA28D2289AF2200C7FD47 /* EditLinkViewController.xib */; }; + 7A6ED50A20ADBF950001849F /* SessionsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A07A46D20AA482C00F7B2BB /* SessionsFunnel.swift */; }; + 7A6ED50C20ADBF950001849F /* SessionsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A07A46D20AA482C00F7B2BB /* SessionsFunnel.swift */; }; + 7A6ED50D20ADBF950001849F /* SessionsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A07A46D20AA482C00F7B2BB /* SessionsFunnel.swift */; }; + 7A6ED50E20ADBF950001849F /* SessionsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A07A46D20AA482C00F7B2BB /* SessionsFunnel.swift */; }; + 7A6ED50F20ADBF950001849F /* ReadingListsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF1B19520A1E14F000C8DFE /* ReadingListsFunnel.swift */; }; + 7A6ED51120ADBF950001849F /* ReadingListsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF1B19520A1E14F000C8DFE /* ReadingListsFunnel.swift */; }; + 7A6ED51220ADBF950001849F /* ReadingListsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF1B19520A1E14F000C8DFE /* ReadingListsFunnel.swift */; }; + 7A6ED51320ADBF950001849F /* ReadingListsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF1B19520A1E14F000C8DFE /* ReadingListsFunnel.swift */; }; + 7A6ED51420ADBF950001849F /* UserHistoryFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4ABA8520AA8966007AA405 /* UserHistoryFunnel.swift */; }; + 7A6ED51620ADBF950001849F /* UserHistoryFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4ABA8520AA8966007AA405 /* UserHistoryFunnel.swift */; }; + 7A6ED51720ADBF950001849F /* UserHistoryFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4ABA8520AA8966007AA405 /* UserHistoryFunnel.swift */; }; + 7A6ED51820ADBF950001849F /* UserHistoryFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4ABA8520AA8966007AA405 /* UserHistoryFunnel.swift */; }; + 7A6ED51920ADBF950001849F /* SettingsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A00E0BF20A9E54400F033C8 /* SettingsFunnel.swift */; }; + 7A6ED51B20ADBF950001849F /* SettingsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A00E0BF20A9E54400F033C8 /* SettingsFunnel.swift */; }; + 7A6ED51C20ADBF950001849F /* SettingsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A00E0BF20A9E54400F033C8 /* SettingsFunnel.swift */; }; + 7A6ED51D20ADBF950001849F /* SettingsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A00E0BF20A9E54400F033C8 /* SettingsFunnel.swift */; }; + 7A6ED51E20ADBF950001849F /* LoginFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A25B1CB20A483F1008C6F29 /* LoginFunnel.swift */; }; + 7A6ED52020ADBF950001849F /* LoginFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A25B1CB20A483F1008C6F29 /* LoginFunnel.swift */; }; + 7A6ED52120ADBF950001849F /* LoginFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A25B1CB20A483F1008C6F29 /* LoginFunnel.swift */; }; + 7A6ED52220ADBF950001849F /* LoginFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A25B1CB20A483F1008C6F29 /* LoginFunnel.swift */; }; + 7A6F55FF21AF508B0076D184 /* TextFormatting.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A6F55FE21AF508B0076D184 /* TextFormatting.storyboard */; }; + 7A6F560021AF508B0076D184 /* TextFormatting.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A6F55FE21AF508B0076D184 /* TextFormatting.storyboard */; }; + 7A6F560121AF508B0076D184 /* TextFormatting.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A6F55FE21AF508B0076D184 /* TextFormatting.storyboard */; }; + 7A6F560221AF508B0076D184 /* TextFormatting.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A6F55FE21AF508B0076D184 /* TextFormatting.storyboard */; }; + 7A6F560521AF527A0076D184 /* TextFormattingInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F560421AF527A0076D184 /* TextFormattingInputViewController.swift */; }; + 7A6F560621AF527A0076D184 /* TextFormattingInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F560421AF527A0076D184 /* TextFormattingInputViewController.swift */; }; + 7A6F560721AF527A0076D184 /* TextFormattingInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F560421AF527A0076D184 /* TextFormattingInputViewController.swift */; }; + 7A6F560821AF527A0076D184 /* TextFormattingInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F560421AF527A0076D184 /* TextFormattingInputViewController.swift */; }; + 7A70797D223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A70797B223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift */; }; + 7A70797E223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A70797B223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift */; }; + 7A70797F223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A70797B223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift */; }; + 7A707980223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A70797B223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift */; }; + 7A707982223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A70797C223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib */; }; + 7A707983223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A70797C223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib */; }; + 7A707984223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A70797C223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib */; }; + 7A707985223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A70797C223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib */; }; + 7A71565B226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71565A226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift */; }; + 7A71565C226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71565A226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift */; }; + 7A71565D226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71565A226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift */; }; + 7A71565E226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71565A226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift */; }; + 7A715661226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A715660226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift */; }; + 7A715662226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A715660226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift */; }; + 7A715663226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A715660226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift */; }; + 7A715664226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A715660226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift */; }; + 7A715667226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A715666226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift */; }; + 7A715668226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A715666226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift */; }; + 7A715669226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A715666226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift */; }; + 7A71566A226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A715666226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift */; }; + 7A71566E22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71566C22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift */; }; + 7A71566F22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71566C22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift */; }; + 7A71567022697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71566C22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift */; }; + 7A71567122697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71566C22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift */; }; + 7A71567322697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A71566D22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib */; }; + 7A71567422697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A71566D22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib */; }; + 7A71567522697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A71566D22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib */; }; + 7A71567622697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A71566D22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib */; }; + 7A71567922699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71567822699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift */; }; + 7A71567A22699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71567822699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift */; }; + 7A71567B22699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71567822699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift */; }; + 7A71567C22699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71567822699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift */; }; + 7A73B48221E54B4200249E09 /* SectionEditorNavigationItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A73B48121E54B4200249E09 /* SectionEditorNavigationItemController.swift */; }; + 7A73B48321E54B4200249E09 /* SectionEditorNavigationItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A73B48121E54B4200249E09 /* SectionEditorNavigationItemController.swift */; }; + 7A73B48421E54B4200249E09 /* SectionEditorNavigationItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A73B48121E54B4200249E09 /* SectionEditorNavigationItemController.swift */; }; + 7A73B48521E54B4200249E09 /* SectionEditorNavigationItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A73B48121E54B4200249E09 /* SectionEditorNavigationItemController.swift */; }; + 7A741DCA207FB9CC00CBAAE2 /* SearchBarExtendedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A741DC8207FB9CB00CBAAE2 /* SearchBarExtendedViewController.swift */; }; + 7A741DCB207FB9CC00CBAAE2 /* SearchBarExtendedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A741DC8207FB9CB00CBAAE2 /* SearchBarExtendedViewController.swift */; }; + 7A741DCC207FB9CC00CBAAE2 /* SearchBarExtendedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A741DC8207FB9CB00CBAAE2 /* SearchBarExtendedViewController.swift */; }; + 7A741DCD207FB9CC00CBAAE2 /* SearchBarExtendedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A741DC8207FB9CB00CBAAE2 /* SearchBarExtendedViewController.swift */; }; + 7A741DCF207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A741DC9207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib */; }; + 7A741DD0207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A741DC9207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib */; }; + 7A741DD1207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A741DC9207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib */; }; + 7A741DD2207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A741DC9207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib */; }; + 7A79CCF2200C2C850099B01F /* BatchEditToolbarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A79CCE6200C29A10099B01F /* BatchEditToolbarViewController.swift */; }; + 7A7AC84621B6B89B003B849B /* SectionEditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AC84521B6B89B003B849B /* SectionEditorViewController.swift */; }; + 7A7AC84721B6B89B003B849B /* SectionEditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AC84521B6B89B003B849B /* SectionEditorViewController.swift */; }; + 7A7AC84821B6B89B003B849B /* SectionEditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AC84521B6B89B003B849B /* SectionEditorViewController.swift */; }; + 7A7AC84921B6B89B003B849B /* SectionEditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AC84521B6B89B003B849B /* SectionEditorViewController.swift */; }; + 7A827659226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A827657226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift */; }; + 7A82765A226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A827657226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift */; }; + 7A82765B226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A827657226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift */; }; + 7A82765C226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A827657226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift */; }; + 7A82765E226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A827658226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib */; }; + 7A82765F226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A827658226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib */; }; + 7A827660226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A827658226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib */; }; + 7A827661226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A827658226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib */; }; + 7A82896821B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A82896621B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift */; }; + 7A82896921B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A82896621B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift */; }; + 7A82896A21B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A82896621B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift */; }; + 7A82896B21B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A82896621B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift */; }; + 7A82898C21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A82898A21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift */; }; + 7A82898D21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A82898A21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift */; }; + 7A82898E21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A82898A21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift */; }; + 7A82898F21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A82898A21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift */; }; + 7A8422472268BBE70074648E /* InsertMediaImageInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8422452268BBE70074648E /* InsertMediaImageInfoView.swift */; }; + 7A8422482268BBE70074648E /* InsertMediaImageInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8422452268BBE70074648E /* InsertMediaImageInfoView.swift */; }; + 7A8422492268BBE70074648E /* InsertMediaImageInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8422452268BBE70074648E /* InsertMediaImageInfoView.swift */; }; + 7A84224A2268BBE70074648E /* InsertMediaImageInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8422452268BBE70074648E /* InsertMediaImageInfoView.swift */; }; + 7A84224C2268BBE70074648E /* InsertMediaImageInfoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A8422462268BBE70074648E /* InsertMediaImageInfoView.xib */; }; + 7A84224D2268BBE70074648E /* InsertMediaImageInfoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A8422462268BBE70074648E /* InsertMediaImageInfoView.xib */; }; + 7A84224E2268BBE70074648E /* InsertMediaImageInfoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A8422462268BBE70074648E /* InsertMediaImageInfoView.xib */; }; + 7A84224F2268BBE70074648E /* InsertMediaImageInfoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A8422462268BBE70074648E /* InsertMediaImageInfoView.xib */; }; + 7A8422532268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8422512268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift */; }; + 7A8422542268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8422512268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift */; }; + 7A8422552268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8422512268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift */; }; + 7A8422562268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8422512268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift */; }; + 7A8422582268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A8422522268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib */; }; + 7A8422592268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A8422522268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib */; }; + 7A84225A2268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A8422522268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib */; }; + 7A84225B2268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A8422522268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib */; }; + 7A9133AA22B162E8002AEBCF /* RemoteNotifications.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7A9133A822B162E7002AEBCF /* RemoteNotifications.xcdatamodeld */; }; + 7A9524CB22665E6400C55CDC /* InsertMediaSettingsImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9524C922665E6400C55CDC /* InsertMediaSettingsImageView.swift */; }; + 7A9524CC22665E6400C55CDC /* InsertMediaSettingsImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9524C922665E6400C55CDC /* InsertMediaSettingsImageView.swift */; }; + 7A9524CD22665E6400C55CDC /* InsertMediaSettingsImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9524C922665E6400C55CDC /* InsertMediaSettingsImageView.swift */; }; + 7A9524CE22665E6400C55CDC /* InsertMediaSettingsImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9524C922665E6400C55CDC /* InsertMediaSettingsImageView.swift */; }; + 7A9524D022665E6400C55CDC /* InsertMediaSettingsImageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9524CA22665E6400C55CDC /* InsertMediaSettingsImageView.xib */; }; + 7A9524D122665E6400C55CDC /* InsertMediaSettingsImageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9524CA22665E6400C55CDC /* InsertMediaSettingsImageView.xib */; }; + 7A9524D222665E6400C55CDC /* InsertMediaSettingsImageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9524CA22665E6400C55CDC /* InsertMediaSettingsImageView.xib */; }; + 7A9524D322665E6400C55CDC /* InsertMediaSettingsImageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9524CA22665E6400C55CDC /* InsertMediaSettingsImageView.xib */; }; + 7A9524D722669A8B00C55CDC /* InsertMediaSettingsButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9524D522669A8B00C55CDC /* InsertMediaSettingsButtonView.swift */; }; + 7A9524D822669A8B00C55CDC /* InsertMediaSettingsButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9524D522669A8B00C55CDC /* InsertMediaSettingsButtonView.swift */; }; + 7A9524D922669A8B00C55CDC /* InsertMediaSettingsButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9524D522669A8B00C55CDC /* InsertMediaSettingsButtonView.swift */; }; + 7A9524DA22669A8B00C55CDC /* InsertMediaSettingsButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9524D522669A8B00C55CDC /* InsertMediaSettingsButtonView.swift */; }; + 7A9524DC22669A8B00C55CDC /* InsertMediaSettingsButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9524D622669A8B00C55CDC /* InsertMediaSettingsButtonView.xib */; }; + 7A9524DD22669A8B00C55CDC /* InsertMediaSettingsButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9524D622669A8B00C55CDC /* InsertMediaSettingsButtonView.xib */; }; + 7A9524DE22669A8B00C55CDC /* InsertMediaSettingsButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9524D622669A8B00C55CDC /* InsertMediaSettingsButtonView.xib */; }; + 7A9524DF22669A8B00C55CDC /* InsertMediaSettingsButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9524D622669A8B00C55CDC /* InsertMediaSettingsButtonView.xib */; }; + 7A96EBA922CFDA4B0037C8A8 /* PageNamespace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4170D8229EFC2A00251582 /* PageNamespace.swift */; }; + 7A998AC11FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A998AC01FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift */; }; + 7A998AC21FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A998AC01FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift */; }; + 7A998AC31FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A998AC01FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift */; }; + 7A998AC41FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A998AC01FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift */; }; + 7A9A611821124CF500403154 /* CreateNewReadingListButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9A611721124CF500403154 /* CreateNewReadingListButtonView.xib */; }; + 7A9A611921124CF500403154 /* CreateNewReadingListButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9A611721124CF500403154 /* CreateNewReadingListButtonView.xib */; }; + 7A9A611A21124CF500403154 /* CreateNewReadingListButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9A611721124CF500403154 /* CreateNewReadingListButtonView.xib */; }; + 7A9A611B21124CF500403154 /* CreateNewReadingListButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9A611721124CF500403154 /* CreateNewReadingListButtonView.xib */; }; + 7A9A611E21124D0F00403154 /* CreateNewReadingListButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9A611D21124D0E00403154 /* CreateNewReadingListButtonView.swift */; }; + 7A9A611F21124D0F00403154 /* CreateNewReadingListButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9A611D21124D0E00403154 /* CreateNewReadingListButtonView.swift */; }; + 7A9A612021124D0F00403154 /* CreateNewReadingListButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9A611D21124D0E00403154 /* CreateNewReadingListButtonView.swift */; }; + 7A9A612121124D0F00403154 /* CreateNewReadingListButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9A611D21124D0E00403154 /* CreateNewReadingListButtonView.swift */; }; + 7A9F060D2266425700856321 /* InsertMediaSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F060B2266425700856321 /* InsertMediaSettingsViewController.swift */; }; + 7A9F060E2266425700856321 /* InsertMediaSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F060B2266425700856321 /* InsertMediaSettingsViewController.swift */; }; + 7A9F060F2266425700856321 /* InsertMediaSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F060B2266425700856321 /* InsertMediaSettingsViewController.swift */; }; + 7A9F06102266425700856321 /* InsertMediaSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F060B2266425700856321 /* InsertMediaSettingsViewController.swift */; }; + 7A9F06192266432200856321 /* InsertMediaSettingsTextTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F06172266432200856321 /* InsertMediaSettingsTextTableViewCell.swift */; }; + 7A9F061A2266432200856321 /* InsertMediaSettingsTextTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F06172266432200856321 /* InsertMediaSettingsTextTableViewCell.swift */; }; + 7A9F061B2266432200856321 /* InsertMediaSettingsTextTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F06172266432200856321 /* InsertMediaSettingsTextTableViewCell.swift */; }; + 7A9F061C2266432200856321 /* InsertMediaSettingsTextTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F06172266432200856321 /* InsertMediaSettingsTextTableViewCell.swift */; }; + 7A9F061E2266432200856321 /* InsertMediaSettingsTextTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9F06182266432200856321 /* InsertMediaSettingsTextTableViewCell.xib */; }; + 7A9F061F2266432200856321 /* InsertMediaSettingsTextTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9F06182266432200856321 /* InsertMediaSettingsTextTableViewCell.xib */; }; + 7A9F06202266432200856321 /* InsertMediaSettingsTextTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9F06182266432200856321 /* InsertMediaSettingsTextTableViewCell.xib */; }; + 7A9F06212266432200856321 /* InsertMediaSettingsTextTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A9F06182266432200856321 /* InsertMediaSettingsTextTableViewCell.xib */; }; + 7A9F2776225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F2775225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift */; }; + 7A9F2777225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F2775225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift */; }; + 7A9F2778225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F2775225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift */; }; + 7A9F2779225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F2775225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift */; }; + 7AA96D5C21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA96D5B21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift */; }; + 7AA96D5D21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA96D5B21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift */; }; + 7AA96D5E21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA96D5B21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift */; }; + 7AA96D5F21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA96D5B21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift */; }; + 7AB209F922FC67B4006FECB4 /* PageHistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB209F822FC67B4006FECB4 /* PageHistoryViewController.swift */; }; + 7AB209FA22FC67B4006FECB4 /* PageHistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB209F822FC67B4006FECB4 /* PageHistoryViewController.swift */; }; + 7AB209FB22FC67B4006FECB4 /* PageHistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB209F822FC67B4006FECB4 /* PageHistoryViewController.swift */; }; + 7AB209FC22FC67B4006FECB4 /* PageHistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB209F822FC67B4006FECB4 /* PageHistoryViewController.swift */; }; + 7AB20A0C22FC8432006FECB4 /* PageHistoryCountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB20A0A22FC8432006FECB4 /* PageHistoryCountsViewController.swift */; }; + 7AB20A0D22FC8432006FECB4 /* PageHistoryCountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB20A0A22FC8432006FECB4 /* PageHistoryCountsViewController.swift */; }; + 7AB20A0E22FC8432006FECB4 /* PageHistoryCountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB20A0A22FC8432006FECB4 /* PageHistoryCountsViewController.swift */; }; + 7AB20A0F22FC8432006FECB4 /* PageHistoryCountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB20A0A22FC8432006FECB4 /* PageHistoryCountsViewController.swift */; }; + 7AB20A1122FC8432006FECB4 /* PageHistoryCountsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AB20A0B22FC8432006FECB4 /* PageHistoryCountsViewController.xib */; }; + 7AB20A1222FC8432006FECB4 /* PageHistoryCountsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AB20A0B22FC8432006FECB4 /* PageHistoryCountsViewController.xib */; }; + 7AB20A1322FC8432006FECB4 /* PageHistoryCountsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AB20A0B22FC8432006FECB4 /* PageHistoryCountsViewController.xib */; }; + 7AB20A1422FC8432006FECB4 /* PageHistoryCountsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AB20A0B22FC8432006FECB4 /* PageHistoryCountsViewController.xib */; }; + 7AB6F0FF22AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB6F0FE22AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift */; }; + 7AB6F10022AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB6F0FE22AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift */; }; + 7AB6F10122AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB6F0FE22AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift */; }; + 7AB6F10222AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB6F0FE22AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift */; }; + 7AB7DEC8227203A600DD61A2 /* InsertMediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB7DEC7227203A600DD61A2 /* InsertMediaViewController.swift */; }; + 7AB7DEC9227203A600DD61A2 /* InsertMediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB7DEC7227203A600DD61A2 /* InsertMediaViewController.swift */; }; + 7AB7DECA227203A600DD61A2 /* InsertMediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB7DEC7227203A600DD61A2 /* InsertMediaViewController.swift */; }; + 7AB7DECB227203A600DD61A2 /* InsertMediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB7DEC7227203A600DD61A2 /* InsertMediaViewController.swift */; }; + 7AB809D022675B2300BFAB7C /* ThemeableTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB809CF22675B2300BFAB7C /* ThemeableTextView.swift */; }; + 7AB809D122675B2300BFAB7C /* ThemeableTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB809CF22675B2300BFAB7C /* ThemeableTextView.swift */; }; + 7AB809D222675B2300BFAB7C /* ThemeableTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB809CF22675B2300BFAB7C /* ThemeableTextView.swift */; }; + 7AB809D322675B2300BFAB7C /* ThemeableTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB809CF22675B2300BFAB7C /* ThemeableTextView.swift */; }; + 7AB809DC22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB809DB22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift */; }; + 7AB809DD22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB809DB22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift */; }; + 7AB809DE22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB809DB22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift */; }; + 7AB809DF22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB809DB22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift */; }; + 7ABAD6B420338CFB006A364C /* ReadingListDetailUnderBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABAD6B220338CFA006A364C /* ReadingListDetailUnderBarViewController.swift */; }; + 7ABAD6B520338CFB006A364C /* ReadingListDetailUnderBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABAD6B220338CFA006A364C /* ReadingListDetailUnderBarViewController.swift */; }; + 7ABAD6B620338CFB006A364C /* ReadingListDetailUnderBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABAD6B220338CFA006A364C /* ReadingListDetailUnderBarViewController.swift */; }; + 7ABAD6B720338CFB006A364C /* ReadingListDetailUnderBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABAD6B220338CFA006A364C /* ReadingListDetailUnderBarViewController.swift */; }; + 7ABAD6B920338CFB006A364C /* ReadingListDetailUnderBarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABAD6B320338CFB006A364C /* ReadingListDetailUnderBarViewController.xib */; }; + 7ABAD6BA20338CFB006A364C /* ReadingListDetailUnderBarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABAD6B320338CFB006A364C /* ReadingListDetailUnderBarViewController.xib */; }; + 7ABAD6BB20338CFB006A364C /* ReadingListDetailUnderBarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABAD6B320338CFB006A364C /* ReadingListDetailUnderBarViewController.xib */; }; + 7ABAD6BC20338CFB006A364C /* ReadingListDetailUnderBarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABAD6B320338CFB006A364C /* ReadingListDetailUnderBarViewController.xib */; }; + 7ABAD6BF20349B91006A364C /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABAD6BE20349B91006A364C /* Collection.swift */; }; + 7ABAD6C020349B91006A364C /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABAD6BE20349B91006A364C /* Collection.swift */; }; + 7ABAD6C120349B91006A364C /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABAD6BE20349B91006A364C /* Collection.swift */; }; + 7ABAD6C220349B91006A364C /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABAD6BE20349B91006A364C /* Collection.swift */; }; + 7ABE17002239B346006BA309 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE16FE2239B346006BA309 /* WelcomeViewController.swift */; }; + 7ABE17012239B346006BA309 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE16FE2239B346006BA309 /* WelcomeViewController.swift */; }; + 7ABE17022239B346006BA309 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE16FE2239B346006BA309 /* WelcomeViewController.swift */; }; + 7ABE17032239B346006BA309 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE16FE2239B346006BA309 /* WelcomeViewController.swift */; }; + 7ABE170C2239B5A0006BA309 /* WelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE170A2239B5A0006BA309 /* WelcomePageViewController.swift */; }; + 7ABE170D2239B5A0006BA309 /* WelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE170A2239B5A0006BA309 /* WelcomePageViewController.swift */; }; + 7ABE170E2239B5A0006BA309 /* WelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE170A2239B5A0006BA309 /* WelcomePageViewController.swift */; }; + 7ABE170F2239B5A0006BA309 /* WelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE170A2239B5A0006BA309 /* WelcomePageViewController.swift */; }; + 7ABE17182239B8EE006BA309 /* WelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17162239B8EE006BA309 /* WelcomeContainerViewController.swift */; }; + 7ABE17192239B8EE006BA309 /* WelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17162239B8EE006BA309 /* WelcomeContainerViewController.swift */; }; + 7ABE171A2239B8EE006BA309 /* WelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17162239B8EE006BA309 /* WelcomeContainerViewController.swift */; }; + 7ABE171B2239B8EE006BA309 /* WelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17162239B8EE006BA309 /* WelcomeContainerViewController.swift */; }; + 7ABE171D2239B8EE006BA309 /* WelcomeContainerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABE17172239B8EE006BA309 /* WelcomeContainerViewController.xib */; }; + 7ABE171E2239B8EE006BA309 /* WelcomeContainerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABE17172239B8EE006BA309 /* WelcomeContainerViewController.xib */; }; + 7ABE171F2239B8EE006BA309 /* WelcomeContainerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABE17172239B8EE006BA309 /* WelcomeContainerViewController.xib */; }; + 7ABE17202239B8EE006BA309 /* WelcomeContainerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABE17172239B8EE006BA309 /* WelcomeContainerViewController.xib */; }; + 7ABE17242239BB54006BA309 /* WelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17222239BB54006BA309 /* WelcomePanelViewController.swift */; }; + 7ABE17252239BB54006BA309 /* WelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17222239BB54006BA309 /* WelcomePanelViewController.swift */; }; + 7ABE17262239BB54006BA309 /* WelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17222239BB54006BA309 /* WelcomePanelViewController.swift */; }; + 7ABE17272239BB54006BA309 /* WelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17222239BB54006BA309 /* WelcomePanelViewController.swift */; }; + 7ABE17292239BB54006BA309 /* WelcomePanelViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABE17232239BB54006BA309 /* WelcomePanelViewController.xib */; }; + 7ABE172A2239BB54006BA309 /* WelcomePanelViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABE17232239BB54006BA309 /* WelcomePanelViewController.xib */; }; + 7ABE172B2239BB54006BA309 /* WelcomePanelViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABE17232239BB54006BA309 /* WelcomePanelViewController.xib */; }; + 7ABE172C2239BB54006BA309 /* WelcomePanelViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ABE17232239BB54006BA309 /* WelcomePanelViewController.xib */; }; + 7ABE17352239DCF6006BA309 /* WelcomeAnimationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17342239DCF5006BA309 /* WelcomeAnimationViewController.swift */; }; + 7ABE17362239DCF6006BA309 /* WelcomeAnimationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17342239DCF5006BA309 /* WelcomeAnimationViewController.swift */; }; + 7ABE17372239DCF6006BA309 /* WelcomeAnimationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17342239DCF5006BA309 /* WelcomeAnimationViewController.swift */; }; + 7ABE17382239DCF6006BA309 /* WelcomeAnimationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE17342239DCF5006BA309 /* WelcomeAnimationViewController.swift */; }; + 7ABE173B2239DEF0006BA309 /* WelcomeAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE173A2239DEF0006BA309 /* WelcomeAnimationView.swift */; }; + 7ABE173C2239DEF0006BA309 /* WelcomeAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE173A2239DEF0006BA309 /* WelcomeAnimationView.swift */; }; + 7ABE173D2239DEF0006BA309 /* WelcomeAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE173A2239DEF0006BA309 /* WelcomeAnimationView.swift */; }; + 7ABE173E2239DEF0006BA309 /* WelcomeAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABE173A2239DEF0006BA309 /* WelcomeAnimationView.swift */; }; + 7AC19E322301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC19E312301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift */; }; + 7AC19E332301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC19E312301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift */; }; + 7AC19E342301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC19E312301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift */; }; + 7AC19E352301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC19E312301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift */; }; + 7AC19E452301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC19E432301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift */; }; + 7AC19E462301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC19E432301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift */; }; + 7AC19E472301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC19E432301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift */; }; + 7AC19E482301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC19E432301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift */; }; + 7AC19E4A2301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AC19E442301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib */; }; + 7AC19E4B2301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AC19E442301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib */; }; + 7AC19E4C2301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AC19E442301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib */; }; + 7AC19E4D2301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AC19E442301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib */; }; + 7AC809C521DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC809C421DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift */; }; + 7AC809C621DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC809C421DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift */; }; + 7AC809C721DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC809C421DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift */; }; + 7AC809C821DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC809C421DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift */; }; + 7AD5D453223874F600C01164 /* RelatedSearchFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AD5D452223874F600C01164 /* RelatedSearchFetcher.swift */; }; + 7ADB2A0E1FD1E96300B84818 /* BatchEditSelectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADB2A081FD1E8C400B84818 /* BatchEditSelectView.swift */; }; + 7ADEAB031FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADEAB011FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift */; }; + 7ADEAB041FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADEAB011FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift */; }; + 7ADEAB051FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADEAB011FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift */; }; + 7ADEAB061FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADEAB011FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift */; }; + 7ADF497B21B45CEE009EA338 /* TextFormattingPlainToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF497921B45CEE009EA338 /* TextFormattingPlainToolbarView.swift */; }; + 7ADF497C21B45CEE009EA338 /* TextFormattingPlainToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF497921B45CEE009EA338 /* TextFormattingPlainToolbarView.swift */; }; + 7ADF497D21B45CEE009EA338 /* TextFormattingPlainToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF497921B45CEE009EA338 /* TextFormattingPlainToolbarView.swift */; }; + 7ADF497E21B45CEE009EA338 /* TextFormattingPlainToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF497921B45CEE009EA338 /* TextFormattingPlainToolbarView.swift */; }; + 7ADF498021B45CEE009EA338 /* TextFormattingPlainToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF497A21B45CEE009EA338 /* TextFormattingPlainToolbarView.xib */; }; + 7ADF498121B45CEE009EA338 /* TextFormattingPlainToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF497A21B45CEE009EA338 /* TextFormattingPlainToolbarView.xib */; }; + 7ADF498221B45CEE009EA338 /* TextFormattingPlainToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF497A21B45CEE009EA338 /* TextFormattingPlainToolbarView.xib */; }; + 7ADF498321B45CEE009EA338 /* TextFormattingPlainToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF497A21B45CEE009EA338 /* TextFormattingPlainToolbarView.xib */; }; + 7ADF498721B45E42009EA338 /* TextFormattingGroupedToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF498521B45E42009EA338 /* TextFormattingGroupedToolbarView.swift */; }; + 7ADF498821B45E42009EA338 /* TextFormattingGroupedToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF498521B45E42009EA338 /* TextFormattingGroupedToolbarView.swift */; }; + 7ADF498921B45E42009EA338 /* TextFormattingGroupedToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF498521B45E42009EA338 /* TextFormattingGroupedToolbarView.swift */; }; + 7ADF498A21B45E42009EA338 /* TextFormattingGroupedToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF498521B45E42009EA338 /* TextFormattingGroupedToolbarView.swift */; }; + 7ADF498C21B45E42009EA338 /* TextFormattingGroupedToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF498621B45E42009EA338 /* TextFormattingGroupedToolbarView.xib */; }; + 7ADF498D21B45E42009EA338 /* TextFormattingGroupedToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF498621B45E42009EA338 /* TextFormattingGroupedToolbarView.xib */; }; + 7ADF498E21B45E42009EA338 /* TextFormattingGroupedToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF498621B45E42009EA338 /* TextFormattingGroupedToolbarView.xib */; }; + 7ADF498F21B45E42009EA338 /* TextFormattingGroupedToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF498621B45E42009EA338 /* TextFormattingGroupedToolbarView.xib */; }; + 7ADF853623516CF500500ADC /* PageHistoryHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF853523516CF500500ADC /* PageHistoryHintController.swift */; }; + 7ADF853723516CF500500ADC /* PageHistoryHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF853523516CF500500ADC /* PageHistoryHintController.swift */; }; + 7ADF853823516CF500500ADC /* PageHistoryHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF853523516CF500500ADC /* PageHistoryHintController.swift */; }; + 7ADF853923516CF500500ADC /* PageHistoryHintController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF853523516CF500500ADC /* PageHistoryHintController.swift */; }; + 7AE1D3331FCD057200393471 /* Saved.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7AE1D3321FCD057200393471 /* Saved.storyboard */; }; + 7AE1D3341FCD057200393471 /* Saved.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7AE1D3321FCD057200393471 /* Saved.storyboard */; }; + 7AE1D3351FCD057200393471 /* Saved.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7AE1D3321FCD057200393471 /* Saved.storyboard */; }; + 7AE1D3361FCD057200393471 /* Saved.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7AE1D3321FCD057200393471 /* Saved.storyboard */; }; + 7AE1D3391FCD10B900393471 /* SavedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE1D3381FCD10B900393471 /* SavedViewController.swift */; }; + 7AE1D33A1FCD10B900393471 /* SavedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE1D3381FCD10B900393471 /* SavedViewController.swift */; }; + 7AE1D33B1FCD10B900393471 /* SavedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE1D3381FCD10B900393471 /* SavedViewController.swift */; }; + 7AE1D33C1FCD10B900393471 /* SavedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE1D3381FCD10B900393471 /* SavedViewController.swift */; }; + 7AE1FE3121B4A9790068BE9F /* TextFormattingButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE1FE2F21B4A9790068BE9F /* TextFormattingButtonView.swift */; }; + 7AE1FE3221B4A9790068BE9F /* TextFormattingButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE1FE2F21B4A9790068BE9F /* TextFormattingButtonView.swift */; }; + 7AE1FE3321B4A9790068BE9F /* TextFormattingButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE1FE2F21B4A9790068BE9F /* TextFormattingButtonView.swift */; }; + 7AE1FE3421B4A9790068BE9F /* TextFormattingButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE1FE2F21B4A9790068BE9F /* TextFormattingButtonView.swift */; }; + 7AE1FE3621B4A9790068BE9F /* TextFormattingButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AE1FE3021B4A9790068BE9F /* TextFormattingButtonView.xib */; }; + 7AE1FE3721B4A9790068BE9F /* TextFormattingButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AE1FE3021B4A9790068BE9F /* TextFormattingButtonView.xib */; }; + 7AE1FE3821B4A9790068BE9F /* TextFormattingButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AE1FE3021B4A9790068BE9F /* TextFormattingButtonView.xib */; }; + 7AE1FE3921B4A9790068BE9F /* TextFormattingButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AE1FE3021B4A9790068BE9F /* TextFormattingButtonView.xib */; }; + 7AE5248D21383D9C00CDC817 /* WikidataFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE5248C21383D9C00CDC817 /* WikidataFetcher.swift */; }; + 7AE99B2821CC4F420092BE7F /* TextSizeFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE99B2721CC4F420092BE7F /* TextSizeFormattingTableViewController.swift */; }; + 7AE99B2921CC4F420092BE7F /* TextSizeFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE99B2721CC4F420092BE7F /* TextSizeFormattingTableViewController.swift */; }; + 7AE99B2A21CC4F420092BE7F /* TextSizeFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE99B2721CC4F420092BE7F /* TextSizeFormattingTableViewController.swift */; }; + 7AE99B2B21CC4F420092BE7F /* TextSizeFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE99B2721CC4F420092BE7F /* TextSizeFormattingTableViewController.swift */; }; + 7AE99B2E21CC53AB0092BE7F /* TextFontFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE99B2D21CC53AB0092BE7F /* TextFontFormattingTableViewController.swift */; }; + 7AE99B2F21CC53AB0092BE7F /* TextFontFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE99B2D21CC53AB0092BE7F /* TextFontFormattingTableViewController.swift */; }; + 7AE99B3021CC53AB0092BE7F /* TextFontFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE99B2D21CC53AB0092BE7F /* TextFontFormattingTableViewController.swift */; }; + 7AE99B3121CC53AB0092BE7F /* TextFontFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE99B2D21CC53AB0092BE7F /* TextFontFormattingTableViewController.swift */; }; + 7AEBAD452102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEBAD442102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift */; }; + 7AEBAD462102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEBAD442102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift */; }; + 7AEBAD472102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEBAD442102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift */; }; + 7AEBAD482102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEBAD442102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift */; }; + 7AEC9859219F529000BEF62B /* DefaultEditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEC9857219F529000BEF62B /* DefaultEditToolbarView.swift */; }; + 7AEC985A219F529000BEF62B /* DefaultEditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEC9857219F529000BEF62B /* DefaultEditToolbarView.swift */; }; + 7AEC985B219F529000BEF62B /* DefaultEditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEC9857219F529000BEF62B /* DefaultEditToolbarView.swift */; }; + 7AEC985C219F529000BEF62B /* DefaultEditToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEC9857219F529000BEF62B /* DefaultEditToolbarView.swift */; }; + 7AEC985E219F529000BEF62B /* DefaultEditToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AEC9858219F529000BEF62B /* DefaultEditToolbarView.xib */; }; + 7AEC985F219F529000BEF62B /* DefaultEditToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AEC9858219F529000BEF62B /* DefaultEditToolbarView.xib */; }; + 7AEC9860219F529000BEF62B /* DefaultEditToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AEC9858219F529000BEF62B /* DefaultEditToolbarView.xib */; }; + 7AEC9861219F529000BEF62B /* DefaultEditToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AEC9858219F529000BEF62B /* DefaultEditToolbarView.xib */; }; + 7AEF527120ADD74D00DDF791 /* WMFCaptcha.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3AD05620ADAFEF00C92E04 /* WMFCaptcha.swift */; }; + 7AEF527320ADF07100DDF791 /* KeychainCredentialsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEF527220ADF07100DDF791 /* KeychainCredentialsManager.swift */; }; + 7AF0265622985CB9000E0A06 /* BeKindInputAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF0265422985CB9000E0A06 /* BeKindInputAccessoryView.swift */; }; + 7AF0265722985CB9000E0A06 /* BeKindInputAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF0265422985CB9000E0A06 /* BeKindInputAccessoryView.swift */; }; + 7AF0265822985CB9000E0A06 /* BeKindInputAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF0265422985CB9000E0A06 /* BeKindInputAccessoryView.swift */; }; + 7AF0265922985CB9000E0A06 /* BeKindInputAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF0265422985CB9000E0A06 /* BeKindInputAccessoryView.swift */; }; + 7AF0265B22985CB9000E0A06 /* BeKindInputAccessoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AF0265522985CB9000E0A06 /* BeKindInputAccessoryView.xib */; }; + 7AF0265C22985CB9000E0A06 /* BeKindInputAccessoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AF0265522985CB9000E0A06 /* BeKindInputAccessoryView.xib */; }; + 7AF0265D22985CB9000E0A06 /* BeKindInputAccessoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AF0265522985CB9000E0A06 /* BeKindInputAccessoryView.xib */; }; + 7AF0265E22985CB9000E0A06 /* BeKindInputAccessoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AF0265522985CB9000E0A06 /* BeKindInputAccessoryView.xib */; }; + 7AF49F80204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF49F7F204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift */; }; + 7AF49F81204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF49F7F204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift */; }; + 7AF49F82204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF49F7F204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift */; }; + 7AF49F83204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF49F7F204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift */; }; + 7AF56C2F21DDEC1C00563A9C /* TextFormattingProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF56C2E21DDEC1C00563A9C /* TextFormattingProviding.swift */; }; + 7AF56C3021DDEC1C00563A9C /* TextFormattingProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF56C2E21DDEC1C00563A9C /* TextFormattingProviding.swift */; }; + 7AF56C3121DDEC1C00563A9C /* TextFormattingProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF56C2E21DDEC1C00563A9C /* TextFormattingProviding.swift */; }; + 7AF56C3221DDEC1C00563A9C /* TextFormattingProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF56C2E21DDEC1C00563A9C /* TextFormattingProviding.swift */; }; + 7AF56C3521DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF56C3421DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift */; }; + 7AF56C3621DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF56C3421DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift */; }; + 7AF56C3721DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF56C3421DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift */; }; + 7AF56C3821DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF56C3421DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift */; }; + 7AF6F76622395BEC00949393 /* EditingWelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF6F76522395BEB00949393 /* EditingWelcomeViewController.swift */; }; + 7AF6F76722395BEC00949393 /* EditingWelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF6F76522395BEB00949393 /* EditingWelcomeViewController.swift */; }; + 7AF6F76822395BEC00949393 /* EditingWelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF6F76522395BEB00949393 /* EditingWelcomeViewController.swift */; }; + 7AF6F76922395BEC00949393 /* EditingWelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF6F76522395BEB00949393 /* EditingWelcomeViewController.swift */; }; + 7AF8B7422102297A009772CC /* SearchSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF8B7402102297A009772CC /* SearchSettingsViewController.swift */; }; + 7AF8B7432102297A009772CC /* SearchSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF8B7402102297A009772CC /* SearchSettingsViewController.swift */; }; + 7AF8B7442102297A009772CC /* SearchSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF8B7402102297A009772CC /* SearchSettingsViewController.swift */; }; + 7AF8B7452102297A009772CC /* SearchSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF8B7402102297A009772CC /* SearchSettingsViewController.swift */; }; + 7AF8CEED22653406000B7676 /* InsertMediaSelectedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF8CEEB22653406000B7676 /* InsertMediaSelectedImageView.swift */; }; + 7AF8CEEE22653406000B7676 /* InsertMediaSelectedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF8CEEB22653406000B7676 /* InsertMediaSelectedImageView.swift */; }; + 7AF8CEEF22653406000B7676 /* InsertMediaSelectedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF8CEEB22653406000B7676 /* InsertMediaSelectedImageView.swift */; }; + 7AF8CEF022653406000B7676 /* InsertMediaSelectedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF8CEEB22653406000B7676 /* InsertMediaSelectedImageView.swift */; }; + 7AFA21BB20110D7900E957E7 /* ReadingListHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFA21B920110D7900E957E7 /* ReadingListHintViewController.swift */; }; + 7AFA21BC20110D7900E957E7 /* ReadingListHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFA21B920110D7900E957E7 /* ReadingListHintViewController.swift */; }; + 7AFA21BD20110D7900E957E7 /* ReadingListHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFA21B920110D7900E957E7 /* ReadingListHintViewController.swift */; }; + 7AFA21BE20110D7900E957E7 /* ReadingListHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFA21B920110D7900E957E7 /* ReadingListHintViewController.swift */; }; + 7AFA21C020110D7900E957E7 /* HintViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AFA21BA20110D7900E957E7 /* HintViewController.xib */; }; + 7AFA21C120110D7900E957E7 /* HintViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AFA21BA20110D7900E957E7 /* HintViewController.xib */; }; + 7AFA21C220110D7900E957E7 /* HintViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AFA21BA20110D7900E957E7 /* HintViewController.xib */; }; + 7AFA21C320110D7900E957E7 /* HintViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7AFA21BA20110D7900E957E7 /* HintViewController.xib */; }; + 7AFC79F821B0367700BB0C50 /* TextFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFC79F721B0367700BB0C50 /* TextFormattingTableViewController.swift */; }; + 7AFC79F921B0367700BB0C50 /* TextFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFC79F721B0367700BB0C50 /* TextFormattingTableViewController.swift */; }; + 7AFC79FA21B0367700BB0C50 /* TextFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFC79F721B0367700BB0C50 /* TextFormattingTableViewController.swift */; }; + 7AFC79FB21B0367700BB0C50 /* TextFormattingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFC79F721B0367700BB0C50 /* TextFormattingTableViewController.swift */; }; + 7AFEB1BC1FA236A100B8DF32 /* UIViewController+Peekable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFEB1BB1FA236A100B8DF32 /* UIViewController+Peekable.swift */; }; + 7AFEB1BD1FA236A100B8DF32 /* UIViewController+Peekable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFEB1BB1FA236A100B8DF32 /* UIViewController+Peekable.swift */; }; + 7AFEB1BE1FA236A100B8DF32 /* UIViewController+Peekable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFEB1BB1FA236A100B8DF32 /* UIViewController+Peekable.swift */; }; + 7AFEB1BF1FA236A100B8DF32 /* UIViewController+Peekable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFEB1BB1FA236A100B8DF32 /* UIViewController+Peekable.swift */; }; + 7AFEB3F51FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFEB3F41FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift */; }; + 7AFEB3F61FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFEB3F41FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift */; }; + 7AFEB3F71FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFEB3F41FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift */; }; + 7AFEB3F81FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFEB3F41FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift */; }; + 830177FA1FBF3E490005681C /* ReadingListsAPIController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830177F91FBF3E490005681C /* ReadingListsAPIController.swift */; }; + 830177FC1FBF3EF70005681C /* NSManagedObjectContext+WMFUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830177FB1FBF3EF70005681C /* NSManagedObjectContext+WMFUtilities.swift */; }; + 83023C0620E51DDF00EC7592 /* SearchLanguagesBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C0420E51DDF00EC7592 /* SearchLanguagesBarViewController.swift */; }; + 83023C0720E51DDF00EC7592 /* SearchLanguagesBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C0420E51DDF00EC7592 /* SearchLanguagesBarViewController.swift */; }; + 83023C0820E51DDF00EC7592 /* SearchLanguagesBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C0420E51DDF00EC7592 /* SearchLanguagesBarViewController.swift */; }; + 83023C0920E51DDF00EC7592 /* SearchLanguagesBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C0420E51DDF00EC7592 /* SearchLanguagesBarViewController.swift */; }; + 83023C0B20E51DDF00EC7592 /* SearchLanguagesBarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83023C0520E51DDF00EC7592 /* SearchLanguagesBarViewController.xib */; }; + 83023C0C20E51DDF00EC7592 /* SearchLanguagesBarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83023C0520E51DDF00EC7592 /* SearchLanguagesBarViewController.xib */; }; + 83023C0D20E51DDF00EC7592 /* SearchLanguagesBarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83023C0520E51DDF00EC7592 /* SearchLanguagesBarViewController.xib */; }; + 83023C0E20E51DDF00EC7592 /* SearchLanguagesBarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83023C0520E51DDF00EC7592 /* SearchLanguagesBarViewController.xib */; }; + 83023C1120E6561900EC7592 /* ViewControllerTransitionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C1020E6561900EC7592 /* ViewControllerTransitionsController.swift */; }; + 83023C1220E6561900EC7592 /* ViewControllerTransitionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C1020E6561900EC7592 /* ViewControllerTransitionsController.swift */; }; + 83023C1320E6561900EC7592 /* ViewControllerTransitionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C1020E6561900EC7592 /* ViewControllerTransitionsController.swift */; }; + 83023C1420E6561900EC7592 /* ViewControllerTransitionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C1020E6561900EC7592 /* ViewControllerTransitionsController.swift */; }; + 83023C1F20E6584F00EC7592 /* SearchTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C1E20E6584F00EC7592 /* SearchTransition.swift */; }; + 83023C2020E6584F00EC7592 /* SearchTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C1E20E6584F00EC7592 /* SearchTransition.swift */; }; + 83023C2120E6584F00EC7592 /* SearchTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C1E20E6584F00EC7592 /* SearchTransition.swift */; }; + 83023C2220E6584F00EC7592 /* SearchTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83023C1E20E6584F00EC7592 /* SearchTransition.swift */; }; + 830378402940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8303783F2940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift */; }; + 830378412940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8303783F2940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift */; }; + 830378422940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8303783F2940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift */; }; + 830378432940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8303783F2940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift */; }; + 830AD2B924D1D615003EEFE6 /* WebPageUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830AD2B824D1D615003EEFE6 /* WebPageUserScript.swift */; }; + 830AD2BA24D1D615003EEFE6 /* WebPageUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830AD2B824D1D615003EEFE6 /* WebPageUserScript.swift */; }; + 830AD2BB24D1D615003EEFE6 /* WebPageUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830AD2B824D1D615003EEFE6 /* WebPageUserScript.swift */; }; + 830AD2BC24D1D615003EEFE6 /* WebPageUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830AD2B824D1D615003EEFE6 /* WebPageUserScript.swift */; }; + 830C0DD523D9AFBE006471C4 /* UIViewController+Push.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830C0DD423D9AFBE006471C4 /* UIViewController+Push.swift */; }; + 830C0DD623D9AFBE006471C4 /* UIViewController+Push.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830C0DD423D9AFBE006471C4 /* UIViewController+Push.swift */; }; + 830C0DD723D9AFBE006471C4 /* UIViewController+Push.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830C0DD423D9AFBE006471C4 /* UIViewController+Push.swift */; }; + 830C0DD823D9AFBE006471C4 /* UIViewController+Push.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830C0DD423D9AFBE006471C4 /* UIViewController+Push.swift */; }; + 830C0DDA23D9C218006471C4 /* Properties.js in Resources */ = {isa = PBXBuildFile; fileRef = 830C0DD923D9C218006471C4 /* Properties.js */; }; + 830C0DDB23D9C218006471C4 /* Properties.js in Resources */ = {isa = PBXBuildFile; fileRef = 830C0DD923D9C218006471C4 /* Properties.js */; }; + 830C0DDC23D9C218006471C4 /* Properties.js in Resources */ = {isa = PBXBuildFile; fileRef = 830C0DD923D9C218006471C4 /* Properties.js */; }; + 830C0DDD23D9C218006471C4 /* Properties.js in Resources */ = {isa = PBXBuildFile; fileRef = 830C0DD923D9C218006471C4 /* Properties.js */; }; + 830D71C31F703C980080078B /* ArticleURLListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830D71C21F703C980080078B /* ArticleURLListViewController.swift */; }; + 830D71C41F703C980080078B /* ArticleURLListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830D71C21F703C980080078B /* ArticleURLListViewController.swift */; }; + 830D71C51F703C980080078B /* ArticleURLListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830D71C21F703C980080078B /* ArticleURLListViewController.swift */; }; + 830D71C61F703C980080078B /* ArticleURLListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830D71C21F703C980080078B /* ArticleURLListViewController.swift */; }; + 830D71CF1F704DD40080078B /* ArticleFetchedResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830D71CE1F704DD40080078B /* ArticleFetchedResultsViewController.swift */; }; + 830D71D01F704DD40080078B /* ArticleFetchedResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830D71CE1F704DD40080078B /* ArticleFetchedResultsViewController.swift */; }; + 830D71D11F704DD40080078B /* ArticleFetchedResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830D71CE1F704DD40080078B /* ArticleFetchedResultsViewController.swift */; }; + 830D71D21F704DD40080078B /* ArticleFetchedResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830D71CE1F704DD40080078B /* ArticleFetchedResultsViewController.swift */; }; + 830ECACF1FBDD8C00080B1EF /* ReadingListsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830ECACE1FBDD8C00080B1EF /* ReadingListsViewController.swift */; }; + 830ECAD01FBDD8C00080B1EF /* ReadingListsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830ECACE1FBDD8C00080B1EF /* ReadingListsViewController.swift */; }; + 830ECAD11FBDD8C00080B1EF /* ReadingListsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830ECACE1FBDD8C00080B1EF /* ReadingListsViewController.swift */; }; + 830ECAD21FBDD8C00080B1EF /* ReadingListsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830ECACE1FBDD8C00080B1EF /* ReadingListsViewController.swift */; }; + 830ECAD61FBDE77F0080B1EF /* ReadingListsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830ECAD51FBDE77F0080B1EF /* ReadingListsTests.swift */; }; + 831937E723E1CE80006A9FF3 /* String+LinkParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 831937E623E1CE80006A9FF3 /* String+LinkParsing.swift */; }; + 831937E923E1CEAC006A9FF3 /* CharacterSet+LinkParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 831937E823E1CEAC006A9FF3 /* CharacterSet+LinkParsing.swift */; }; + 831C15C62099EB3A001B04BF /* WMFArticle+Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 831C15C52099EB3A001B04BF /* WMFArticle+Errors.swift */; }; + 8320331B22B90528004A9EDA /* NavigationStateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC92E65228D8C7B0035E7F0 /* NavigationStateController.swift */; }; + 8320331C22B90528004A9EDA /* NavigationStateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC92E65228D8C7B0035E7F0 /* NavigationStateController.swift */; }; + 8320331D22B90529004A9EDA /* NavigationStateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC92E65228D8C7B0035E7F0 /* NavigationStateController.swift */; }; + 8320331E22B90529004A9EDA /* NavigationStateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC92E65228D8C7B0035E7F0 /* NavigationStateController.swift */; }; + 8320332122B90548004A9EDA /* NSManagedObjectContext+NavigationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8320332022B90548004A9EDA /* NSManagedObjectContext+NavigationState.swift */; }; + 8320332322B906A0004A9EDA /* NavigationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8320332222B906A0004A9EDA /* NavigationState.swift */; }; + 8321FCCA23871D8F0079F3C7 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8321FCC923871D8F0079F3C7 /* Router.swift */; }; + 8321FCCC2387231E0079F3C7 /* ViewControllerRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8321FCCB2387231E0079F3C7 /* ViewControllerRouter.swift */; }; + 8321FCCD2387231E0079F3C7 /* ViewControllerRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8321FCCB2387231E0079F3C7 /* ViewControllerRouter.swift */; }; + 8321FCCE2387231E0079F3C7 /* ViewControllerRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8321FCCB2387231E0079F3C7 /* ViewControllerRouter.swift */; }; + 8321FCCF2387231E0079F3C7 /* ViewControllerRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8321FCCB2387231E0079F3C7 /* ViewControllerRouter.swift */; }; + 83222DB11F8E554800338BE5 /* WMFContent+CoreDataProperties.h in Headers */ = {isa = PBXBuildFile; fileRef = 83222DAD1F8E554800338BE5 /* WMFContent+CoreDataProperties.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83222DB21F8E554800338BE5 /* WMFContent+CoreDataClass.h in Headers */ = {isa = PBXBuildFile; fileRef = 83222DAE1F8E554800338BE5 /* WMFContent+CoreDataClass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83222DB31F8E554800338BE5 /* WMFContent+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = 83222DAF1F8E554800338BE5 /* WMFContent+CoreDataClass.m */; }; + 83222DB41F8E554800338BE5 /* WMFContent+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = 83222DB01F8E554800338BE5 /* WMFContent+CoreDataProperties.m */; }; + 832289DB1F7291BA0081A5FB /* SizeThatFitsReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832289DA1F7291BA0081A5FB /* SizeThatFitsReusableView.swift */; }; + 832289DC1F7291BA0081A5FB /* SizeThatFitsReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832289DA1F7291BA0081A5FB /* SizeThatFitsReusableView.swift */; }; + 832289DD1F7291BA0081A5FB /* SizeThatFitsReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832289DA1F7291BA0081A5FB /* SizeThatFitsReusableView.swift */; }; + 832289DE1F7291BA0081A5FB /* SizeThatFitsReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832289DA1F7291BA0081A5FB /* SizeThatFitsReusableView.swift */; }; + 832A7A5B23EA138C00D0A750 /* ArticleViewController+References.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832A7A5A23EA138C00D0A750 /* ArticleViewController+References.swift */; }; + 832A7A5C23EA138C00D0A750 /* ArticleViewController+References.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832A7A5A23EA138C00D0A750 /* ArticleViewController+References.swift */; }; + 832A7A5D23EA138C00D0A750 /* ArticleViewController+References.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832A7A5A23EA138C00D0A750 /* ArticleViewController+References.swift */; }; + 832A7A5E23EA138C00D0A750 /* ArticleViewController+References.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832A7A5A23EA138C00D0A750 /* ArticleViewController+References.swift */; }; + 832A7A6023EAE03200D0A750 /* String+JavaScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832A7A5F23EAE03200D0A750 /* String+JavaScript.swift */; }; + 832B2B8423D9F9420087EB5F /* NSRegularExpression+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832B2B8323D9F9420087EB5F /* NSRegularExpression+Utilities.swift */; }; + 832BD3BC28996B68002623CA /* VanishAccountContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832BD3BB28996B68002623CA /* VanishAccountContentView.swift */; }; + 832BD3BD28996B68002623CA /* VanishAccountContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832BD3BB28996B68002623CA /* VanishAccountContentView.swift */; }; + 832BD3BE28996B68002623CA /* VanishAccountContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832BD3BB28996B68002623CA /* VanishAccountContentView.swift */; }; + 832BD3BF28996B68002623CA /* VanishAccountContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832BD3BB28996B68002623CA /* VanishAccountContentView.swift */; }; + 8330531F23EF051900123141 /* NSArray+WMFMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 8330531D23EF051900123141 /* NSArray+WMFMapping.m */; }; + 8330532023EF051900123141 /* NSArray+WMFMapping.h in Headers */ = {isa = PBXBuildFile; fileRef = 8330531E23EF051900123141 /* NSArray+WMFMapping.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8330532223EF05D000123141 /* WMFBlocksKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8330532123EF05D000123141 /* WMFBlocksKit.swift */; }; + 8330532923EF0B4200123141 /* ArticleViewController+Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8330532823EF0B4200123141 /* ArticleViewController+Media.swift */; }; + 8330532A23EF0B4200123141 /* ArticleViewController+Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8330532823EF0B4200123141 /* ArticleViewController+Media.swift */; }; + 8330532B23EF0B4200123141 /* ArticleViewController+Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8330532823EF0B4200123141 /* ArticleViewController+Media.swift */; }; + 8330532C23EF0B4200123141 /* ArticleViewController+Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8330532823EF0B4200123141 /* ArticleViewController+Media.swift */; }; + 8330532E23EF107D00123141 /* MediaListGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8330532D23EF107D00123141 /* MediaListGalleryViewController.swift */; }; + 8330532F23EF107D00123141 /* MediaListGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8330532D23EF107D00123141 /* MediaListGalleryViewController.swift */; }; + 8330533023EF107D00123141 /* MediaListGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8330532D23EF107D00123141 /* MediaListGalleryViewController.swift */; }; + 8330533123EF107D00123141 /* MediaListGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8330532D23EF107D00123141 /* MediaListGalleryViewController.swift */; }; + 8330533323F0388E00123141 /* DataStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8330533223F0388E00123141 /* DataStoreTests.swift */; }; + 8334EC4C286A443B00929DF2 /* TalkPageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 838790B22858009000067B1D /* TalkPageFetcher.swift */; }; + 8334EC4D286A443B00929DF2 /* TalkPageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 838790B22858009000067B1D /* TalkPageFetcher.swift */; }; + 8334EC4E286A443C00929DF2 /* TalkPageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 838790B22858009000067B1D /* TalkPageFetcher.swift */; }; + 8336F1432119BD6E000CDE02 /* MediaWikiAcceptLanguageMapping.json in Resources */ = {isa = PBXBuildFile; fileRef = 8336F1422119BD6E000CDE02 /* MediaWikiAcceptLanguageMapping.json */; }; + 8338AF8D21F7B33E000C4055 /* WMFLegacyFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 8338AF8B21F7B33E000C4055 /* WMFLegacyFetcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8338AF8E21F7B33E000C4055 /* WMFLegacyFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 8338AF8C21F7B33E000C4055 /* WMFLegacyFetcher.m */; }; + 833B8C89281AE2100021C12C /* RemoteNotificationsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B1218327FC8750006B8CCC /* RemoteNotificationsFunnel.swift */; }; + 833B8C8A281AE2110021C12C /* RemoteNotificationsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B1218327FC8750006B8CCC /* RemoteNotificationsFunnel.swift */; }; + 833B8C8B281AE2120021C12C /* RemoteNotificationsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B1218327FC8750006B8CCC /* RemoteNotificationsFunnel.swift */; }; + 833D4FFB20A9E20800B44E7C /* String+HTML.swift in Sources */ = {isa = PBXBuildFile; fileRef = 833D4FFA20A9E20800B44E7C /* String+HTML.swift */; }; + 833D6B48229EE872003CB650 /* TalkPageTopic+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 833D6B47229EE872003CB650 /* TalkPageTopic+Extensions.swift */; }; + 833D6B49229EE872003CB650 /* TalkPageTopic+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 833D6B47229EE872003CB650 /* TalkPageTopic+Extensions.swift */; }; + 833D6B4A229EE872003CB650 /* TalkPageTopic+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 833D6B47229EE872003CB650 /* TalkPageTopic+Extensions.swift */; }; + 833D6B4B229EE872003CB650 /* TalkPageTopic+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 833D6B47229EE872003CB650 /* TalkPageTopic+Extensions.swift */; }; + 834400B020B3368A005F087D /* NSCharacterSet+WMFExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8048C1C0CE0B40065EBC0 /* NSCharacterSet+WMFExtras.m */; }; + 834400B120B3368E005F087D /* NSCharacterSet+WMFExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8048B1C0CE0B40065EBC0 /* NSCharacterSet+WMFExtras.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 834C269E240D49F400245BE7 /* ReferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834C269D240D49F400245BE7 /* ReferenceViewController.swift */; }; + 834C269F240D49F400245BE7 /* ReferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834C269D240D49F400245BE7 /* ReferenceViewController.swift */; }; + 834C26A0240D49F400245BE7 /* ReferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834C269D240D49F400245BE7 /* ReferenceViewController.swift */; }; + 834C26A1240D49F400245BE7 /* ReferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834C269D240D49F400245BE7 /* ReferenceViewController.swift */; }; + 834CC34B21075B7600F62818 /* UITabBar+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834CC34A21075B7600F62818 /* UITabBar+Theme.swift */; }; + 834CC34C21075B7600F62818 /* UITabBar+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834CC34A21075B7600F62818 /* UITabBar+Theme.swift */; }; + 834CC34D21075B7600F62818 /* UITabBar+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834CC34A21075B7600F62818 /* UITabBar+Theme.swift */; }; + 834CC34E21075B7600F62818 /* UITabBar+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834CC34A21075B7600F62818 /* UITabBar+Theme.swift */; }; + 834F47F42833D91F00F86C80 /* RemoteNotificationFilterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 834F47F32833D91F00F86C80 /* RemoteNotificationFilterType.swift */; }; + 8350FC4C20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8350FC4B20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift */; }; + 8350FC4D20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8350FC4B20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift */; }; + 8350FC4E20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8350FC4B20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift */; }; + 8350FC4F20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8350FC4B20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift */; }; + 83510B0728F4CF6400B6235B /* TalkPageErrorStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83510B0628F4CF6400B6235B /* TalkPageErrorStateView.swift */; }; + 83510B0828F4CF6400B6235B /* TalkPageErrorStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83510B0628F4CF6400B6235B /* TalkPageErrorStateView.swift */; }; + 83510B0928F4CF6400B6235B /* TalkPageErrorStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83510B0628F4CF6400B6235B /* TalkPageErrorStateView.swift */; }; + 83510B0A28F4CF6400B6235B /* TalkPageErrorStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83510B0628F4CF6400B6235B /* TalkPageErrorStateView.swift */; }; + 8351CE7820D4424100E32FC1 /* CollectionViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8351CE7720D4424100E32FC1 /* CollectionViewHeader.swift */; }; + 8351CE7920D4424100E32FC1 /* CollectionViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8351CE7720D4424100E32FC1 /* CollectionViewHeader.swift */; }; + 8351CE7A20D4424100E32FC1 /* CollectionViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8351CE7720D4424100E32FC1 /* CollectionViewHeader.swift */; }; + 8351CE7B20D4424100E32FC1 /* CollectionViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8351CE7720D4424100E32FC1 /* CollectionViewHeader.swift */; }; + 8356115D28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8356115C28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift */; }; + 8356115E28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8356115C28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift */; }; + 8356115F28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8356115C28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift */; }; + 8356116028D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8356115C28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift */; }; + 8359BAC721E4C9C1009B5E6C /* Fetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8359BAC621E4C9C1009B5E6C /* Fetcher.swift */; }; + 835A042D223AD63000D4D758 /* ArticleSummaryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 835A042C223AD63000D4D758 /* ArticleSummaryController.swift */; }; + 8361474B24223689003E49D3 /* ArticleViewController+Announcements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8361474A24223689003E49D3 /* ArticleViewController+Announcements.swift */; }; + 8361474C24223689003E49D3 /* ArticleViewController+Announcements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8361474A24223689003E49D3 /* ArticleViewController+Announcements.swift */; }; + 8361474D24223689003E49D3 /* ArticleViewController+Announcements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8361474A24223689003E49D3 /* ArticleViewController+Announcements.swift */; }; + 8361474E24223689003E49D3 /* ArticleViewController+Announcements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8361474A24223689003E49D3 /* ArticleViewController+Announcements.swift */; }; + 8367A27F20D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8367A27E20D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift */; }; + 8367A28020D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8367A27E20D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift */; }; + 8367A28120D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8367A27E20D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift */; }; + 8367A28220D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8367A27E20D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift */; }; + 8368BB8424129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8368BB8324129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift */; }; + 8368BB8524129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8368BB8324129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift */; }; + 8368BB8624129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8368BB8324129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift */; }; + 8368BB8724129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8368BB8324129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift */; }; + 836944DC1F572452007BD6DA /* ThemeableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82E954B1F15397D007BD960 /* ThemeableTextField.swift */; }; + 836BF56E2869F9C200B98321 /* TalkPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836BF56D2869F9C200B98321 /* TalkPageViewController.swift */; }; + 836BF56F2869F9C200B98321 /* TalkPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836BF56D2869F9C200B98321 /* TalkPageViewController.swift */; }; + 836BF5702869F9C200B98321 /* TalkPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836BF56D2869F9C200B98321 /* TalkPageViewController.swift */; }; + 836BF5712869F9C200B98321 /* TalkPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836BF56D2869F9C200B98321 /* TalkPageViewController.swift */; }; + 837A15F328DA591E00AAC3FC /* TalkPageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837A15F228DA591E00AAC3FC /* TalkPageCache.swift */; }; + 837A15F428DA591E00AAC3FC /* TalkPageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837A15F228DA591E00AAC3FC /* TalkPageCache.swift */; }; + 837A15F528DA591E00AAC3FC /* TalkPageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837A15F228DA591E00AAC3FC /* TalkPageCache.swift */; }; + 837A15F628DA591E00AAC3FC /* TalkPageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837A15F228DA591E00AAC3FC /* TalkPageCache.swift */; }; + 837E619B2510E47400C67494 /* ArticleSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D3FC12223A8BCD0048384B /* ArticleSummary.swift */; }; + 8380753720DC7481000D222C /* ColumnarCollectionViewLayoutInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8380753620DC7481000D222C /* ColumnarCollectionViewLayoutInfo.swift */; }; + 8380753920DC7684000D222C /* ColumarCollectionViewLayoutSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8380753820DC7684000D222C /* ColumarCollectionViewLayoutSection.swift */; }; + 8380753B20DC7D04000D222C /* ColumnarCollectionViewLayoutMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8380753A20DC7D04000D222C /* ColumnarCollectionViewLayoutMetrics.swift */; }; + 8380754520DE627E000D222C /* WMFContentGroup+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8380754420DE627D000D222C /* WMFContentGroup+Display.swift */; }; + 8382F8C720D844C600AE5250 /* ArticleLocationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8C020D8431000AE5250 /* ArticleLocationCollectionViewCell.swift */; }; + 8382F8C820D844C700AE5250 /* ArticleLocationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8C020D8431000AE5250 /* ArticleLocationCollectionViewCell.swift */; }; + 8382F8C920D844C700AE5250 /* ArticleLocationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8C020D8431000AE5250 /* ArticleLocationCollectionViewCell.swift */; }; + 8382F8CA20D844C800AE5250 /* ArticleLocationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8C020D8431000AE5250 /* ArticleLocationCollectionViewCell.swift */; }; + 8382F8CD20D9206000AE5250 /* ImageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8CC20D9206000AE5250 /* ImageCollectionViewCell.swift */; }; + 8382F8CE20D9206000AE5250 /* ImageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8CC20D9206000AE5250 /* ImageCollectionViewCell.swift */; }; + 8382F8CF20D9206000AE5250 /* ImageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8CC20D9206000AE5250 /* ImageCollectionViewCell.swift */; }; + 8382F8D020D9206000AE5250 /* ImageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8CC20D9206000AE5250 /* ImageCollectionViewCell.swift */; }; + 8382F8D320D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8D220D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift */; }; + 8382F8D420D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8D220D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift */; }; + 8382F8D520D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8D220D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift */; }; + 8382F8D620D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8D220D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift */; }; + 8382F8D920D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8D820D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift */; }; + 8382F8DA20D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8D820D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift */; }; + 8382F8DB20D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8D820D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift */; }; + 8382F8DC20D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8382F8D820D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift */; }; + 8383446C1F62EBD000BD5A37 /* UIView+Constraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8383446B1F62EBD000BD5A37 /* UIView+Constraints.swift */; }; + 83836ECC1F615E5B007D1A05 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83836ECA1F615E5B007D1A05 /* ShareViewController.swift */; }; + 83836ECD1F615E5B007D1A05 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83836ECA1F615E5B007D1A05 /* ShareViewController.swift */; }; + 83836ECE1F615E5B007D1A05 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83836ECA1F615E5B007D1A05 /* ShareViewController.swift */; }; + 83836ECF1F615E5B007D1A05 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83836ECA1F615E5B007D1A05 /* ShareViewController.swift */; }; + 83836ED11F615E5B007D1A05 /* ShareViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83836ECB1F615E5B007D1A05 /* ShareViewController.xib */; }; + 83836ED21F615E5B007D1A05 /* ShareViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83836ECB1F615E5B007D1A05 /* ShareViewController.xib */; }; + 83836ED31F615E5B007D1A05 /* ShareViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83836ECB1F615E5B007D1A05 /* ShareViewController.xib */; }; + 83836ED41F615E5B007D1A05 /* ShareViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83836ECB1F615E5B007D1A05 /* ShareViewController.xib */; }; + 8386BDE723857F87007EE89D /* URLParsingAndRoutingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8386BDE623857F87007EE89D /* URLParsingAndRoutingTests.swift */; }; + 8386BDED2386C269007EE89D /* WikipediaURLTranslations.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08423DD2384E2C7005E93A0 /* WikipediaURLTranslations.swift */; }; + 8386BDF12386D3E1007EE89D /* RequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8386BDF02386D3E1007EE89D /* RequestError.swift */; }; + 8386BDF52386D735007EE89D /* ViewController+URLHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8386BDEE2386CAAB007EE89D /* ViewController+URLHandling.swift */; }; + 8386BDF62386D735007EE89D /* ViewController+URLHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8386BDEE2386CAAB007EE89D /* ViewController+URLHandling.swift */; }; + 8386BDF72386D735007EE89D /* ViewController+URLHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8386BDEE2386CAAB007EE89D /* ViewController+URLHandling.swift */; }; + 8386BDF82386D736007EE89D /* ViewController+URLHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8386BDEE2386CAAB007EE89D /* ViewController+URLHandling.swift */; }; + 8386BDFB2386D754007EE89D /* SinglePageWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8386BDFA2386D754007EE89D /* SinglePageWebViewController.swift */; }; + 8386BDFC2386D754007EE89D /* SinglePageWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8386BDFA2386D754007EE89D /* SinglePageWebViewController.swift */; }; + 8386BDFD2386D754007EE89D /* SinglePageWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8386BDFA2386D754007EE89D /* SinglePageWebViewController.swift */; }; + 8386BDFE2386D754007EE89D /* SinglePageWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8386BDFA2386D754007EE89D /* SinglePageWebViewController.swift */; }; + 838790B32858009000067B1D /* TalkPageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 838790B22858009000067B1D /* TalkPageFetcher.swift */; }; + 8387CE8824C8C70A00439D93 /* WMFSecureUnarchiveFromDataTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8387CE8724C8C70A00439D93 /* WMFSecureUnarchiveFromDataTransformer.swift */; }; + 8387CE8F24C99C2600439D93 /* WMFMTLModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 8387CE8D24C99C2600439D93 /* WMFMTLModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8387CE9024C99C2600439D93 /* WMFMTLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 8387CE8E24C99C2600439D93 /* WMFMTLModel.m */; }; + 83927D7B1F70570400051890 /* DisambiguationPagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83927D7A1F70570400051890 /* DisambiguationPagesViewController.swift */; }; + 83927D7C1F70570400051890 /* DisambiguationPagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83927D7A1F70570400051890 /* DisambiguationPagesViewController.swift */; }; + 83927D7D1F70570400051890 /* DisambiguationPagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83927D7A1F70570400051890 /* DisambiguationPagesViewController.swift */; }; + 83927D7E1F70570400051890 /* DisambiguationPagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83927D7A1F70570400051890 /* DisambiguationPagesViewController.swift */; }; + 83927D811F705B7B00051890 /* SearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83927D801F705B7B00051890 /* SearchResultsViewController.swift */; }; + 83927D821F705B7B00051890 /* SearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83927D801F705B7B00051890 /* SearchResultsViewController.swift */; }; + 83927D831F705B7B00051890 /* SearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83927D801F705B7B00051890 /* SearchResultsViewController.swift */; }; + 83927D841F705B7B00051890 /* SearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83927D801F705B7B00051890 /* SearchResultsViewController.swift */; }; + 8392E8681F55801B007E2EE2 /* NSTextAttachment+WMFExtras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8392E8671F557FC0007E2EE2 /* NSTextAttachment+WMFExtras.swift */; }; + 8397601B2811ABC7000FE3B1 /* UNAuthorizationStatus+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8397601A2811ABC7000FE3B1 /* UNAuthorizationStatus+String.swift */; }; + 83987AD020E4FB2C00C92C60 /* UISearchBar+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83987ACF20E4FB2C00C92C60 /* UISearchBar+Theme.swift */; }; + 83987AD120E4FB2C00C92C60 /* UISearchBar+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83987ACF20E4FB2C00C92C60 /* UISearchBar+Theme.swift */; }; + 83987AD220E4FB2C00C92C60 /* UISearchBar+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83987ACF20E4FB2C00C92C60 /* UISearchBar+Theme.swift */; }; + 83987AD320E4FB2C00C92C60 /* UISearchBar+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83987ACF20E4FB2C00C92C60 /* UISearchBar+Theme.swift */; }; + 83A1561420DBE08C0052487B /* ColumnarCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A1561320DBE08C0052487B /* ColumnarCollectionViewLayout.swift */; }; + 83A171D72819B6A60029FB89 /* UNAuthorizationStatus+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8397601A2811ABC7000FE3B1 /* UNAuthorizationStatus+String.swift */; }; + 83A171D82819B6A70029FB89 /* UNAuthorizationStatus+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8397601A2811ABC7000FE3B1 /* UNAuthorizationStatus+String.swift */; }; + 83A171D92819B6A80029FB89 /* UNAuthorizationStatus+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8397601A2811ABC7000FE3B1 /* UNAuthorizationStatus+String.swift */; }; + 83A642752226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A642742226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift */; }; + 83A642762226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A642742226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift */; }; + 83A642772226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A642742226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift */; }; + 83A642782226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A642742226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift */; }; + 83A6D44325100BEE00F9F909 /* Bundle+IsAppExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A6D44225100BEE00F9F909 /* Bundle+IsAppExtension.swift */; }; + 83A72BBF24E70BB200732493 /* localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83A72BBE24E70BB200732493 /* localization.swift */; }; + 83A8E34121A431F100B3FF82 /* WMFLegacySerializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A8E33F21A431F100B3FF82 /* WMFLegacySerializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83A8E34221A431F100B3FF82 /* WMFLegacySerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A8E34021A431F100B3FF82 /* WMFLegacySerializer.m */; }; + 83A933462514C491006EB48A /* WMFCrossProcessCoreDataSynchronizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A933442514C491006EB48A /* WMFCrossProcessCoreDataSynchronizer.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 83A933472514C491006EB48A /* WMFCrossProcessCoreDataSynchronizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A933452514C491006EB48A /* WMFCrossProcessCoreDataSynchronizer.m */; }; + 83ACAA9924E6D112003B3035 /* Collection+AsyncMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = D826C51421766E570012F940 /* Collection+AsyncMap.swift */; }; + 83ACAA9C24E6D8F8003B3035 /* PageNamespace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4170D8229EFC2A00251582 /* PageNamespace.swift */; }; + 83ACAA9E24E6D94C003B3035 /* MWKSearchResult+PageNamespace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ACAA9D24E6D94C003B3035 /* MWKSearchResult+PageNamespace.swift */; }; + 83ACAAA224E6E38A003B3035 /* Wikipedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ACAAA124E6E38A003B3035 /* Wikipedia.swift */; }; + 83ACAAA524E6E47D003B3035 /* Wikipedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ACAAA124E6E38A003B3035 /* Wikipedia.swift */; }; + 83ACAAA724E6E655003B3035 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ACAAA624E6E655003B3035 /* main.swift */; }; + 83ACAAA824E6E6C5003B3035 /* wikipedia-languages.json in Resources */ = {isa = PBXBuildFile; fileRef = 83ACAAA324E6E42A003B3035 /* wikipedia-languages.json */; }; + 83ACAAA924E6E6E3003B3035 /* wikipedia-namespaces in Resources */ = {isa = PBXBuildFile; fileRef = B077A51323861E2200223526 /* wikipedia-namespaces */; }; + 83ACAAAB24E6E745003B3035 /* WikipediaLookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ACAAAA24E6E745003B3035 /* WikipediaLookup.swift */; }; + 83ACAAAD24E6EED9003B3035 /* WikipediaSiteInfoLookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ACAAAC24E6EED9003B3035 /* WikipediaSiteInfoLookup.swift */; }; + 83ACAAAE24E6EF0B003B3035 /* WikipediaSiteInfoLookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ACAAAC24E6EED9003B3035 /* WikipediaSiteInfoLookup.swift */; }; + 83ACF8E528E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ACF8E428E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift */; }; + 83ACF8E628E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ACF8E428E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift */; }; + 83ACF8E728E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ACF8E428E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift */; }; + 83ACF8E828E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ACF8E428E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift */; }; + 83AE1C811F34BB59004B62E0 /* ImageDimmingExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7683C01F30C56300A487AA /* ImageDimmingExampleViewController.swift */; }; + 83AE1C821F34BB5A004B62E0 /* ImageDimmingExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7683C01F30C56300A487AA /* ImageDimmingExampleViewController.swift */; }; + 83AE1C831F34BB5A004B62E0 /* ImageDimmingExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7683C01F30C56300A487AA /* ImageDimmingExampleViewController.swift */; }; + 83AE1C861F34BB64004B62E0 /* ImageDimmingExampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA7683C11F30C56300A487AA /* ImageDimmingExampleViewController.xib */; }; + 83AE1C871F34BB65004B62E0 /* ImageDimmingExampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA7683C11F30C56300A487AA /* ImageDimmingExampleViewController.xib */; }; + 83AE1C881F34BB65004B62E0 /* ImageDimmingExampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA7683C11F30C56300A487AA /* ImageDimmingExampleViewController.xib */; }; + 83AF34F724D3341E000046D6 /* BackgroundTasks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83AF34F624D3341D000046D6 /* BackgroundTasks.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 83AF34F824D33428000046D6 /* BackgroundTasks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83AF34F624D3341D000046D6 /* BackgroundTasks.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 83AF34F924D33432000046D6 /* BackgroundTasks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83AF34F624D3341D000046D6 /* BackgroundTasks.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 83AF34FA24D3343B000046D6 /* BackgroundTasks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83AF34F624D3341D000046D6 /* BackgroundTasks.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 83B019D024F6ACAA0014B5EF /* WikipediaLanguageCommandLineUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B019CD24F6ACAA0014B5EF /* WikipediaLanguageCommandLineUtility.swift */; }; + 83B019D124F6ACAA0014B5EF /* WikipediaLanguageCommandLineUtilityAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B019CE24F6ACAA0014B5EF /* WikipediaLanguageCommandLineUtilityAPI.swift */; }; + 83B019D224F6ACAA0014B5EF /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B019CF24F6ACAA0014B5EF /* main.swift */; }; + 83B019D624F6C31B0014B5EF /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83B019D524F6C31B0014B5EF /* WidgetKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 83B01F7223DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7123DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift */; }; + 83B01F7323DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7123DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift */; }; + 83B01F7423DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7123DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift */; }; + 83B01F7523DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7123DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift */; }; + 83B01F7723DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7623DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift */; }; + 83B01F7823DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7623DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift */; }; + 83B01F7923DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7623DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift */; }; + 83B01F7A23DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7623DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift */; }; + 83B01F7C23DB0BA2001185F4 /* ArticleViewController+Editing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7B23DB0BA2001185F4 /* ArticleViewController+Editing.swift */; }; + 83B01F7D23DB0BA2001185F4 /* ArticleViewController+Editing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7B23DB0BA2001185F4 /* ArticleViewController+Editing.swift */; }; + 83B01F7E23DB0BA2001185F4 /* ArticleViewController+Editing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7B23DB0BA2001185F4 /* ArticleViewController+Editing.swift */; }; + 83B01F7F23DB0BA2001185F4 /* ArticleViewController+Editing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F7B23DB0BA2001185F4 /* ArticleViewController+Editing.swift */; }; + 83B01F8123DB1235001185F4 /* SectionFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F8023DB1235001185F4 /* SectionFetcher.swift */; }; + 83B01F8223DB1235001185F4 /* SectionFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F8023DB1235001185F4 /* SectionFetcher.swift */; }; + 83B01F8323DB1235001185F4 /* SectionFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F8023DB1235001185F4 /* SectionFetcher.swift */; }; + 83B01F8423DB1235001185F4 /* SectionFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F8023DB1235001185F4 /* SectionFetcher.swift */; }; + 83B01F8B23DB399E001185F4 /* DescriptionEditViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83B01F8A23DB399E001185F4 /* DescriptionEditViewController.storyboard */; }; + 83B01F8C23DB399E001185F4 /* DescriptionEditViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83B01F8A23DB399E001185F4 /* DescriptionEditViewController.storyboard */; }; + 83B01F8D23DB399E001185F4 /* DescriptionEditViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83B01F8A23DB399E001185F4 /* DescriptionEditViewController.storyboard */; }; + 83B01F8E23DB399E001185F4 /* DescriptionEditViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83B01F8A23DB399E001185F4 /* DescriptionEditViewController.storyboard */; }; + 83B01F9023DB41BE001185F4 /* ArticleViewController+Sharing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F8F23DB41BE001185F4 /* ArticleViewController+Sharing.swift */; }; + 83B01F9123DB41BE001185F4 /* ArticleViewController+Sharing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F8F23DB41BE001185F4 /* ArticleViewController+Sharing.swift */; }; + 83B01F9223DB41BE001185F4 /* ArticleViewController+Sharing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F8F23DB41BE001185F4 /* ArticleViewController+Sharing.swift */; }; + 83B01F9323DB41BE001185F4 /* ArticleViewController+Sharing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F8F23DB41BE001185F4 /* ArticleViewController+Sharing.swift */; }; + 83B01F9523DB41D7001185F4 /* ArticleViewController+FindInPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F9423DB41D7001185F4 /* ArticleViewController+FindInPage.swift */; }; + 83B01F9623DB41D7001185F4 /* ArticleViewController+FindInPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F9423DB41D7001185F4 /* ArticleViewController+FindInPage.swift */; }; + 83B01F9723DB41D7001185F4 /* ArticleViewController+FindInPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F9423DB41D7001185F4 /* ArticleViewController+FindInPage.swift */; }; + 83B01F9823DB41D7001185F4 /* ArticleViewController+FindInPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F9423DB41D7001185F4 /* ArticleViewController+FindInPage.swift */; }; + 83B01F9A23DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F9923DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift */; }; + 83B01F9B23DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F9923DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift */; }; + 83B01F9C23DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F9923DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift */; }; + 83B01F9D23DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B01F9923DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift */; }; + 83B1218427FC8750006B8CCC /* RemoteNotificationsFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B1218327FC8750006B8CCC /* RemoteNotificationsFunnel.swift */; }; + 83B4CDBF20E3DCD6007D5A6E /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B4CDBE20E3DCD6007D5A6E /* SearchViewController.swift */; }; + 83B4CDC020E3DCD6007D5A6E /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B4CDBE20E3DCD6007D5A6E /* SearchViewController.swift */; }; + 83B4CDC120E3DCD6007D5A6E /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B4CDBE20E3DCD6007D5A6E /* SearchViewController.swift */; }; + 83B4CDC220E3DCD6007D5A6E /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B4CDBE20E3DCD6007D5A6E /* SearchViewController.swift */; }; + 83B87ECC1F71431F00F342F1 /* ArticleCollectionViewCell+ListDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B87EC61F713BC200F342F1 /* ArticleCollectionViewCell+ListDisplay.swift */; }; + 83BBBE5623F56F9400AD0994 /* LocaleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83BBBE5523F56F9400AD0994 /* LocaleTests.swift */; }; + 83C0656B23D23220001821BC /* TableOfContentsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C0656A23D23220001821BC /* TableOfContentsItem.swift */; }; + 83C0656C23D23220001821BC /* TableOfContentsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C0656A23D23220001821BC /* TableOfContentsItem.swift */; }; + 83C0656D23D23220001821BC /* TableOfContentsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C0656A23D23220001821BC /* TableOfContentsItem.swift */; }; + 83C0656E23D23220001821BC /* TableOfContentsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C0656A23D23220001821BC /* TableOfContentsItem.swift */; }; + 83C06882292EC85700DF1403 /* TalkPageFormattingToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06881292EC85700DF1403 /* TalkPageFormattingToolbarView.swift */; }; + 83C06883292EC85700DF1403 /* TalkPageFormattingToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06881292EC85700DF1403 /* TalkPageFormattingToolbarView.swift */; }; + 83C06884292EC85700DF1403 /* TalkPageFormattingToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06881292EC85700DF1403 /* TalkPageFormattingToolbarView.swift */; }; + 83C06885292EC85700DF1403 /* TalkPageFormattingToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06881292EC85700DF1403 /* TalkPageFormattingToolbarView.swift */; }; + 83C06887292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06886292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift */; }; + 83C06888292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06886292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift */; }; + 83C06889292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06886292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift */; }; + 83C0688A292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06886292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift */; }; + 83C0688E292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C0688D292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift */; }; + 83C0688F292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C0688D292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift */; }; + 83C06890292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C0688D292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift */; }; + 83C06891292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C0688D292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift */; }; + 83C06893292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06892292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift */; }; + 83C06894292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06892292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift */; }; + 83C06895292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06892292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift */; }; + 83C06896292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C06892292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift */; }; + 83C643582239508600FC16BF /* RandomArticleFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C6435222394F0300FC16BF /* RandomArticleFetcher.swift */; }; + 83CA612A20D1675800EF0C4A /* ExploreCardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83CA612920D1675800EF0C4A /* ExploreCardViewController.swift */; }; + 83CA612B20D1675800EF0C4A /* ExploreCardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83CA612920D1675800EF0C4A /* ExploreCardViewController.swift */; }; + 83CA612D20D1675800EF0C4A /* ExploreCardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83CA612920D1675800EF0C4A /* ExploreCardViewController.swift */; }; + 83CCB289209CA4E600D31565 /* NSRegularExpression+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 83CCB287209CA4E600D31565 /* NSRegularExpression+HTML.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83CCB28A209CA4E600D31565 /* NSRegularExpression+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CCB288209CA4E600D31565 /* NSRegularExpression+HTML.m */; }; + 83CDC7D425122A1700A2F8A1 /* PermanentCacheController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83CDC7D325122A1700A2F8A1 /* PermanentCacheController.swift */; }; + 83D05189246EA70D00DA92C6 /* NSMutableAttributedString+Mutations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D05188246EA70D00DA92C6 /* NSMutableAttributedString+Mutations.swift */; }; + 83D5EC871F755E1F003DE6F2 /* SwipeableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D5EC861F755E1F003DE6F2 /* SwipeableCell.swift */; }; + 83DAA9B023FEB611002D5716 /* ReferenceBackLinksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DAA9AF23FEB611002D5716 /* ReferenceBackLinksViewController.swift */; }; + 83DAA9B123FEB611002D5716 /* ReferenceBackLinksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DAA9AF23FEB611002D5716 /* ReferenceBackLinksViewController.swift */; }; + 83DAA9B223FEB611002D5716 /* ReferenceBackLinksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DAA9AF23FEB611002D5716 /* ReferenceBackLinksViewController.swift */; }; + 83DAA9B323FEB611002D5716 /* ReferenceBackLinksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DAA9AF23FEB611002D5716 /* ReferenceBackLinksViewController.swift */; }; + 83DB0A5723EEDE2100DA5F58 /* MobileviewToMobileHTMLConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DB0A5623EEDE2100DA5F58 /* MobileviewToMobileHTMLConverter.swift */; }; + 83DB0A5923EEDE2D00DA5F58 /* MWKDataStore+LegacyMobileview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B087F8D023E3AE3B00ACA012 /* MWKDataStore+LegacyMobileview.swift */; }; + 83DB0A5A23EEDE2D00DA5F58 /* MWKDataStore+LegacyMobileview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B087F8D023E3AE3B00ACA012 /* MWKDataStore+LegacyMobileview.swift */; }; + 83DB0A5B23EEDE2D00DA5F58 /* MWKDataStore+LegacyMobileview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B087F8D023E3AE3B00ACA012 /* MWKDataStore+LegacyMobileview.swift */; }; + 83DB0A5C23EEDE2E00DA5F58 /* MWKDataStore+LegacyMobileview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B087F8D023E3AE3B00ACA012 /* MWKDataStore+LegacyMobileview.swift */; }; + 83DB0A5E23EEDE4400DA5F58 /* LegacyArticle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DB0A5D23EEDE4400DA5F58 /* LegacyArticle.swift */; }; + 83DB4410244A57590046FABE /* RootNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DB440F244A57590046FABE /* RootNavigationController.swift */; }; + 83DB4411244A57590046FABE /* RootNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DB440F244A57590046FABE /* RootNavigationController.swift */; }; + 83DB4412244A57590046FABE /* RootNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DB440F244A57590046FABE /* RootNavigationController.swift */; }; + 83DB4413244A57590046FABE /* RootNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DB440F244A57590046FABE /* RootNavigationController.swift */; }; + 83DE45B92449C09B00671878 /* SplashScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DE45B72449C09B00671878 /* SplashScreenViewController.swift */; }; + 83DE45BA2449C09B00671878 /* SplashScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DE45B72449C09B00671878 /* SplashScreenViewController.swift */; }; + 83DE45BB2449C09B00671878 /* SplashScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DE45B72449C09B00671878 /* SplashScreenViewController.swift */; }; + 83DE45BC2449C09B00671878 /* SplashScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83DE45B72449C09B00671878 /* SplashScreenViewController.swift */; }; + 83DF1D1424F53878007E08D8 /* WMFPreferredLanguageInfoProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 83DF1D1324F53878007E08D8 /* WMFPreferredLanguageInfoProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83E3E7252440F1FE00AA2E9A /* LoadingAnimationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E3E7242440F1FE00AA2E9A /* LoadingAnimationViewController.swift */; }; + 83E3E7262440F1FE00AA2E9A /* LoadingAnimationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E3E7242440F1FE00AA2E9A /* LoadingAnimationViewController.swift */; }; + 83E3E7272440F1FE00AA2E9A /* LoadingAnimationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E3E7242440F1FE00AA2E9A /* LoadingAnimationViewController.swift */; }; + 83E3E7282440F1FE00AA2E9A /* LoadingAnimationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E3E7242440F1FE00AA2E9A /* LoadingAnimationViewController.swift */; }; + 83E3E72A2440F24300AA2E9A /* LoadingAnimationViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83E3E7292440F24300AA2E9A /* LoadingAnimationViewController.xib */; }; + 83E3E72B2440F24300AA2E9A /* LoadingAnimationViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83E3E7292440F24300AA2E9A /* LoadingAnimationViewController.xib */; }; + 83E3E72C2440F24300AA2E9A /* LoadingAnimationViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83E3E7292440F24300AA2E9A /* LoadingAnimationViewController.xib */; }; + 83E3E72D2440F24300AA2E9A /* LoadingAnimationViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83E3E7292440F24300AA2E9A /* LoadingAnimationViewController.xib */; }; + 83E52BB41F681F940045E776 /* ShareAFactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E52BB21F681F940045E776 /* ShareAFactViewController.swift */; }; + 83E52BB51F681F940045E776 /* ShareAFactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E52BB21F681F940045E776 /* ShareAFactViewController.swift */; }; + 83E52BB61F681F940045E776 /* ShareAFactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E52BB21F681F940045E776 /* ShareAFactViewController.swift */; }; + 83E52BB71F681F940045E776 /* ShareAFactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E52BB21F681F940045E776 /* ShareAFactViewController.swift */; }; + 83E52BB91F681F940045E776 /* ShareAFactViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83E52BB31F681F940045E776 /* ShareAFactViewController.xib */; }; + 83E52BBA1F681F940045E776 /* ShareAFactViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83E52BB31F681F940045E776 /* ShareAFactViewController.xib */; }; + 83E52BBB1F681F940045E776 /* ShareAFactViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83E52BB31F681F940045E776 /* ShareAFactViewController.xib */; }; + 83E52BBC1F681F940045E776 /* ShareAFactViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83E52BB31F681F940045E776 /* ShareAFactViewController.xib */; }; + 83E52BBF1F682E3E0045E776 /* LicenseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E52BBE1F682E3E0045E776 /* LicenseView.swift */; }; + 83E52BC01F682E3E0045E776 /* LicenseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E52BBE1F682E3E0045E776 /* LicenseView.swift */; }; + 83E52BC11F682E3E0045E776 /* LicenseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E52BBE1F682E3E0045E776 /* LicenseView.swift */; }; + 83E52BC21F682E3E0045E776 /* LicenseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E52BBE1F682E3E0045E776 /* LicenseView.swift */; }; + 83E776A320FFA4D700E26A47 /* DetailTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E776A220FFA4D700E26A47 /* DetailTransition.swift */; }; + 83E776A420FFA4D700E26A47 /* DetailTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E776A220FFA4D700E26A47 /* DetailTransition.swift */; }; + 83E776A520FFA4D700E26A47 /* DetailTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E776A220FFA4D700E26A47 /* DetailTransition.swift */; }; + 83E776A620FFA4D700E26A47 /* DetailTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E776A220FFA4D700E26A47 /* DetailTransition.swift */; }; + 83E880E823EB19270087223F /* MediaList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E880E723EB19270087223F /* MediaList.swift */; }; + 83E9A2121F56FE5E006EB091 /* FakeProgressController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E9A2111F56FE5E006EB091 /* FakeProgressController.swift */; }; + 83E9C45B2419193C006BDBC2 /* WikipediaSiteInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E9C45A2419193C006BDBC2 /* WikipediaSiteInfo.swift */; }; + 83ED2E24289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ED2E23289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift */; }; + 83ED2E25289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ED2E23289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift */; }; + 83ED2E26289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ED2E23289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift */; }; + 83ED2E27289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83ED2E23289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift */; }; + 83EDC4C128B424B5007D0192 /* VanishAccountPopUpAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83EDC4C028B424B5007D0192 /* VanishAccountPopUpAlertView.swift */; }; + 83EDC4C228B424B5007D0192 /* VanishAccountPopUpAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83EDC4C028B424B5007D0192 /* VanishAccountPopUpAlertView.swift */; }; + 83EDC4C328B424B6007D0192 /* VanishAccountPopUpAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83EDC4C028B424B5007D0192 /* VanishAccountPopUpAlertView.swift */; }; + 83EDC4C428B424B6007D0192 /* VanishAccountPopUpAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83EDC4C028B424B5007D0192 /* VanishAccountPopUpAlertView.swift */; }; + 83EE476A20D019A100A21F34 /* ExploreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83EE476920D019A100A21F34 /* ExploreViewController.swift */; }; + 83EE476B20D019A100A21F34 /* ExploreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83EE476920D019A100A21F34 /* ExploreViewController.swift */; }; + 83EE476C20D019A100A21F34 /* ExploreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83EE476920D019A100A21F34 /* ExploreViewController.swift */; }; + 83EE476D20D019A100A21F34 /* ExploreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83EE476920D019A100A21F34 /* ExploreViewController.swift */; }; + 83EE477020D01A9A00A21F34 /* ExploreCardCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83EE476F20D01A9A00A21F34 /* ExploreCardCollectionViewCell.swift */; }; + 83F1095B23D07E5D003F3E9E /* APIURLComponentsBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1095623D07E3B003F3E9E /* APIURLComponentsBuilder.swift */; }; + 83F1095F23D09F80003F3E9E /* ArticleViewController+WIconPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1095E23D09F80003F3E9E /* ArticleViewController+WIconPopover.swift */; }; + 83F1096023D09F80003F3E9E /* ArticleViewController+WIconPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1095E23D09F80003F3E9E /* ArticleViewController+WIconPopover.swift */; }; + 83F1096123D09F80003F3E9E /* ArticleViewController+WIconPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1095E23D09F80003F3E9E /* ArticleViewController+WIconPopover.swift */; }; + 83F1096223D09F80003F3E9E /* ArticleViewController+WIconPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1095E23D09F80003F3E9E /* ArticleViewController+WIconPopover.swift */; }; + 83F1096423D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096323D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift */; }; + 83F1096523D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096323D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift */; }; + 83F1096623D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096323D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift */; }; + 83F1096723D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096323D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift */; }; + 83F1096923D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096823D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift */; }; + 83F1096A23D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096823D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift */; }; + 83F1096B23D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096823D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift */; }; + 83F1096C23D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096823D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift */; }; + 83F1096E23D0E787003F3E9E /* RandomArticleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096D23D0E787003F3E9E /* RandomArticleViewController.swift */; }; + 83F1096F23D0E787003F3E9E /* RandomArticleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096D23D0E787003F3E9E /* RandomArticleViewController.swift */; }; + 83F1097023D0E787003F3E9E /* RandomArticleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096D23D0E787003F3E9E /* RandomArticleViewController.swift */; }; + 83F1097123D0E787003F3E9E /* RandomArticleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1096D23D0E787003F3E9E /* RandomArticleViewController.swift */; }; + 83F1097323D0F115003F3E9E /* HelpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1097223D0F115003F3E9E /* HelpViewController.swift */; }; + 83F1097423D0F115003F3E9E /* HelpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1097223D0F115003F3E9E /* HelpViewController.swift */; }; + 83F1097523D0F115003F3E9E /* HelpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1097223D0F115003F3E9E /* HelpViewController.swift */; }; + 83F1097623D0F115003F3E9E /* HelpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F1097223D0F115003F3E9E /* HelpViewController.swift */; }; + 83F26B2A220B62EC002D87A4 /* SectionEditorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F26B29220B62EC002D87A4 /* SectionEditorButton.swift */; }; + 83F26B2B220B62EC002D87A4 /* SectionEditorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F26B29220B62EC002D87A4 /* SectionEditorButton.swift */; }; + 83F26B2C220B62EC002D87A4 /* SectionEditorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F26B29220B62EC002D87A4 /* SectionEditorButton.swift */; }; + 83F26B2D220B62EC002D87A4 /* SectionEditorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F26B29220B62EC002D87A4 /* SectionEditorButton.swift */; }; + 83FBE96F1F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FBE96E1F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift */; }; + 83FBE9701F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FBE96E1F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift */; }; + 83FBE9711F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FBE96E1F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift */; }; + 83FBE9721F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FBE96E1F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift */; }; + 83FBE9751F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FBE9741F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift */; }; + 83FBE9761F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FBE9741F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift */; }; + 83FBE9771F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FBE9741F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift */; }; + 83FBE9781F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FBE9741F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift */; }; + 83FDE799293564AC006D55FE /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FDE798293564AC006D55FE /* Link.swift */; }; + 83FDE79A293564AC006D55FE /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FDE798293564AC006D55FE /* Link.swift */; }; + 83FDE79B293564AC006D55FE /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FDE798293564AC006D55FE /* Link.swift */; }; + 83FDE79C293564AC006D55FE /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FDE798293564AC006D55FE /* Link.swift */; }; + 83FFFFBA29AEC094005506A0 /* Components in Frameworks */ = {isa = PBXBuildFile; productRef = 83FFFFB929AEC094005506A0 /* Components */; }; + 982800D624D302BF004B1850 /* EventPlatformClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 982800D524D302BF004B1850 /* EventPlatformClient.swift */; }; + A452F9F824081A5500D8ED09 /* MockCLLocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A452F9F624081A5500D8ED09 /* MockCLLocationManager.swift */; }; + A452F9F924081A5500D8ED09 /* MockCLHeading.swift in Sources */ = {isa = PBXBuildFile; fileRef = A452F9F724081A5500D8ED09 /* MockCLHeading.swift */; }; + A452F9FB24081A7200D8ED09 /* LocationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A452F9FA24081A7200D8ED09 /* LocationManagerTests.swift */; }; + A452F9FD24081B0200D8ED09 /* MockUIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = A452F9FC24081B0200D8ED09 /* MockUIDevice.swift */; }; + A4C558BD2403D74100AFBFDC /* LocationManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4C558BC2403D74100AFBFDC /* LocationManagerProtocol.swift */; }; + A4C558BF2403D7E300AFBFDC /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4C558BE2403D7E200AFBFDC /* LocationManager.swift */; }; + B00050141C52D73800515F70 /* UIApplication+RTL.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00050131C52D73800515F70 /* UIApplication+RTL.swift */; }; + B0016CB921354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0016CB821354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift */; }; + B0016CBA21354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0016CB821354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift */; }; + B0016CBB21354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0016CB821354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift */; }; + B0016CBC21354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0016CB821354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift */; }; + B0016CBF2136105900FA1096 /* SetupButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0016CBE2136105900FA1096 /* SetupButton.swift */; }; + B0016CC321362DB300FA1096 /* SetupGradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0016CC221362DB000FA1096 /* SetupGradientView.swift */; }; + B0016CC421362DB300FA1096 /* SetupGradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0016CC221362DB000FA1096 /* SetupGradientView.swift */; }; + B0016CC521362DB300FA1096 /* SetupGradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0016CC221362DB000FA1096 /* SetupGradientView.swift */; }; + B0016CC621362DB300FA1096 /* SetupGradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0016CC221362DB000FA1096 /* SetupGradientView.swift */; }; + B00DDEDB1DB4B76B00615FA2 /* UIView+WMFSubviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEDA1DB4B76B00615FA2 /* UIView+WMFSubviews.swift */; }; + B00DDEDD1DB591C400615FA2 /* WMFWelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEDC1DB591C400615FA2 /* WMFWelcomeContainerViewController.swift */; }; + B00DDEE31DB5B16E00615FA2 /* WMFWelcomeAnimationViewControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEE21DB5B16E00615FA2 /* WMFWelcomeAnimationViewControllers.swift */; }; + B010E1A81E723E3600CFE1CD /* WMFAuthLinkLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B010E1A71E723E3600CFE1CD /* WMFAuthLinkLabel.swift */; }; + B010E1A91E723E3600CFE1CD /* WMFAuthLinkLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B010E1A71E723E3600CFE1CD /* WMFAuthLinkLabel.swift */; }; + B01490A11DB96BD6007F5391 /* WMFReferencePanels.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B01490A01DB96BD6007F5391 /* WMFReferencePanels.storyboard */; }; + B01490A31DB96E5F007F5391 /* WMFReferencePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01490A21DB96E5F007F5391 /* WMFReferencePageViewController.swift */; }; + B01490A51DB96EA7007F5391 /* WMFReferencePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01490A41DB96EA7007F5391 /* WMFReferencePanelViewController.swift */; }; + B01662B31D1B8CAB006F4544 /* NSURL+WMFQueryParametersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B01662B11D1B8A40006F4544 /* NSURL+WMFQueryParametersTests.m */; }; + B018501620BC846600A508F1 /* WMF.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + B018501820BC84E300A508F1 /* WMF.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + B019FED02029347300BDE9C9 /* UIStackView+SubviewVerification.swift in Sources */ = {isa = PBXBuildFile; fileRef = B019FECE2029347200BDE9C9 /* UIStackView+SubviewVerification.swift */; }; + B01CFC5F1E70B00100B3546A /* WMFDeleteBackwardReportingTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01CFC5E1E70B00100B3546A /* WMFDeleteBackwardReportingTextField.swift */; }; + B01CFC611E71069000B3546A /* String?+WMFExtras.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01CFC601E71069000B3546A /* String?+WMFExtras.swift */; }; + B01E3AF921F986750015B715 /* PreviewWebViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E3AF821F986750015B715 /* PreviewWebViewContainer.swift */; }; + B01E3AFA21F986750015B715 /* PreviewWebViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E3AF821F986750015B715 /* PreviewWebViewContainer.swift */; }; + B01E3AFB21F986750015B715 /* PreviewWebViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E3AF821F986750015B715 /* PreviewWebViewContainer.swift */; }; + B01E3AFC21F986750015B715 /* PreviewWebViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E3AF821F986750015B715 /* PreviewWebViewContainer.swift */; }; + B01E3AFF21F98BFF0015B715 /* EditPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E3AFE21F98BFF0015B715 /* EditPreviewViewController.swift */; }; + B01E3B0021F98BFF0015B715 /* EditPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E3AFE21F98BFF0015B715 /* EditPreviewViewController.swift */; }; + B01E3B0121F98BFF0015B715 /* EditPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E3AFE21F98BFF0015B715 /* EditPreviewViewController.swift */; }; + B01E3B0221F98BFF0015B715 /* EditPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E3AFE21F98BFF0015B715 /* EditPreviewViewController.swift */; }; + B01E54AF206479CC00374FEE /* ProgressContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E54AE206479CC00374FEE /* ProgressContainer.swift */; }; + B01E54B0206479CC00374FEE /* ProgressContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E54AE206479CC00374FEE /* ProgressContainer.swift */; }; + B01E54B1206479CC00374FEE /* ProgressContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E54AE206479CC00374FEE /* ProgressContainer.swift */; }; + B01E54B2206479CC00374FEE /* ProgressContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E54AE206479CC00374FEE /* ProgressContainer.swift */; }; + B01EA07E2022856300813726 /* ScrollableEducationPanelView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B01EA07D2022856200813726 /* ScrollableEducationPanelView.xib */; }; + B01EA07F2022856300813726 /* ScrollableEducationPanelView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B01EA07D2022856200813726 /* ScrollableEducationPanelView.xib */; }; + B01EA0802022856300813726 /* ScrollableEducationPanelView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B01EA07D2022856200813726 /* ScrollableEducationPanelView.xib */; }; + B01EA0812022856300813726 /* ScrollableEducationPanelView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B01EA07D2022856200813726 /* ScrollableEducationPanelView.xib */; }; + B02376B41D6ECCF9007DBA73 /* UIViewController+WMFDynamicHeightPopoverMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B02376B31D6ECCF9007DBA73 /* UIViewController+WMFDynamicHeightPopoverMessage.m */; }; + B0267CE91E316A79006B6D8D /* WMFForgotPasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0267CE81E316A79006B6D8D /* WMFForgotPasswordViewController.storyboard */; }; + B0267CED1E316AE3006B6D8D /* WMFForgotPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0267CEC1E316AE3006B6D8D /* WMFForgotPasswordViewController.swift */; }; + B0267CF31E32A0CB006B6D8D /* WMFPasswordResetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0267CF21E32A0CB006B6D8D /* WMFPasswordResetter.swift */; }; + B027447D1E6253E200E7B248 /* WMFScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027447C1E6253E200E7B248 /* WMFScrollViewController.swift */; }; + B027447F1E665D9F00E7B248 /* UIViewController+WMFChildViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027447E1E665D9F00E7B248 /* UIViewController+WMFChildViewController.swift */; }; + B027FD281E678F5C005644A9 /* WMFAuthButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027FD271E678F5C005644A9 /* WMFAuthButton.swift */; }; + B02B82751C696ECA00B19309 /* WMFSettingsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B02B82731C696ECA00B19309 /* WMFSettingsTableViewCell.m */; }; + B02B82761C696ECA00B19309 /* WMFSettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B02B82741C696ECA00B19309 /* WMFSettingsTableViewCell.xib */; }; + B02F96661DFA11DC007DA007 /* WMFArticleListTableViewCell+DynamicTypeFontTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B02F96651DFA11DC007DA007 /* WMFArticleListTableViewCell+DynamicTypeFontTests.swift */; }; + B031032D1F677BEC00E2FCF6 /* WMFWelcomeExplorationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03103271F677BB400E2FCF6 /* WMFWelcomeExplorationViewController.swift */; }; + B031032E1F677BEC00E2FCF6 /* WMFWelcomeExplorationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03103271F677BB400E2FCF6 /* WMFWelcomeExplorationViewController.swift */; }; + B031032F1F677BED00E2FCF6 /* WMFWelcomeExplorationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03103271F677BB400E2FCF6 /* WMFWelcomeExplorationViewController.swift */; }; + B03103301F677BED00E2FCF6 /* WMFWelcomeExplorationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03103271F677BB400E2FCF6 /* WMFWelcomeExplorationViewController.swift */; }; + B0338A841DBF0AF20012F588 /* WMFReferencePageBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0338A831DBF0AF20012F588 /* WMFReferencePageBackgroundView.swift */; }; + B0379A2C1D8B756C00D973CF /* WMFReferencePopoverMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0379A2A1D8B756C00D973CF /* WMFReferencePopoverMessageViewController.m */; }; + B0379A371D8B9D2400D973CF /* WMFReferencePopoverMessageViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0379A361D8B9D2400D973CF /* WMFReferencePopoverMessageViewController.storyboard */; }; + B0408C552127F2C100AC76CE /* WMFImageGalleryGradientViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0408C542127F2C100AC76CE /* WMFImageGalleryGradientViews.swift */; }; + B0408C562127F2C100AC76CE /* WMFImageGalleryGradientViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0408C542127F2C100AC76CE /* WMFImageGalleryGradientViews.swift */; }; + B0408C572127F2C100AC76CE /* WMFImageGalleryGradientViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0408C542127F2C100AC76CE /* WMFImageGalleryGradientViews.swift */; }; + B0408C582127F2C100AC76CE /* WMFImageGalleryGradientViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0408C542127F2C100AC76CE /* WMFImageGalleryGradientViews.swift */; }; + B0421AA2206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0421AA1206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift */; }; + B0421AA3206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0421AA1206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift */; }; + B0421AA4206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0421AA1206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift */; }; + B0421AA5206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0421AA1206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift */; }; + B0432344210680A900A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0432343210680A800A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift */; }; + B0432345210680A900A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0432343210680A800A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift */; }; + B0432346210680A900A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0432343210680A800A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift */; }; + B0432347210680A900A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0432343210680A800A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift */; }; + B04AE84C21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04AE84B21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift */; }; + B04AE84D21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04AE84B21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift */; }; + B04AE84E21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04AE84B21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift */; }; + B04AE84F21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04AE84B21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift */; }; + B04C444B1E56966B00C6DFB0 /* Array+WMFAllFieldsFilled.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04C444A1E56966B00C6DFB0 /* Array+WMFAllFieldsFilled.swift */; }; + B0524AF12144D7BE00D8FD8D /* DescriptionHelpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524AEF2144D7BE00D8FD8D /* DescriptionHelpViewController.swift */; }; + B0524AF22144D7BE00D8FD8D /* DescriptionHelpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524AEF2144D7BE00D8FD8D /* DescriptionHelpViewController.swift */; }; + B0524AF32144D7BE00D8FD8D /* DescriptionHelpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524AEF2144D7BE00D8FD8D /* DescriptionHelpViewController.swift */; }; + B0524AF42144D7BE00D8FD8D /* DescriptionHelpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524AEF2144D7BE00D8FD8D /* DescriptionHelpViewController.swift */; }; + B0524AF62144D7BE00D8FD8D /* DescriptionHelpViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0524AF02144D7BE00D8FD8D /* DescriptionHelpViewController.xib */; }; + B0524AF72144D7BE00D8FD8D /* DescriptionHelpViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0524AF02144D7BE00D8FD8D /* DescriptionHelpViewController.xib */; }; + B0524AF82144D7BE00D8FD8D /* DescriptionHelpViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0524AF02144D7BE00D8FD8D /* DescriptionHelpViewController.xib */; }; + B0524AF92144D7BE00D8FD8D /* DescriptionHelpViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0524AF02144D7BE00D8FD8D /* DescriptionHelpViewController.xib */; }; + B0524B0321484FB400D8FD8D /* DescriptionWelcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0524B0221484FB400D8FD8D /* DescriptionWelcome.storyboard */; }; + B0524B0421484FB400D8FD8D /* DescriptionWelcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0524B0221484FB400D8FD8D /* DescriptionWelcome.storyboard */; }; + B0524B0521484FB400D8FD8D /* DescriptionWelcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0524B0221484FB400D8FD8D /* DescriptionWelcome.storyboard */; }; + B0524B0621484FB400D8FD8D /* DescriptionWelcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0524B0221484FB400D8FD8D /* DescriptionWelcome.storyboard */; }; + B0524B1F214854E900D8FD8D /* DescriptionWelcomeInitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B09214854E500D8FD8D /* DescriptionWelcomeInitialViewController.swift */; }; + B0524B20214854E900D8FD8D /* DescriptionWelcomeInitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B09214854E500D8FD8D /* DescriptionWelcomeInitialViewController.swift */; }; + B0524B21214854E900D8FD8D /* DescriptionWelcomeInitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B09214854E500D8FD8D /* DescriptionWelcomeInitialViewController.swift */; }; + B0524B22214854E900D8FD8D /* DescriptionWelcomeInitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B09214854E500D8FD8D /* DescriptionWelcomeInitialViewController.swift */; }; + B0524B29214854E900D8FD8D /* DescriptionWelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B0B214854E600D8FD8D /* DescriptionWelcomePanelViewController.swift */; }; + B0524B2A214854E900D8FD8D /* DescriptionWelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B0B214854E600D8FD8D /* DescriptionWelcomePanelViewController.swift */; }; + B0524B2B214854E900D8FD8D /* DescriptionWelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B0B214854E600D8FD8D /* DescriptionWelcomePanelViewController.swift */; }; + B0524B2C214854E900D8FD8D /* DescriptionWelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B0B214854E600D8FD8D /* DescriptionWelcomePanelViewController.swift */; }; + B0524B47214854E900D8FD8D /* DescriptionWelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B11214854E700D8FD8D /* DescriptionWelcomePageViewController.swift */; }; + B0524B48214854E900D8FD8D /* DescriptionWelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B11214854E700D8FD8D /* DescriptionWelcomePageViewController.swift */; }; + B0524B49214854E900D8FD8D /* DescriptionWelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B11214854E700D8FD8D /* DescriptionWelcomePageViewController.swift */; }; + B0524B4A214854E900D8FD8D /* DescriptionWelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B11214854E700D8FD8D /* DescriptionWelcomePageViewController.swift */; }; + B0524B51214854E900D8FD8D /* DescriptionWelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B13214854E700D8FD8D /* DescriptionWelcomeContainerViewController.swift */; }; + B0524B52214854E900D8FD8D /* DescriptionWelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B13214854E700D8FD8D /* DescriptionWelcomeContainerViewController.swift */; }; + B0524B53214854E900D8FD8D /* DescriptionWelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B13214854E700D8FD8D /* DescriptionWelcomeContainerViewController.swift */; }; + B0524B54214854E900D8FD8D /* DescriptionWelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B13214854E700D8FD8D /* DescriptionWelcomeContainerViewController.swift */; }; + B0524B65214854E900D8FD8D /* DescriptionWelcomeImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B17214854E800D8FD8D /* DescriptionWelcomeImageViewController.swift */; }; + B0524B66214854E900D8FD8D /* DescriptionWelcomeImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B17214854E800D8FD8D /* DescriptionWelcomeImageViewController.swift */; }; + B0524B67214854E900D8FD8D /* DescriptionWelcomeImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B17214854E800D8FD8D /* DescriptionWelcomeImageViewController.swift */; }; + B0524B68214854E900D8FD8D /* DescriptionWelcomeImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B17214854E800D8FD8D /* DescriptionWelcomeImageViewController.swift */; }; + B0524B6F214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B19214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift */; }; + B0524B70214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B19214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift */; }; + B0524B71214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B19214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift */; }; + B0524B72214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B19214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift */; }; + B0524B75214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B74214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift */; }; + B0524B76214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B74214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift */; }; + B0524B77214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B74214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift */; }; + B0524B78214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0524B74214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift */; }; + B0606EB120AA6FF0006EC6B9 /* SnapshotRecorderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0606EB020AA6FF0006EC6B9 /* SnapshotRecorderTests.swift */; }; + B0606EC520AA955C006EC6B9 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0606EC420AA955B006EC6B9 /* SnapshotHelper.swift */; }; + B066F0D51E513DAA00A199F8 /* UIViewController+WMFHideKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B066F0D41E513DAA00A199F8 /* UIViewController+WMFHideKeyboard.swift */; }; + B068EDE0206B183500C827D1 /* Progress+ProgressUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B068EDDF206B183500C827D1 /* Progress+ProgressUI.swift */; }; + B068EDE1206B183500C827D1 /* Progress+ProgressUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B068EDDF206B183500C827D1 /* Progress+ProgressUI.swift */; }; + B068EDE2206B183500C827D1 /* Progress+ProgressUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B068EDDF206B183500C827D1 /* Progress+ProgressUI.swift */; }; + B068EDE3206B183500C827D1 /* Progress+ProgressUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B068EDDF206B183500C827D1 /* Progress+ProgressUI.swift */; }; + B069FA2E1CEACB8400083D59 /* WeakScriptMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B069FA2D1CEACB8400083D59 /* WeakScriptMessageDelegate.swift */; }; + B083371E1DADB251002860D2 /* WMFWelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083371D1DADB251002860D2 /* WMFWelcomePageViewController.swift */; }; + B083371F1DADBD7F002860D2 /* WMFWelcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E26B0741C0F4F720004D687 /* WMFWelcome.storyboard */; }; + B083375D1DB16A09002860D2 /* WMFWelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083375C1DB16A09002860D2 /* WMFWelcomePanelViewController.swift */; }; + B083375F1DB17DA5002860D2 /* WMFWelcomeLanguageTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083375E1DB17DA5002860D2 /* WMFWelcomeLanguageTableViewController.swift */; }; + B08337611DB17DC5002860D2 /* WMFWelcomeAnalyticsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08337601DB17DC5002860D2 /* WMFWelcomeAnalyticsViewController.swift */; }; + B0845E1120618DA400CDD98E /* SavedProgressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0845E0F20618DA400CDD98E /* SavedProgressViewController.swift */; }; + B0845E1220618DA400CDD98E /* SavedProgressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0845E0F20618DA400CDD98E /* SavedProgressViewController.swift */; }; + B0845E1320618DA400CDD98E /* SavedProgressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0845E0F20618DA400CDD98E /* SavedProgressViewController.swift */; }; + B0845E1420618DA400CDD98E /* SavedProgressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0845E0F20618DA400CDD98E /* SavedProgressViewController.swift */; }; + B0845E1C2061B44A00CDD98E /* SavedProgressViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0845E1B2061B44A00CDD98E /* SavedProgressViewController.storyboard */; }; + B0845E1D2061B44A00CDD98E /* SavedProgressViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0845E1B2061B44A00CDD98E /* SavedProgressViewController.storyboard */; }; + B0845E1E2061B44A00CDD98E /* SavedProgressViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0845E1B2061B44A00CDD98E /* SavedProgressViewController.storyboard */; }; + B0845E1F2061B44A00CDD98E /* SavedProgressViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0845E1B2061B44A00CDD98E /* SavedProgressViewController.storyboard */; }; + B085536C2399E368002100F8 /* UIAccessibility+Grouping.swift in Sources */ = {isa = PBXBuildFile; fileRef = B085536B2399E368002100F8 /* UIAccessibility+Grouping.swift */; }; + B08624311F72EA1900B770FD /* WMFWelcomeAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08624301F72EA1900B770FD /* WMFWelcomeAnimationBackgroundView.swift */; }; + B08624321F72EA1900B770FD /* WMFWelcomeAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08624301F72EA1900B770FD /* WMFWelcomeAnimationBackgroundView.swift */; }; + B08624331F72EA1A00B770FD /* WMFWelcomeAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08624301F72EA1900B770FD /* WMFWelcomeAnimationBackgroundView.swift */; }; + B08624341F72EA1A00B770FD /* WMFWelcomeAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08624301F72EA1900B770FD /* WMFWelcomeAnimationBackgroundView.swift */; }; + B0866F461CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = B0866F441CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.m */; }; + B0866F471CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0866F451CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.xib */; }; + B08E7E9B1C1FA57A00EC3C99 /* UIViewController+WMFEmptyView.m in Sources */ = {isa = PBXBuildFile; fileRef = B08E7E9A1C1FA57A00EC3C99 /* UIViewController+WMFEmptyView.m */; }; + B09705B4236B29D7006FDB5C /* DiffThanker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09705B3236B29D7006FDB5C /* DiffThanker.swift */; }; + B09705B5236B29D7006FDB5C /* DiffThanker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09705B3236B29D7006FDB5C /* DiffThanker.swift */; }; + B09705B6236B29D7006FDB5C /* DiffThanker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09705B3236B29D7006FDB5C /* DiffThanker.swift */; }; + B09705B7236B29D7006FDB5C /* DiffThanker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09705B3236B29D7006FDB5C /* DiffThanker.swift */; }; + B09B03EB1CE0FB2600009083 /* WMFPageHistoryRevision.m in Sources */ = {isa = PBXBuildFile; fileRef = B09B03EA1CE0FB2600009083 /* WMFPageHistoryRevision.m */; }; + B09B03ED1CE0FB4200009083 /* PageHistorySection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03EC1CE0FB4200009083 /* PageHistorySection.swift */; }; + B09B03F21CE0FB6300009083 /* PageHistoryFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03F11CE0FB6300009083 /* PageHistoryFetcher.swift */; }; + B09B03F51CE0FB7700009083 /* ReadingThemesControlsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03F31CE0FB7700009083 /* ReadingThemesControlsViewController.swift */; }; + B09B03F61CE0FB7700009083 /* ReadingThemesControlsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B09B03F41CE0FB7700009083 /* ReadingThemesControlsViewController.xib */; }; + B09B30CF1DB813760012281F /* UIViewController+WMFStoryboardUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B30CE1DB813760012281F /* UIViewController+WMFStoryboardUtilities.swift */; }; + B09B30D11DB817660012281F /* UIViewController+WMFWelcomeStoryboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B30D01DB817660012281F /* UIViewController+WMFWelcomeStoryboard.swift */; }; + B09BE6A11FB3DA46007F52E3 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B09BE6A01FB3DA45007F52E3 /* WebKit.framework */; }; + B09CE59A222F623900067D2A /* WKWebView+EditSelectionJavascript.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09CE599222F623800067D2A /* WKWebView+EditSelectionJavascript.swift */; }; + B09CE59B222F623900067D2A /* WKWebView+EditSelectionJavascript.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09CE599222F623800067D2A /* WKWebView+EditSelectionJavascript.swift */; }; + B09CE59C222F623900067D2A /* WKWebView+EditSelectionJavascript.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09CE599222F623800067D2A /* WKWebView+EditSelectionJavascript.swift */; }; + B09CE59D222F623900067D2A /* WKWebView+EditSelectionJavascript.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09CE599222F623800067D2A /* WKWebView+EditSelectionJavascript.swift */; }; + B0ACB13321265B930078C136 /* WMFImageGalleryDescriptionTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0ACB13221265B930078C136 /* WMFImageGalleryDescriptionTextView.swift */; }; + B0ACB13421265B9C0078C136 /* WMFImageGalleryDescriptionTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0ACB13221265B930078C136 /* WMFImageGalleryDescriptionTextView.swift */; }; + B0ACB13521265B9D0078C136 /* WMFImageGalleryDescriptionTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0ACB13221265B930078C136 /* WMFImageGalleryDescriptionTextView.swift */; }; + B0ACB13621265B9D0078C136 /* WMFImageGalleryDescriptionTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0ACB13221265B930078C136 /* WMFImageGalleryDescriptionTextView.swift */; }; + B0B0EC221C6999A9006F0D9C /* WMFSettingsMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B0EC211C6999A9006F0D9C /* WMFSettingsMenuItem.m */; }; + B0B423471EF1FEE000D3DC4C /* WMFFeedOnThisDayEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = B0B423451EF1FEE000D3DC4C /* WMFFeedOnThisDayEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B0B423481EF1FEE000D3DC4C /* WMFFeedOnThisDayEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B423461EF1FEE000D3DC4C /* WMFFeedOnThisDayEvent.m */; }; + B0B4234C1EF2055200D3DC4C /* WMFOnThisDayEventsFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = B0B4234A1EF2055200D3DC4C /* WMFOnThisDayEventsFetcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B0B4234D1EF2055200D3DC4C /* WMFOnThisDayEventsFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B4234B1EF2055200D3DC4C /* WMFOnThisDayEventsFetcher.m */; }; + B0B423501EF32D2700D3DC4C /* WMFOnThisDayContentSource.h in Headers */ = {isa = PBXBuildFile; fileRef = B0B4234E1EF32D2700D3DC4C /* WMFOnThisDayContentSource.h */; }; + B0B423511EF32D2700D3DC4C /* WMFOnThisDayContentSource.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B4234F1EF32D2700D3DC4C /* WMFOnThisDayContentSource.m */; }; + B0B423611EF9D69C00D3DC4C /* OnThisDayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B423601EF9D69C00D3DC4C /* OnThisDayViewController.swift */; }; + B0B423621EF9D6A300D3DC4C /* OnThisDayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B423601EF9D69C00D3DC4C /* OnThisDayViewController.swift */; }; + B0B423631EF9D6A300D3DC4C /* OnThisDayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B423601EF9D69C00D3DC4C /* OnThisDayViewController.swift */; }; + B0B423641EF9D6A400D3DC4C /* OnThisDayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B423601EF9D69C00D3DC4C /* OnThisDayViewController.swift */; }; + B0B423681EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B423661EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift */; }; + B0B423691EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B423661EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift */; }; + B0B4236A1EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B423661EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift */; }; + B0B4236B1EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B423661EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift */; }; + B0B4236D1EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0B423671EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib */; }; + B0B4236E1EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0B423671EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib */; }; + B0B4236F1EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0B423671EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib */; }; + B0B423701EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0B423671EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib */; }; + B0B4237A1F0211AB00D3DC4C /* WMFFeedArticlePreview+DescriptionOrSnippet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B423781F0211A000D3DC4C /* WMFFeedArticlePreview+DescriptionOrSnippet.swift */; }; + B0B4CF0A1CC0500E0051DF7A /* WMFArticleLanguagesSectionHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B4CF091CC0500E0051DF7A /* WMFArticleLanguagesSectionHeader.m */; }; + B0B4CF0C1CC0501B0051DF7A /* WMFArticleLanguagesSectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0B4CF0B1CC0501B0051DF7A /* WMFArticleLanguagesSectionHeader.xib */; }; + B0BCF0AB2023AC7700986F72 /* ScrollableEducationPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BCF0AA2023AC7700986F72 /* ScrollableEducationPanelViewController.swift */; }; + B0BCF0AC2023AC7700986F72 /* ScrollableEducationPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BCF0AA2023AC7700986F72 /* ScrollableEducationPanelViewController.swift */; }; + B0BCF0AD2023AC7700986F72 /* ScrollableEducationPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BCF0AA2023AC7700986F72 /* ScrollableEducationPanelViewController.swift */; }; + B0BCF0AE2023AC7700986F72 /* ScrollableEducationPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BCF0AA2023AC7700986F72 /* ScrollableEducationPanelViewController.swift */; }; + B0BCF0B9202537D800986F72 /* Panels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BCF0B8202537D800986F72 /* Panels.swift */; }; + B0BCF0BA202537D800986F72 /* Panels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BCF0B8202537D800986F72 /* Panels.swift */; }; + B0BCF0BB202537D800986F72 /* Panels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BCF0B8202537D800986F72 /* Panels.swift */; }; + B0BCF0BC202537D800986F72 /* Panels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BCF0B8202537D800986F72 /* Panels.swift */; }; + B0BDA58220B09A090098DB65 /* XCUIApplication+SnapshotUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BDA58120B09A090098DB65 /* XCUIApplication+SnapshotUtilities.swift */; }; + B0C06B9F218240CA00E481CC /* Collection+AsyncMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C06B9E218240CA00E481CC /* Collection+AsyncMapTests.swift */; }; + B0C6BE401E4068C60033BD6E /* WMFLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE3F1E4068C60033BD6E /* WMFLoginViewController.swift */; }; + B0C6BE421E413B3F0033BD6E /* WMFAccountCreationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE411E413B3F0033BD6E /* WMFAccountCreationViewController.swift */; }; + B0C6BE481E428C940033BD6E /* WMFAccountCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE471E428C940033BD6E /* WMFAccountCreator.swift */; }; + B0C6BE4A1E42D19D0033BD6E /* WMFCaptchaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE491E42D19D0033BD6E /* WMFCaptchaViewController.swift */; }; + B0C6BE531E4526810033BD6E /* WMFChangePasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0C6BE521E4526810033BD6E /* WMFChangePasswordViewController.storyboard */; }; + B0C6BE571E4526A40033BD6E /* WMFChangePasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE561E4526A40033BD6E /* WMFChangePasswordViewController.swift */; }; + B0C7A0791F710E75008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A0781F710E74008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift */; }; + B0C7A07A1F710E75008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A0781F710E74008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift */; }; + B0C7A07B1F710E75008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A0781F710E74008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift */; }; + B0C7A07D1F710E75008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A0781F710E74008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift */; }; + B0C7A07F1F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A07E1F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift */; }; + B0C7A0801F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A07E1F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift */; }; + B0C7A0811F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A07E1F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift */; }; + B0C7A0831F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A07E1F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift */; }; + B0C7A0851F710EB1008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A0841F710EB0008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift */; }; + B0C7A0861F710EB1008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A0841F710EB0008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift */; }; + B0C7A0871F710EB1008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A0841F710EB0008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift */; }; + B0C7A0891F710EB1008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C7A0841F710EB0008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift */; }; + B0CD9DD61F70997300051843 /* WMFWelcomeAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F84EEC1C8E444400801560 /* WMFWelcomeAnimationView.swift */; }; + B0CD9DD71F70997300051843 /* WMFWelcomeAnimationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F0874F1C860E910086F710 /* WMFWelcomeAnimationExtensions.swift */; }; + B0CD9DD91F70997300051843 /* WMFWelcomeIntroductionAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB571C8A89EA00996DE0 /* WMFWelcomeIntroductionAnimationView.swift */; }; + B0CD9DDA1F70997300051843 /* WMFWelcomeExplorationAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03103261F677BB300E2FCF6 /* WMFWelcomeExplorationAnimationView.swift */; }; + B0CD9DDB1F70997300051843 /* WMFWelcomeLanguagesAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB591C8A89EF00996DE0 /* WMFWelcomeLanguagesAnimationView.swift */; }; + B0CD9DDC1F70997300051843 /* WMFWelcomeAnalyticsAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB551C8A89E300996DE0 /* WMFWelcomeAnalyticsAnimationView.swift */; }; + B0CD9DDD1F70997400051843 /* WMFWelcomeAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F84EEC1C8E444400801560 /* WMFWelcomeAnimationView.swift */; }; + B0CD9DDE1F70997400051843 /* WMFWelcomeAnimationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F0874F1C860E910086F710 /* WMFWelcomeAnimationExtensions.swift */; }; + B0CD9DE01F70997400051843 /* WMFWelcomeIntroductionAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB571C8A89EA00996DE0 /* WMFWelcomeIntroductionAnimationView.swift */; }; + B0CD9DE11F70997400051843 /* WMFWelcomeExplorationAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03103261F677BB300E2FCF6 /* WMFWelcomeExplorationAnimationView.swift */; }; + B0CD9DE21F70997400051843 /* WMFWelcomeLanguagesAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB591C8A89EF00996DE0 /* WMFWelcomeLanguagesAnimationView.swift */; }; + B0CD9DE31F70997400051843 /* WMFWelcomeAnalyticsAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB551C8A89E300996DE0 /* WMFWelcomeAnalyticsAnimationView.swift */; }; + B0CD9DE41F70997400051843 /* WMFWelcomeAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F84EEC1C8E444400801560 /* WMFWelcomeAnimationView.swift */; }; + B0CD9DE51F70997400051843 /* WMFWelcomeAnimationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F0874F1C860E910086F710 /* WMFWelcomeAnimationExtensions.swift */; }; + B0CD9DE71F70997400051843 /* WMFWelcomeIntroductionAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB571C8A89EA00996DE0 /* WMFWelcomeIntroductionAnimationView.swift */; }; + B0CD9DE81F70997400051843 /* WMFWelcomeExplorationAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03103261F677BB300E2FCF6 /* WMFWelcomeExplorationAnimationView.swift */; }; + B0CD9DE91F70997400051843 /* WMFWelcomeLanguagesAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB591C8A89EF00996DE0 /* WMFWelcomeLanguagesAnimationView.swift */; }; + B0CD9DEA1F70997400051843 /* WMFWelcomeAnalyticsAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB551C8A89E300996DE0 /* WMFWelcomeAnalyticsAnimationView.swift */; }; + B0CD9DEB1F70997500051843 /* WMFWelcomeAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F84EEC1C8E444400801560 /* WMFWelcomeAnimationView.swift */; }; + B0CD9DEC1F70997500051843 /* WMFWelcomeAnimationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F0874F1C860E910086F710 /* WMFWelcomeAnimationExtensions.swift */; }; + B0CD9DEE1F70997500051843 /* WMFWelcomeIntroductionAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB571C8A89EA00996DE0 /* WMFWelcomeIntroductionAnimationView.swift */; }; + B0CD9DEF1F70997500051843 /* WMFWelcomeExplorationAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03103261F677BB300E2FCF6 /* WMFWelcomeExplorationAnimationView.swift */; }; + B0CD9DF01F70997500051843 /* WMFWelcomeLanguagesAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB591C8A89EF00996DE0 /* WMFWelcomeLanguagesAnimationView.swift */; }; + B0CD9DF11F70997500051843 /* WMFWelcomeAnalyticsAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F7CB551C8A89E300996DE0 /* WMFWelcomeAnalyticsAnimationView.swift */; }; + B0D3E70C214AF776007578BA /* DescriptionEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D3E70A214AF776007578BA /* DescriptionEditViewController.swift */; }; + B0D3E70D214AF776007578BA /* DescriptionEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D3E70A214AF776007578BA /* DescriptionEditViewController.swift */; }; + B0D3E70E214AF776007578BA /* DescriptionEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D3E70A214AF776007578BA /* DescriptionEditViewController.swift */; }; + B0D3E70F214AF776007578BA /* DescriptionEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D3E70A214AF776007578BA /* DescriptionEditViewController.swift */; }; + B0D4916F21F999A3002BBDD3 /* EditSaveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D4916E21F999A3002BBDD3 /* EditSaveViewController.swift */; }; + B0D4917021F999A3002BBDD3 /* EditSaveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D4916E21F999A3002BBDD3 /* EditSaveViewController.swift */; }; + B0D4917121F999A3002BBDD3 /* EditSaveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D4916E21F999A3002BBDD3 /* EditSaveViewController.swift */; }; + B0D4917221F999A3002BBDD3 /* EditSaveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D4916E21F999A3002BBDD3 /* EditSaveViewController.swift */; }; + B0D530EB1CE151C10078BAED /* CodeFileLocationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0D530EA1CE151C10078BAED /* CodeFileLocationTests.m */; }; + B0DE92271D6E26C700EC76A7 /* WMFBarButtonItemPopoverMessageViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0DE92261D6E26C700EC76A7 /* WMFBarButtonItemPopoverMessageViewController.storyboard */; }; + B0DE922B1D6E272B00EC76A7 /* WMFBarButtonItemPopoverMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0DE92291D6E272B00EC76A7 /* WMFBarButtonItemPopoverMessageViewController.m */; }; + B0DF6F811CFE1D0B0046E507 /* WKWebView+WMFWebViewControllerJavascript.m in Sources */ = {isa = PBXBuildFile; fileRef = B0DF6F801CFE1D0B0046E507 /* WKWebView+WMFWebViewControllerJavascript.m */; }; + B0E294CD1DB2CF4300861D04 /* UIView+Animations.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E294CC1DB2CF4300861D04 /* UIView+Animations.swift */; }; + B0E294D31DB2DC8900861D04 /* WMFWelcomeIntroductionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E294D21DB2DC8900861D04 /* WMFWelcomeIntroductionViewController.swift */; }; + B0E802B81C0CD2140065EBC0 /* UIBarButtonItem+WMFButtonConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802B71C0CD2140065EBC0 /* UIBarButtonItem+WMFButtonConvenience.m */; }; + B0E802BE1C0CD2360065EBC0 /* UIButton+WMFButton.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802BD1C0CD2360065EBC0 /* UIButton+WMFButton.m */; }; + B0E802C11C0CD27F0065EBC0 /* WMFAppViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802C01C0CD27F0065EBC0 /* WMFAppViewController.m */; }; + B0E8031C1C0CD6820065EBC0 /* WMFCompassView.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8031B1C0CD6820065EBC0 /* WMFCompassView.m */; }; + B0E803441C0CD7980065EBC0 /* WMFSearchFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803431C0CD7980065EBC0 /* WMFSearchFetcher.m */; }; + B0E803481C0CD7AA0065EBC0 /* WMFSearchResults.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803471C0CD7AA0065EBC0 /* WMFSearchResults.m */; }; + B0E8036D1C0CD98B0065EBC0 /* TableOfContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E8036C1C0CD98B0065EBC0 /* TableOfContentsViewController.swift */; }; + B0E8036F1C0CD99A0065EBC0 /* TableOfContentsPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E8036E1C0CD99A0065EBC0 /* TableOfContentsPresentationController.swift */; }; + B0E803711C0CD9A80065EBC0 /* TableOfContentsAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E803701C0CD9A80065EBC0 /* TableOfContentsAnimator.swift */; }; + B0E803761C0CD9C10065EBC0 /* TableOfContentsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E803721C0CD9C10065EBC0 /* TableOfContentsCell.swift */; }; + B0E803771C0CD9C10065EBC0 /* TableOfContentsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0E803731C0CD9C10065EBC0 /* TableOfContentsCell.xib */; }; + B0E803911C0CDABE0065EBC0 /* UIView+WMFSnapshotting.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803901C0CDABE0065EBC0 /* UIView+WMFSnapshotting.m */; }; + B0E803CC1C0CDC9B0065EBC0 /* WMFTitleInsetRespectingButton.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803CB1C0CDC9B0065EBC0 /* WMFTitleInsetRespectingButton.m */; }; + B0E803E61C0CDD450065EBC0 /* UIViewController+WMFStoryboardUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803E51C0CDD450065EBC0 /* UIViewController+WMFStoryboardUtilities.m */; }; + B0E8040B1C0CDE480065EBC0 /* WMFAccountCreationViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E803FC1C0CDE480065EBC0 /* WMFAccountCreationViewController.storyboard */; }; + B0E8040C1C0CDE480065EBC0 /* WMFCaptchaViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E803FD1C0CDE480065EBC0 /* WMFCaptchaViewController.storyboard */; }; + B0E8040F1C0CDE480065EBC0 /* WMFLoginViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804001C0CDE480065EBC0 /* WMFLoginViewController.storyboard */; }; + B0E804121C0CDE480065EBC0 /* EditSaveViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804031C0CDE480065EBC0 /* EditSaveViewController.storyboard */; }; + B0E804181C0CDE480065EBC0 /* WMFSettingsViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804091C0CDE480065EBC0 /* WMFSettingsViewController.storyboard */; }; + B0E804BF1C0CE0B40065EBC0 /* DDLog+WMFLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804741C0CE0B40065EBC0 /* DDLog+WMFLogger.m */; }; + B0E804C81C0CE0B40065EBC0 /* NSAttributedString+WMFModify.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804841C0CE0B40065EBC0 /* NSAttributedString+WMFModify.m */; }; + B0E804DA1C0CE0B40065EBC0 /* NSString+FormattedAttributedString.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804A71C0CE0B40065EBC0 /* NSString+FormattedAttributedString.m */; }; + B0E8054D1C0CE0DC0065EBC0 /* UIScrollView+ScrollSubviewToLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805091C0CE0DC0065EBC0 /* UIScrollView+ScrollSubviewToLocation.m */; }; + B0E8054E1C0CE0DC0065EBC0 /* UIScrollView+WMFContentOffsetUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8050B1C0CE0DC0065EBC0 /* UIScrollView+WMFContentOffsetUtils.m */; }; + B0E805551C0CE0DC0065EBC0 /* UIView+IBExtras.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E805181C0CE0DC0065EBC0 /* UIView+IBExtras.swift */; }; + B0E805591C0CE0DC0065EBC0 /* UIView+WMFFrameUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805201C0CE0DC0065EBC0 /* UIView+WMFFrameUtils.m */; }; + B0E805611C0CE0DC0065EBC0 /* WKWebView+ElementLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8052E1C0CE0DC0065EBC0 /* WKWebView+ElementLocation.m */; }; + B0E8058D1C0CE2C60065EBC0 /* CreateAccountFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805781C0CE2C60065EBC0 /* CreateAccountFunnel.m */; }; + B0E805911C0CE2C60065EBC0 /* WMFLoginFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805801C0CE2C60065EBC0 /* WMFLoginFunnel.m */; }; + B0E805921C0CE2C60065EBC0 /* ProtectedEditAttemptFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805821C0CE2C60065EBC0 /* ProtectedEditAttemptFunnel.m */; }; + B0E805941C0CE2C60065EBC0 /* SavedPagesFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805861C0CE2C60065EBC0 /* SavedPagesFunnel.m */; }; + B0E805951C0CE2C60065EBC0 /* ToCInteractionFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805881C0CE2C60065EBC0 /* ToCInteractionFunnel.m */; }; + B0E805961C0CE2C60065EBC0 /* WMFHamburgerMenuFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8058A1C0CE2C60065EBC0 /* WMFHamburgerMenuFunnel.m */; }; + B0E8059A1C0CE2E40065EBC0 /* WMFSearchFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805991C0CE2E40065EBC0 /* WMFSearchFunnel.m */; }; + B0E8059D1C0CE2F50065EBC0 /* WMFShareFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8059C1C0CE2F50065EBC0 /* WMFShareFunnel.m */; }; + B0E806331C0CE7680065EBC0 /* MTLValueTransformer+WMFNumericValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8062A1C0CE7670065EBC0 /* MTLValueTransformer+WMFNumericValueTransformer.m */; }; + B0E8065C1C0CE84B0065EBC0 /* WikiTextSectionUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806571C0CE84B0065EBC0 /* WikiTextSectionUploader.m */; }; + B0E8066B1C0CE9030065EBC0 /* MWKLanguageLinkFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806621C0CE9030065EBC0 /* MWKLanguageLinkFetcher.m */; }; + B0E806951C0CEA7B0065EBC0 /* AboutViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806931C0CEA7B0065EBC0 /* AboutViewController.m */; }; + B0E806961C0CEA7B0065EBC0 /* AboutViewController.plist in Resources */ = {isa = PBXBuildFile; fileRef = B0E806941C0CEA7B0065EBC0 /* AboutViewController.plist */; }; + B0E806C41C0CEB380065EBC0 /* WMFSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806C11C0CEB380065EBC0 /* WMFSettingsViewController.m */; }; + B0E8071F1C0CEC8A0065EBC0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8071E1C0CEC8A0065EBC0 /* main.m */; }; + B0E8073E1C0CED810065EBC0 /* WMFLogFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807351C0CED810065EBC0 /* WMFLogFormatter.m */; }; + B0E807DB1C0CF04A0065EBC0 /* MWKSearchRedirectMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807CA1C0CF04A0065EBC0 /* MWKSearchRedirectMapping.m */; }; + B0E8086D1C0D15170065EBC0 /* WMFCodingStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8086C1C0D15170065EBC0 /* WMFCodingStyle.m */; }; + B0E808741C0D154C0065EBC0 /* NSBundle+TestAssets.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E808731C0D154C0065EBC0 /* NSBundle+TestAssets.m */; }; + B0E808771C0D155A0065EBC0 /* XCTestCase+WMFBundleConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E808761C0D155A0065EBC0 /* XCTestCase+WMFBundleConvenience.m */; }; + B0E8087D1C0D15760065EBC0 /* WMFRandomFileUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8087C1C0D15760065EBC0 /* WMFRandomFileUtilities.m */; }; + B0E808831C0D15A20065EBC0 /* MWKDataStore+TemporaryDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E808811C0D15A20065EBC0 /* MWKDataStore+TemporaryDataStore.m */; }; + B0E8088F1C0D16140065EBC0 /* WMFAsyncTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8088E1C0D16140065EBC0 /* WMFAsyncTestCase.m */; }; + B0E808951C0D16330065EBC0 /* NSArray+WMFShuffle.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E808941C0D16330065EBC0 /* NSArray+WMFShuffle.m */; }; + B0E808981C0D16430065EBC0 /* XCTestCase+WMFLocaleTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E808971C0D16430065EBC0 /* XCTestCase+WMFLocaleTesting.m */; }; + B0E8089C1C0D165B0065EBC0 /* XCTestCase+SwiftDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E8089B1C0D165B0065EBC0 /* XCTestCase+SwiftDefaults.swift */; }; + B0E808A91C0D16A00065EBC0 /* UIView+VisualTestSizingUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E808A81C0D16A00065EBC0 /* UIView+VisualTestSizingUtils.m */; }; + B0E808B61C0D17070065EBC0 /* LSStubResponseDSL+WithJSON.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E808B51C0D17070065EBC0 /* LSStubResponseDSL+WithJSON.m */; }; + B0E808B91C0D17160065EBC0 /* WMFHTTPHangingProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E808B81C0D17160065EBC0 /* WMFHTTPHangingProtocol.m */; }; + B0E809051C0D18A00065EBC0 /* CircularBitwiseRotationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E809041C0D18A00065EBC0 /* CircularBitwiseRotationTests.m */; }; + B0E809091C0D18BC0065EBC0 /* NSString+WMFHTMLParsingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E809081C0D18BC0065EBC0 /* NSString+WMFHTMLParsingTests.m */; }; + B0E8090B1C0D18D90065EBC0 /* NSString+FormattedAttributedStringTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8090A1C0D18D90065EBC0 /* NSString+FormattedAttributedStringTests.m */; }; + B0E8090D1C0D18E70065EBC0 /* WMFImageURLParsingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8090C1C0D18E70065EBC0 /* WMFImageURLParsingTests.m */; }; + B0E8090F1C0D18F30065EBC0 /* WMFMathTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8090E1C0D18F30065EBC0 /* WMFMathTests.m */; }; + B0E809111C0D18FD0065EBC0 /* WMFSubstringUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E809101C0D18FD0065EBC0 /* WMFSubstringUtilsTests.m */; }; + B0E809131C0D19090065EBC0 /* WMFDateFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E809121C0D19090065EBC0 /* WMFDateFormatterTests.m */; }; + B0E8092F1C0D1A0B0065EBC0 /* NSURL+WMFExtrasTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8092E1C0D1A0B0065EBC0 /* NSURL+WMFExtrasTests.m */; }; + B0E809351C0D1A2F0065EBC0 /* WMFGeometryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E809341C0D1A2F0065EBC0 /* WMFGeometryTests.m */; }; + B0E809371C0D1A420065EBC0 /* MWKLanguageLinkControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E809361C0D1A420065EBC0 /* MWKLanguageLinkControllerTests.m */; }; + B0E8093B1C0D1A590065EBC0 /* WMFSafeAssignTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8093A1C0D1A590065EBC0 /* WMFSafeAssignTests.m */; }; + B0E809411C0D1A820065EBC0 /* NSURL+WMFLinkParsingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E809401C0D1A820065EBC0 /* NSURL+WMFLinkParsingTests.m */; }; + B0E809551C0D1B510065EBC0 /* CLLocation+WMFBearingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E809541C0D1B510065EBC0 /* CLLocation+WMFBearingTests.m */; }; + B0E8095E1C0D1B930065EBC0 /* WMFMTLModelSerializationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8095D1C0D1B930065EBC0 /* WMFMTLModelSerializationTests.m */; }; + B0E809601C0D1BA30065EBC0 /* WMFSearchFetcherTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8095F1C0D1BA30065EBC0 /* WMFSearchFetcherTests.m */; }; + B0ED17341E4912EB008B70AD /* WMFTwoFactorPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0ED17331E4912EB008B70AD /* WMFTwoFactorPasswordViewController.swift */; }; + B0ED173D1E49831B008B70AD /* WMFTwoFactorPasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0ED173C1E49831B008B70AD /* WMFTwoFactorPasswordViewController.storyboard */; }; + B0EF42D01C43FDD200D125A8 /* UIApplicationShortcutItem+WMFShortcutItem.m in Sources */ = {isa = PBXBuildFile; fileRef = B0EF42CF1C43FDD200D125A8 /* UIApplicationShortcutItem+WMFShortcutItem.m */; }; + B0EFCD681EBEC2F6008F36E5 /* LibrariesUsed.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0EFCD631EBEC231008F36E5 /* LibrariesUsed.storyboard */; }; + B0EFCD691EBEC2F7008F36E5 /* LibrariesUsed.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0EFCD631EBEC231008F36E5 /* LibrariesUsed.storyboard */; }; + B0EFCD6A1EBEC2F8008F36E5 /* LibrariesUsed.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0EFCD631EBEC231008F36E5 /* LibrariesUsed.storyboard */; }; + B0EFCD6B1EBEC2FA008F36E5 /* LibrariesUsed.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0EFCD631EBEC231008F36E5 /* LibrariesUsed.storyboard */; }; + B0EFCD6D1EBF12E5008F36E5 /* LibrariesUsed.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0EFCD6C1EBF12E5008F36E5 /* LibrariesUsed.swift */; }; + B0EFCD6E1EBF12E5008F36E5 /* LibrariesUsed.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0EFCD6C1EBF12E5008F36E5 /* LibrariesUsed.swift */; }; + B0EFCD6F1EBF12E5008F36E5 /* LibrariesUsed.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0EFCD6C1EBF12E5008F36E5 /* LibrariesUsed.swift */; }; + B0EFCD701EBF12E5008F36E5 /* LibrariesUsed.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0EFCD6C1EBF12E5008F36E5 /* LibrariesUsed.swift */; }; + B0EFCD721EBF13B2008F36E5 /* LibrariesUsed.plist in Resources */ = {isa = PBXBuildFile; fileRef = B0EFCD711EBF13B2008F36E5 /* LibrariesUsed.plist */; }; + B0EFCD731EBF13B2008F36E5 /* LibrariesUsed.plist in Resources */ = {isa = PBXBuildFile; fileRef = B0EFCD711EBF13B2008F36E5 /* LibrariesUsed.plist */; }; + B0EFCD741EBF13B2008F36E5 /* LibrariesUsed.plist in Resources */ = {isa = PBXBuildFile; fileRef = B0EFCD711EBF13B2008F36E5 /* LibrariesUsed.plist */; }; + B0EFCD751EBF13B2008F36E5 /* LibrariesUsed.plist in Resources */ = {isa = PBXBuildFile; fileRef = B0EFCD711EBF13B2008F36E5 /* LibrariesUsed.plist */; }; + B0F4761B21F921D300C4E254 /* EditSummaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F4761921F921D300C4E254 /* EditSummaryViewController.swift */; }; + B0F4761C21F921D300C4E254 /* EditSummaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F4761921F921D300C4E254 /* EditSummaryViewController.swift */; }; + B0F4761D21F921D300C4E254 /* EditSummaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F4761921F921D300C4E254 /* EditSummaryViewController.swift */; }; + B0F4761E21F921D300C4E254 /* EditSummaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F4761921F921D300C4E254 /* EditSummaryViewController.swift */; }; + B0F4762021F921D300C4E254 /* EditSummaryViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0F4761A21F921D300C4E254 /* EditSummaryViewController.xib */; }; + B0F4762121F921D300C4E254 /* EditSummaryViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0F4761A21F921D300C4E254 /* EditSummaryViewController.xib */; }; + B0F4762221F921D300C4E254 /* EditSummaryViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0F4761A21F921D300C4E254 /* EditSummaryViewController.xib */; }; + B0F4762321F921D300C4E254 /* EditSummaryViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0F4761A21F921D300C4E254 /* EditSummaryViewController.xib */; }; + B0F9299C1F84789D002A0788 /* WMFWelcomeInitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F9299B1F84789C002A0788 /* WMFWelcomeInitialViewController.swift */; }; + B0F9299E1F84789D002A0788 /* WMFWelcomeInitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F9299B1F84789C002A0788 /* WMFWelcomeInitialViewController.swift */; }; + B0F9299F1F84789D002A0788 /* WMFWelcomeInitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F9299B1F84789C002A0788 /* WMFWelcomeInitialViewController.swift */; }; + B0F929A01F84789D002A0788 /* WMFWelcomeInitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F9299B1F84789C002A0788 /* WMFWelcomeInitialViewController.swift */; }; + B0F92C6F1E3C580900B72802 /* WMFCaptchaResetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F92C6E1E3C580900B72802 /* WMFCaptchaResetter.swift */; }; + B0F92C821E3FFEB900B72802 /* WMFAuthAccountCreationInfoFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F92C811E3FFEB900B72802 /* WMFAuthAccountCreationInfoFetcher.swift */; }; + B0FFFB2A21C9BED1001E787E /* TextFormattingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0FFFB2921C9BED0001E787E /* TextFormattingButton.swift */; }; + B0FFFB2B21C9BED1001E787E /* TextFormattingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0FFFB2921C9BED0001E787E /* TextFormattingButton.swift */; }; + B0FFFB2C21C9BED1001E787E /* TextFormattingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0FFFB2921C9BED0001E787E /* TextFormattingButton.swift */; }; + B0FFFB2D21C9BED1001E787E /* TextFormattingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0FFFB2921C9BED0001E787E /* TextFormattingButton.swift */; }; + B32535F11EE856FF00372E93 /* EventLogging.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B32535EF1EE856FF00372E93 /* EventLogging.xcdatamodeld */; }; + B32536001EE87A6200372E93 /* EventRecord+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32535FE1EE87A6200372E93 /* EventRecord+CoreDataClass.swift */; }; + B32536011EE87A6200372E93 /* EventRecord+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32535FF1EE87A6200372E93 /* EventRecord+CoreDataProperties.swift */; }; + B3632E7F1EE5F98C007A2464 /* EventLoggingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3369A341EE1F69E0075953E /* EventLoggingService.swift */; }; + B37B38F01E5F432F00FE11BD /* WMFDatabaseHousekeeper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37B38EF1E5F432F00FE11BD /* WMFDatabaseHousekeeper.swift */; }; + B37B6FE91EEAFE11007CBB12 /* EventLoggingServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37B6FE81EEAFE11007CBB12 /* EventLoggingServiceTests.swift */; }; + B389CFCB1E6784B600483C06 /* WMFDatabaseHousekeeperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B389CFCA1E6784B600483C06 /* WMFDatabaseHousekeeperTests.swift */; }; + B389CFCE1E6F238300483C06 /* WMFMapsActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B389CFCD1E6F238300483C06 /* WMFMapsActivity.swift */; }; + B39427441E71F79700D3146D /* NSDictionaryBlocksKitTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B39427411E71F79700D3146D /* NSDictionaryBlocksKitTest.m */; }; + B39427451E71F79700D3146D /* NSSetBlocksKitTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B39427421E71F79700D3146D /* NSSetBlocksKitTest.m */; }; + B3F21D0F1EB0EBB4000ED0BB /* PlaceSearchService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3F21D0E1EB0EBB4000ED0BB /* PlaceSearchService.swift */; }; + B3F21D101EB0EBB4000ED0BB /* PlaceSearchService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3F21D0E1EB0EBB4000ED0BB /* PlaceSearchService.swift */; }; + B3F21D111EB0EBB4000ED0BB /* PlaceSearchService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3F21D0E1EB0EBB4000ED0BB /* PlaceSearchService.swift */; }; + B3F21D121EB0EBB4000ED0BB /* PlaceSearchService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3F21D0E1EB0EBB4000ED0BB /* PlaceSearchService.swift */; }; + BA4524181F324C3100439C42 /* FontSizeSliderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4524161F324C3100439C42 /* FontSizeSliderViewController.swift */; }; + BA4524191F324C3100439C42 /* FontSizeSliderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4524161F324C3100439C42 /* FontSizeSliderViewController.swift */; }; + BA45241A1F324C3100439C42 /* FontSizeSliderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4524161F324C3100439C42 /* FontSizeSliderViewController.swift */; }; + BA45241B1F324C3100439C42 /* FontSizeSliderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4524161F324C3100439C42 /* FontSizeSliderViewController.swift */; }; + BA45241D1F324C3100439C42 /* FontSizeSliderViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA4524171F324C3100439C42 /* FontSizeSliderViewController.xib */; }; + BA45241E1F324C3100439C42 /* FontSizeSliderViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA4524171F324C3100439C42 /* FontSizeSliderViewController.xib */; }; + BA45241F1F324C3100439C42 /* FontSizeSliderViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA4524171F324C3100439C42 /* FontSizeSliderViewController.xib */; }; + BA4524201F324C3100439C42 /* FontSizeSliderViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA4524171F324C3100439C42 /* FontSizeSliderViewController.xib */; }; + BA4524241F32500C00439C42 /* TextSizeChangeExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4524221F32500C00439C42 /* TextSizeChangeExampleViewController.swift */; }; + BA4524251F32500C00439C42 /* TextSizeChangeExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4524221F32500C00439C42 /* TextSizeChangeExampleViewController.swift */; }; + BA4524261F32500C00439C42 /* TextSizeChangeExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4524221F32500C00439C42 /* TextSizeChangeExampleViewController.swift */; }; + BA4524271F32500C00439C42 /* TextSizeChangeExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4524221F32500C00439C42 /* TextSizeChangeExampleViewController.swift */; }; + BA4524291F32500C00439C42 /* TextSizeChangeExampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA4524231F32500C00439C42 /* TextSizeChangeExampleViewController.xib */; }; + BA45242A1F32500C00439C42 /* TextSizeChangeExampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA4524231F32500C00439C42 /* TextSizeChangeExampleViewController.xib */; }; + BA45242B1F32500C00439C42 /* TextSizeChangeExampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA4524231F32500C00439C42 /* TextSizeChangeExampleViewController.xib */; }; + BA45242C1F32500C00439C42 /* TextSizeChangeExampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA4524231F32500C00439C42 /* TextSizeChangeExampleViewController.xib */; }; + BA6972571F2BA0D900E35F78 /* SettingsTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6972561F2BA0D900E35F78 /* SettingsTableViewSection.swift */; }; + BA6972591F2BA2D700E35F78 /* SettingsTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6972561F2BA0D900E35F78 /* SettingsTableViewSection.swift */; }; + BA69725A1F2BA2D800E35F78 /* SettingsTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6972561F2BA0D900E35F78 /* SettingsTableViewSection.swift */; }; + BA69725B1F2BA2D800E35F78 /* SettingsTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA6972561F2BA0D900E35F78 /* SettingsTableViewSection.swift */; }; + BA7683C21F30C56300A487AA /* ImageDimmingExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7683C01F30C56300A487AA /* ImageDimmingExampleViewController.swift */; }; + BA7683C31F30C56300A487AA /* ImageDimmingExampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BA7683C11F30C56300A487AA /* ImageDimmingExampleViewController.xib */; }; + BA7683C51F30D86A00A487AA /* ProminentSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7683C41F30D86A00A487AA /* ProminentSwitch.swift */; }; + BA7683C71F30D87D00A487AA /* ProminentSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7683C41F30D86A00A487AA /* ProminentSwitch.swift */; }; + BA7683C81F30D87E00A487AA /* ProminentSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7683C41F30D86A00A487AA /* ProminentSwitch.swift */; }; + BA7683C91F30D87F00A487AA /* ProminentSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7683C41F30D86A00A487AA /* ProminentSwitch.swift */; }; + BA7FF0B41F6188C70054CF02 /* CollectionViewCellActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7FF0B31F6188C70054CF02 /* CollectionViewCellActionsView.swift */; }; + BA7FF0B61F618F5A0054CF02 /* CollectionViewEditController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7FF0B51F618F5A0054CF02 /* CollectionViewEditController.swift */; }; + BA8203E21F15B4CC00925E93 /* ShareActivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA8203E11F15B4CC00925E93 /* ShareActivityController.swift */; }; + BAA0D91C1F4F165A00091284 /* PageIssuesTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAA0D91B1F4F165A00091284 /* PageIssuesTableViewController.swift */; }; + BAA0D91D1F4F165A00091284 /* PageIssuesTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAA0D91B1F4F165A00091284 /* PageIssuesTableViewController.swift */; }; + BAA0D91E1F4F165A00091284 /* PageIssuesTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAA0D91B1F4F165A00091284 /* PageIssuesTableViewController.swift */; }; + BAA0D91F1F4F165A00091284 /* PageIssuesTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAA0D91B1F4F165A00091284 /* PageIssuesTableViewController.swift */; }; + BAC6EEC81F1E519A00228AD0 /* AppearanceSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAFCE8411F1D7FD30077D5E9 /* AppearanceSettingsViewController.swift */; }; + BAC6EEC91F1E519B00228AD0 /* AppearanceSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAFCE8411F1D7FD30077D5E9 /* AppearanceSettingsViewController.swift */; }; + BAC6EECA1F1E519B00228AD0 /* AppearanceSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAFCE8411F1D7FD30077D5E9 /* AppearanceSettingsViewController.swift */; }; + BAFCE8431F1D7FD30077D5E9 /* AppearanceSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAFCE8411F1D7FD30077D5E9 /* AppearanceSettingsViewController.swift */; }; + BC23E4DD1C223DCB00B5AFDE /* WMFArticleRevisionFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4DC1C223DCB00B5AFDE /* WMFArticleRevisionFetcher.m */; }; + BC23E4E21C223FAE00B5AFDE /* WMFArticleRevision.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4E11C223FAE00B5AFDE /* WMFArticleRevision.m */; }; + BC23E4E51C22429100B5AFDE /* WMFRevisionQueryResults.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4E41C22429100B5AFDE /* WMFRevisionQueryResults.m */; }; + BC45FF481C1B1F9F00BAE501 /* NSUserDefaults+WMFReset.m in Sources */ = {isa = PBXBuildFile; fileRef = BC45FF471C1B1F9F00BAE501 /* NSUserDefaults+WMFReset.m */; }; + BC45FF4B1C1B22C200BAE501 /* NSObject+WMFReflection.m in Sources */ = {isa = PBXBuildFile; fileRef = BC45FF4A1C1B22C200BAE501 /* NSObject+WMFReflection.m */; }; + BC52D0F71C207D3300F625A9 /* TWNStringsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC52D0F61C207D3300F625A9 /* TWNStringsTests.m */; }; + BC62AE621C58FC810064C589 /* NSProcessInfo+WMFTestEnvironment.m in Sources */ = {isa = PBXBuildFile; fileRef = BC62AE611C58FC810064C589 /* NSProcessInfo+WMFTestEnvironment.m */; }; + BC62FFC01C11064200533DA9 /* MWKImageInfoFetcher+PicOfTheDayInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = BC62FFBF1C11064200533DA9 /* MWKImageInfoFetcher+PicOfTheDayInfo.m */; }; + BC90DE791C57C5AD007E0E81 /* WMFWelcomeLanguageViewControllerVisualTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC90DE781C57C5AD007E0E81 /* WMFWelcomeLanguageViewControllerVisualTests.m */; }; + BCA15AE51C0E213300D0A3EA /* LoggingDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA15AE41C0E213300D0A3EA /* LoggingDefaults.swift */; }; + BCCB813E1C110702008BC602 /* NSDate+WMFPOTDTitle.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCB813C1C110702008BC602 /* NSDate+WMFPOTDTitle.m */; }; + BCD3200A1C6EC6BC00317D08 /* NSTimeZone+WMFTestingUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD320091C6EC6BC00317D08 /* NSTimeZone+WMFTestingUtils.m */; }; + BCD557BB1C45B1600060A51A /* UIApplication+VisualTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD557BA1C45B1600060A51A /* UIApplication+VisualTestUtils.m */; }; + D4991439181D51DE00E6073C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4991438181D51DE00E6073C /* Foundation.framework */; }; + D499143B181D51DE00E6073C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D499143A181D51DE00E6073C /* CoreGraphics.framework */; }; + D499143D181D51DE00E6073C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D499143C181D51DE00E6073C /* UIKit.framework */; }; + D4E6D9121A5C65F9004916C1 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 040E5C4E184566F4007AFE6F /* CoreData.framework */; }; + D801C9301EB8E131001FA294 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D801C8531EB8E131001FA294 /* Localizable.strings */; }; + D801C9351EB8E131001FA294 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = D801C8611EB8E131001FA294 /* Localizable.stringsdict */; }; + D801C9361EB9344E001FA294 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D801C8511EB8E131001FA294 /* InfoPlist.strings */; }; + D801C93C1EB9404A001FA294 /* WMFLocalization.h in Headers */ = {isa = PBXBuildFile; fileRef = D801C93A1EB9404A001FA294 /* WMFLocalization.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D801C93D1EB9404A001FA294 /* WMFLocalization.m in Sources */ = {isa = PBXBuildFile; fileRef = D801C93B1EB9404A001FA294 /* WMFLocalization.m */; }; + D808DCEB1E438BE300A3E89C /* PlaceSearchSuggestionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEA1E438BE300A3E89C /* PlaceSearchSuggestionController.swift */; }; + D808DCED1E438C0C00A3E89C /* PlaceSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEC1E438C0C00A3E89C /* PlaceSearch.swift */; }; + D808DCEF1E438C5100A3E89C /* MKCoordinateRegion+Dimensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEE1E438C5100A3E89C /* MKCoordinateRegion+Dimensions.swift */; }; + D80A79291F31E63C00EC06AB /* NSCharacterSet+WMFLinkParsing.h in Headers */ = {isa = PBXBuildFile; fileRef = D80A79271F31E63C00EC06AB /* NSCharacterSet+WMFLinkParsing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D80A792A1F31E63C00EC06AB /* NSCharacterSet+WMFLinkParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D80A79281F31E63C00EC06AB /* NSCharacterSet+WMFLinkParsing.m */; }; + D80ACD281EA0DD0000DC3F20 /* FLAnimatedImage+SafeForSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D80ACD261EA0DD0000DC3F20 /* FLAnimatedImage+SafeForSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D80ACD291EA0DD0000DC3F20 /* FLAnimatedImage+SafeForSwift.m in Sources */ = {isa = PBXBuildFile; fileRef = D80ACD271EA0DD0000DC3F20 /* FLAnimatedImage+SafeForSwift.m */; }; + D80BF0A32347735E00B3B522 /* AppSearchButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80BF0A22347735E00B3B522 /* AppSearchButton.swift */; }; + D80BF0A42347735E00B3B522 /* AppSearchButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80BF0A22347735E00B3B522 /* AppSearchButton.swift */; }; + D80BF0A52347735E00B3B522 /* AppSearchButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80BF0A22347735E00B3B522 /* AppSearchButton.swift */; }; + D80BF0A62347735E00B3B522 /* AppSearchButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80BF0A22347735E00B3B522 /* AppSearchButton.swift */; }; + D80ED2591EE178A800CE8C50 /* Gradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80ED2581EE178A800CE8C50 /* Gradient.swift */; }; + D80ED25C1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80ED25A1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift */; }; + D80ED25D1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80ED25A1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift */; }; + D80ED25E1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80ED25A1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift */; }; + D80ED25F1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80ED25A1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift */; }; + D80ED2601EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D80ED25B1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib */; }; + D80ED2611EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D80ED25B1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib */; }; + D80ED2621EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D80ED25B1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib */; }; + D80ED2631EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D80ED25B1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib */; }; + D813FDA51EC34B2600FA4690 /* WMFArticle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D813FDA41EC34B2600FA4690 /* WMFArticle+Extensions.swift */; }; + D81445FF1E7093870078D71E /* UIViewController+WMFChildViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027447E1E665D9F00E7B248 /* UIViewController+WMFChildViewController.swift */; }; + D81446021E7094290078D71E /* WMFAuthButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027FD271E678F5C005644A9 /* WMFAuthButton.swift */; }; + D81446041E70C2430078D71E /* WMFMapsActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B389CFCD1E6F238300483C06 /* WMFMapsActivity.swift */; }; + D8181FA52188DC1400FDEC59 /* String+Domains.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8181FA42188DC1400FDEC59 /* String+Domains.swift */; }; + D818D3811ED7254D0076110D /* ColumnarCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3801ED7254D0076110D /* ColumnarCollectionViewController.swift */; }; + D818D3821ED7254D0076110D /* ColumnarCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3801ED7254D0076110D /* ColumnarCollectionViewController.swift */; }; + D818D3831ED7254D0076110D /* ColumnarCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3801ED7254D0076110D /* ColumnarCollectionViewController.swift */; }; + D818D3841ED7254D0076110D /* ColumnarCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3801ED7254D0076110D /* ColumnarCollectionViewController.swift */; }; + D818D3861ED750E40076110D /* ArticleCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3851ED750E40076110D /* ArticleCollectionViewController.swift */; }; + D818D3871ED750E40076110D /* ArticleCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3851ED750E40076110D /* ArticleCollectionViewController.swift */; }; + D818D3881ED750E40076110D /* ArticleCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3851ED750E40076110D /* ArticleCollectionViewController.swift */; }; + D818D3891ED750E40076110D /* ArticleCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3851ED750E40076110D /* ArticleCollectionViewController.swift */; }; + D818D38B1ED765470076110D /* ArticleLocationCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D38A1ED765470076110D /* ArticleLocationCollectionViewController.swift */; }; + D818D38C1ED765470076110D /* ArticleLocationCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D38A1ED765470076110D /* ArticleLocationCollectionViewController.swift */; }; + D818D38D1ED765470076110D /* ArticleLocationCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D38A1ED765470076110D /* ArticleLocationCollectionViewController.swift */; }; + D818D38E1ED765470076110D /* ArticleLocationCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D38A1ED765470076110D /* ArticleLocationCollectionViewController.swift */; }; + D818D3AB1ED87E8F0076110D /* ArticleLocationCellUpdating.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3AA1ED87E8F0076110D /* ArticleLocationCellUpdating.swift */; }; + D818D3AC1ED87E8F0076110D /* ArticleLocationCellUpdating.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3AA1ED87E8F0076110D /* ArticleLocationCellUpdating.swift */; }; + D818D3AD1ED87E8F0076110D /* ArticleLocationCellUpdating.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3AA1ED87E8F0076110D /* ArticleLocationCellUpdating.swift */; }; + D818D3AE1ED87E8F0076110D /* ArticleLocationCellUpdating.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818D3AA1ED87E8F0076110D /* ArticleLocationCellUpdating.swift */; }; + D818FEBB21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818FEBA21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift */; }; + D818FEBC21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818FEBA21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift */; }; + D818FEBD21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818FEBA21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift */; }; + D818FEBE21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = D818FEBA21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift */; }; + D81930DA1E9F97B200554B19 /* WMFExploreFeedContentController.h in Headers */ = {isa = PBXBuildFile; fileRef = D81930D81E9F97B200554B19 /* WMFExploreFeedContentController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D81930DB1E9F97B200554B19 /* WMFExploreFeedContentController.m in Sources */ = {isa = PBXBuildFile; fileRef = D81930D91E9F97B200554B19 /* WMFExploreFeedContentController.m */; }; + D81A28BE231E8F4C001CC77D /* ExtensionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81A28BD231E8F4C001CC77D /* ExtensionViewController.swift */; }; + D81E5F881E5F2C8400E1A80C /* UIApplication+SystemSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81E5F871E5F2C8400E1A80C /* UIApplication+SystemSettings.swift */; }; + D81E5F8A1E5F949B00E1A80C /* WMFAssertions.h in Headers */ = {isa = PBXBuildFile; fileRef = D81E5F891E5F949B00E1A80C /* WMFAssertions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D81EFDE21D775B140035F2EB /* NSUserActivity+WMFExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E5DC8621C6D716100C39A6F /* NSUserActivity+WMFExtensions.m */; }; + D81EFDE31D775B180035F2EB /* NSUserActivity+WMFExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E5DC8611C6D716100C39A6F /* NSUserActivity+WMFExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D81EFDE41D775B6B0035F2EB /* SavedPageSpotlightManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E568D471C78273B00E68FC1 /* SavedPageSpotlightManager.swift */; }; + D82117FC1EE58C080076C040 /* MapAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82117FB1EE58C080076C040 /* MapAnnotation.swift */; }; + D82117FD1EE58C080076C040 /* MapAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82117FB1EE58C080076C040 /* MapAnnotation.swift */; }; + D82117FE1EE58C080076C040 /* MapAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82117FB1EE58C080076C040 /* MapAnnotation.swift */; }; + D82117FF1EE58C080076C040 /* MapAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82117FB1EE58C080076C040 /* MapAnnotation.swift */; }; + D826C51521766E570012F940 /* Collection+AsyncMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = D826C51421766E570012F940 /* Collection+AsyncMap.swift */; }; + D826C51721766F1A0012F940 /* BackgroundFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D826C51621766F1A0012F940 /* BackgroundFetcher.swift */; }; + D826C51B217741C50012F940 /* ReachabilityNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D826C51A217741C50012F940 /* ReachabilityNotifier.swift */; }; + D82972831E3950100061550A /* ArticlePlace.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82972821E3950100061550A /* ArticlePlace.swift */; }; + D82972881E3A49980061550A /* ArticlePopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82972861E3A49980061550A /* ArticlePopoverViewController.swift */; }; + D82972891E3A49980061550A /* ArticlePopoverViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D82972871E3A49980061550A /* ArticlePopoverViewController.xib */; }; + D82972941E4361BF0061550A /* WMFKeyValue+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = D8987E011E325C7A00E75DA6 /* WMFKeyValue+CoreDataClass.m */; }; + D82972951E4361C60061550A /* WMFKeyValue+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = D8987E031E325C7A00E75DA6 /* WMFKeyValue+CoreDataProperties.m */; }; + D82C3A99213451100073EEAC /* DeviceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82C3A98213451100073EEAC /* DeviceInfo.swift */; }; + D82CA32F2020E87D005C2D5C /* ReadingListsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82CA32E2020E87D005C2D5C /* ReadingListsOperation.swift */; }; + D82CA3332020E8D8005C2D5C /* ReadingListsSyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82CA3322020E8D8005C2D5C /* ReadingListsSyncOperation.swift */; }; + D82E956A1F156F77007BD960 /* UIView+SubviewEnumeration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82E95691F156F77007BD960 /* UIView+SubviewEnumeration.swift */; }; + D82E956B1F156F77007BD960 /* UIView+SubviewEnumeration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82E95691F156F77007BD960 /* UIView+SubviewEnumeration.swift */; }; + D82E956C1F156F77007BD960 /* UIView+SubviewEnumeration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82E95691F156F77007BD960 /* UIView+SubviewEnumeration.swift */; }; + D82E956D1F156F77007BD960 /* UIView+SubviewEnumeration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82E95691F156F77007BD960 /* UIView+SubviewEnumeration.swift */; }; + D82E95851F16502E007BD960 /* WMFLanguagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D82E95831F16502E007BD960 /* WMFLanguagesViewController.m */; }; + D82E95861F16502E007BD960 /* WMFLanguagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D82E95831F16502E007BD960 /* WMFLanguagesViewController.m */; }; + D82E95871F16502E007BD960 /* WMFLanguagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D82E95831F16502E007BD960 /* WMFLanguagesViewController.m */; }; + D82E95881F16502E007BD960 /* WMFLanguagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D82E95831F16502E007BD960 /* WMFLanguagesViewController.m */; }; + D82E958A1F16502E007BD960 /* WMFLanguagesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D82E95841F16502E007BD960 /* WMFLanguagesViewController.xib */; }; + D82E958B1F16502E007BD960 /* WMFLanguagesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D82E95841F16502E007BD960 /* WMFLanguagesViewController.xib */; }; + D82E958C1F16502E007BD960 /* WMFLanguagesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D82E95841F16502E007BD960 /* WMFLanguagesViewController.xib */; }; + D82E958D1F16502E007BD960 /* WMFLanguagesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D82E95841F16502E007BD960 /* WMFLanguagesViewController.xib */; }; + D837B5A61F06AA8C00DCB9CD /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = D837B5A51F06AA8C00DCB9CD /* Theme.swift */; }; + D837B5A81F06E5C600DCB9CD /* DateFormatter+WikipediaLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D837B5A71F06E5C600DCB9CD /* DateFormatter+WikipediaLanguage.swift */; }; + D837B5AA1F0D0D1600DCB9CD /* WMFFeedOnThisDayEvent+LocalizedDates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D837B5A91F0D0D1600DCB9CD /* WMFFeedOnThisDayEvent+LocalizedDates.swift */; }; + D837B5B21F0D68B800DCB9CD /* URL+LinkParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D837B5B11F0D68B800DCB9CD /* URL+LinkParsing.swift */; }; + D837CC37231FE9CC00BA6130 /* ThemeableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D837CC36231FE9CC00BA6130 /* ThemeableViewController.swift */; }; + D837CC38231FE9CC00BA6130 /* ThemeableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D837CC36231FE9CC00BA6130 /* ThemeableViewController.swift */; }; + D837CC39231FE9CC00BA6130 /* ThemeableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D837CC36231FE9CC00BA6130 /* ThemeableViewController.swift */; }; + D837CC3A231FE9CC00BA6130 /* ThemeableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D837CC36231FE9CC00BA6130 /* ThemeableViewController.swift */; }; + D8396D1B22CF7052005625D8 /* WMFArticleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8396D1A22CF7052005625D8 /* WMFArticleTests.swift */; }; + D83FA6B51D74CDE0008CAB00 /* EventLoggingFunnel.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8057D1C0CE2C60065EBC0 /* EventLoggingFunnel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D83FA6B61D74CDE6008CAB00 /* EventLoggingFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8057E1C0CE2C60065EBC0 /* EventLoggingFunnel.m */; }; + D8421B53203CC8420040F50B /* DebugReadingListsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8421B51203CC8420040F50B /* DebugReadingListsViewController.swift */; }; + D8421B54203CC8420040F50B /* DebugReadingListsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8421B51203CC8420040F50B /* DebugReadingListsViewController.swift */; }; + D8421B55203CC8420040F50B /* DebugReadingListsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8421B51203CC8420040F50B /* DebugReadingListsViewController.swift */; }; + D8421B56203CC8420040F50B /* DebugReadingListsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8421B51203CC8420040F50B /* DebugReadingListsViewController.swift */; }; + D8421B58203CC8420040F50B /* DebugReadingListsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8421B52203CC8420040F50B /* DebugReadingListsViewController.xib */; }; + D8421B59203CC8420040F50B /* DebugReadingListsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8421B52203CC8420040F50B /* DebugReadingListsViewController.xib */; }; + D8421B5A203CC8420040F50B /* DebugReadingListsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8421B52203CC8420040F50B /* DebugReadingListsViewController.xib */; }; + D8421B5B203CC8420040F50B /* DebugReadingListsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8421B52203CC8420040F50B /* DebugReadingListsViewController.xib */; }; + D844480F1DDA33D900425630 /* Wikipedia.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D844480D1DDA33D900425630 /* Wikipedia.xcdatamodeld */; }; + D84448221DDB60FF00425630 /* WMFArticle+Extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D84448201DDB60FF00425630 /* WMFArticle+Extensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D84448231DDB60FF00425630 /* WMFArticle+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D84448211DDB60FF00425630 /* WMFArticle+Extensions.m */; }; + D84448281DDB632100425630 /* WMFArticle+CoreDataClass.h in Headers */ = {isa = PBXBuildFile; fileRef = D84448241DDB632100425630 /* WMFArticle+CoreDataClass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D84448291DDB632100425630 /* WMFArticle+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = D84448251DDB632100425630 /* WMFArticle+CoreDataClass.m */; }; + D844482A1DDB632100425630 /* WMFArticle+CoreDataProperties.h in Headers */ = {isa = PBXBuildFile; fileRef = D84448261DDB632100425630 /* WMFArticle+CoreDataProperties.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844482B1DDB632100425630 /* WMFArticle+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = D84448271DDB632100425630 /* WMFArticle+CoreDataProperties.m */; }; + D84448581DDCE49D00425630 /* WMFContentGroup+CoreDataClass.h in Headers */ = {isa = PBXBuildFile; fileRef = D84448541DDCE49D00425630 /* WMFContentGroup+CoreDataClass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D84448591DDCE49D00425630 /* WMFContentGroup+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = D84448551DDCE49D00425630 /* WMFContentGroup+CoreDataClass.m */; }; + D844485A1DDCE49D00425630 /* WMFContentGroup+CoreDataProperties.h in Headers */ = {isa = PBXBuildFile; fileRef = D84448561DDCE49D00425630 /* WMFContentGroup+CoreDataProperties.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844485B1DDCE49D00425630 /* WMFContentGroup+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = D84448571DDCE49D00425630 /* WMFContentGroup+CoreDataProperties.m */; }; + D844485E1DDCE4E500425630 /* WMFContentGroup+Extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D844485C1DDCE4E500425630 /* WMFContentGroup+Extensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844485F1DDCE4E500425630 /* WMFContentGroup+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D844485D1DDCE4E500425630 /* WMFContentGroup+Extensions.m */; }; + D844D9701D6CB2600042D692 /* WMF.h in Headers */ = {isa = PBXBuildFile; fileRef = D844D96E1D6CB2600042D692 /* WMF.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9731D6CB2600042D692 /* WMF.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; }; + D844D97D1D6CB29B0042D692 /* MWKDataObject.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807831C0CEF660065EBC0 /* MWKDataObject.m */; }; + D844D97E1D6CB2A10042D692 /* MWKDataObject.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807821C0CEF660065EBC0 /* MWKDataObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D97F1D6CB32D0042D692 /* MWKSiteDataObject.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807881C0CEF660065EBC0 /* MWKSiteDataObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9801D6CB3310042D692 /* MWKSiteDataObject.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807891C0CEF660065EBC0 /* MWKSiteDataObject.m */; }; + D844D9971D6CB5CA0042D692 /* WikipediaAppUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8072D1C0CED810065EBC0 /* WikipediaAppUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9981D6CB5CD0042D692 /* WikipediaAppUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8072E1C0CED810065EBC0 /* WikipediaAppUtils.m */; }; + D844D99C1D6CB6170042D692 /* NSString+WMFExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804A41C0CE0B40065EBC0 /* NSString+WMFExtras.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D99D1D6CB61B0042D692 /* NSString+WMFExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804A51C0CE0B40065EBC0 /* NSString+WMFExtras.m */; }; + D844D9A61D6CB7230042D692 /* MWKList.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807851C0CEF660065EBC0 /* MWKList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9A71D6CB7280042D692 /* MWKList.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807861C0CEF660065EBC0 /* MWKList.m */; }; + D844D9B21D6CB7770042D692 /* MWKSavedPageList.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807B41C0CF0180065EBC0 /* MWKSavedPageList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9B51D6CB77D0042D692 /* MWKSavedPageList.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807B51C0CF0180065EBC0 /* MWKSavedPageList.m */; }; + D844D9B61D6CB7940042D692 /* MWKRecentSearchEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807A51C0CEFE30065EBC0 /* MWKRecentSearchEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9B71D6CB7940042D692 /* MWKRecentSearchList.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807A71C0CEFE30065EBC0 /* MWKRecentSearchList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9B81D6CB7980042D692 /* MWKRecentSearchEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807A61C0CEFE30065EBC0 /* MWKRecentSearchEntry.m */; }; + D844D9B91D6CB7980042D692 /* MWKRecentSearchList.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807A81C0CEFE30065EBC0 /* MWKRecentSearchList.m */; }; + D844D9BE1D6CB7B30042D692 /* MWKDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807C11C0CF04A0065EBC0 /* MWKDataStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9C01D6CB7BA0042D692 /* MWKDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807C21C0CF04A0065EBC0 /* MWKDataStore.m */; }; + D844D9C21D6CB7D20042D692 /* MWKImageInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807C41C0CF04A0065EBC0 /* MWKImageInfo.m */; }; + D844D9C31D6CB7D40042D692 /* MWKImageInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807C31C0CF04A0065EBC0 /* MWKImageInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9DC1D6CBBFA0042D692 /* NSString+WMFHTMLParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804AD1C0CE0B40065EBC0 /* NSString+WMFHTMLParsing.m */; }; + D844D9DD1D6CBBFE0042D692 /* NSString+WMFHTMLParsing.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804AC1C0CE0B40065EBC0 /* NSString+WMFHTMLParsing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9DE1D6CBC0E0042D692 /* WMFImageURLParsing.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807321C0CED810065EBC0 /* WMFImageURLParsing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9EE1D6CBFFD0042D692 /* MWKDataStoreList.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807841C0CEF660065EBC0 /* MWKDataStoreList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9EF1D6CC0010042D692 /* MWKList+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807871C0CEF660065EBC0 /* MWKList+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9F01D6CC01C0042D692 /* MWKLicense.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8079B1C0CEFBD0065EBC0 /* MWKLicense.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844D9F31D6CC0220042D692 /* MWKLicense.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8079C1C0CEFBD0065EBC0 /* MWKLicense.m */; }; + D844D9F61D6CC0440042D692 /* Cancellable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E806781C0CE9C70065EBC0 /* Cancellable.swift */; }; + D844D9F71D6CC05F0042D692 /* ImageDownload.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E805C61C0CE5250065EBC0 /* ImageDownload.swift */; }; + D844DA011D6CC3C20042D692 /* MWKLanguageLink.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8079A1C0CEFBD0065EBC0 /* MWKLanguageLink.m */; }; + D844DA021D6CC3C40042D692 /* MWKLanguageLink.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807991C0CEFBD0065EBC0 /* MWKLanguageLink.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844DA031D6CC4C90042D692 /* MWKLanguageLinkController_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E806B31C0CEB160065EBC0 /* MWKLanguageLinkController_Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844DA041D6CC4C90042D692 /* MWKLanguageLinkController.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E806B41C0CEB160065EBC0 /* MWKLanguageLinkController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844DA061D6CC4C90042D692 /* MWKLanguageFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0EBCA7441C162EE9004F1FD9 /* MWKLanguageFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D844DA071D6CC4D40042D692 /* MWKLanguageLinkController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806B51C0CEB160065EBC0 /* MWKLanguageLinkController.m */; }; + D844DA091D6CC4D40042D692 /* MWKLanguageFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EBCA7451C162EE9004F1FD9 /* MWKLanguageFilter.m */; }; + D844DA0A1D6CC5240042D692 /* NSLocale+WMFExtras.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E804971C0CE0B40065EBC0 /* NSLocale+WMFExtras.swift */; }; + D84649AD1D4514F7009DB4A0 /* WMFTaskGroupTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D84649AC1D4514F7009DB4A0 /* WMFTaskGroupTests.m */; }; + D84692E01D5E1E3F000A7058 /* TableOfContentsHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84692DE1D5E1E3F000A7058 /* TableOfContentsHeader.swift */; }; + D84692E11D5E1E3F000A7058 /* TableOfContentsHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = D84692DF1D5E1E3F000A7058 /* TableOfContentsHeader.xib */; }; + D8479FAE1F222FE90025FD7A /* Stickers.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D8479FAD1F222FE90025FD7A /* Stickers.xcassets */; }; + D8479FB21F222FE90025FD7A /* Wikipedia Stickers.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D8479FAB1F222FE80025FD7A /* Wikipedia Stickers.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + D84B224D1DAFD0F7007C44AA /* WMFNotificationsController.h in Headers */ = {isa = PBXBuildFile; fileRef = D8F1BF241D9C2AFB00036E71 /* WMFNotificationsController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D84B224E1DAFD0FC007C44AA /* WMFNotificationsController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F1BF251D9C2AFB00036E71 /* WMFNotificationsController.m */; }; + D84B224F1DAFD14D007C44AA /* WMFFaceDetectionCache.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E805CF1C0CE5420065EBC0 /* WMFFaceDetectionCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D84B22501DAFD15A007C44AA /* WMFFaceDetectionCache.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805D01C0CE5420065EBC0 /* WMFFaceDetectionCache.m */; }; + D84B22511DAFD1E1007C44AA /* CIContext+WMFImageProcessing.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8046B1C0CE0B40065EBC0 /* CIContext+WMFImageProcessing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D84B22521DAFD1E1007C44AA /* CIContext+WMFImageProcessing.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8046C1C0CE0B40065EBC0 /* CIContext+WMFImageProcessing.m */; }; + D84B22531DAFD1E1007C44AA /* CIDetector+WMFFaceDetection.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8046D1C0CE0B40065EBC0 /* CIDetector+WMFFaceDetection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D84B22541DAFD1E1007C44AA /* CIDetector+WMFFaceDetection.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8046E1C0CE0B40065EBC0 /* CIDetector+WMFFaceDetection.m */; }; + D84BF62F1DBE616D00E0C85E /* UIViewController+WMFAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84BF62E1DBE616D00E0C85E /* UIViewController+WMFAlerts.swift */; }; + D84C35F11F323CCA00895FA1 /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8733C951ECA48490011E379 /* CollectionViewCell.swift */; }; + D84C35F21F323CD100895FA1 /* ArticleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87721F21EC0CDB60005E634 /* ArticleCollectionViewCell.swift */; }; + D84C35F31F323CD100895FA1 /* ArticleFullWidthImageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8831D381EC33F1D008CA89A /* ArticleFullWidthImageCollectionViewCell.swift */; }; + D84C35F41F323CD100895FA1 /* ArticleRightAlignedImageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D813FD9F1EC3419400FA4690 /* ArticleRightAlignedImageCollectionViewCell.swift */; }; + D84C35F51F323CE800895FA1 /* SaveButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87721FC1EC0DCA30005E634 /* SaveButton.swift */; }; + D84C35F61F323CF000895FA1 /* AlignedImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D876C2851E5CDE6500FCA00A /* AlignedImageButton.swift */; }; + D84C36161F32401B00895FA1 /* RankedArticleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8497F6D1EE09FA100100CBD /* RankedArticleCollectionViewCell.swift */; }; + D84C36171F32402000895FA1 /* AnnouncementCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83C5ABA1F2281A90066C892 /* AnnouncementCollectionViewCell.swift */; }; + D84C361A1F32403D00895FA1 /* NewsCollectionViewCell+WMFFeedContentDisplaying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04034491F0722B3001B837B /* NewsCollectionViewCell+WMFFeedContentDisplaying.swift */; }; + D84C361B1F32403D00895FA1 /* OnThisDayCollectionViewCell+WMFFeedContentDisplaying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B040343D1F0592F7001B837B /* OnThisDayCollectionViewCell+WMFFeedContentDisplaying.swift */; }; + D84C361C1F32403D00895FA1 /* OnThisDayExploreCollectionViewCell+WMFFeedContentDisplaying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B040343E1F0592F7001B837B /* OnThisDayExploreCollectionViewCell+WMFFeedContentDisplaying.swift */; }; + D84C361D1F32404700895FA1 /* SideScrollingCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B423521EF47DCD00D3DC4C /* SideScrollingCollectionViewCell.swift */; }; + D84C361E1F32404700895FA1 /* NewsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D89845281ECC8A1700849DA4 /* NewsCollectionViewCell.swift */; }; + D84C361F1F32404700895FA1 /* OnThisDayCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B423591EF4845500D3DC4C /* OnThisDayCollectionViewCell.swift */; }; + D84C36201F32404700895FA1 /* OnThisDayExploreCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04034371F059243001B837B /* OnThisDayExploreCollectionViewCell.swift */; }; + D84C36391F3241ED00895FA1 /* WMFGradientView.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8043E1C0CDF850065EBC0 /* WMFGradientView.m */; }; + D84C363A1F3241F100895FA1 /* WMFGradientView.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8043D1C0CDF850065EBC0 /* WMFGradientView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D84C363C1F32428A00895FA1 /* CircledRankView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8497F631EE09BE600100CBD /* CircledRankView.swift */; }; + D84C363D1F32438B00895FA1 /* SizeThatFitsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8497F681EE09D0200100CBD /* SizeThatFitsView.swift */; }; + D84C363E1F32441800895FA1 /* WMFDynamicTypeExtentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D1B4591DDD02BB004FCAE6 /* WMFDynamicTypeExtentions.swift */; }; + D84C36401F3245A200895FA1 /* ArticleCollectionViewCell+Themeable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84C363F1F3245A200895FA1 /* ArticleCollectionViewCell+Themeable.swift */; }; + D84C36411F3245CD00895FA1 /* ArticleCollectionViewCell+WMFFeedContentDisplaying.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8733C8C1ECA14DD0011E379 /* ArticleCollectionViewCell+WMFFeedContentDisplaying.swift */; }; + D84C36421F3245DF00895FA1 /* WMFFeedContentDisplaying.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E28C4871D751ED6000C5919 /* WMFFeedContentDisplaying.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D84C36431F3245DF00895FA1 /* WMFContentGroup+WMFFeedContentDisplaying.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E6A6F511D9E9AB300189C80 /* WMFContentGroup+WMFFeedContentDisplaying.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D84C36441F3245E600895FA1 /* WMFContentGroup+WMFFeedContentDisplaying.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E6A6F521D9E9AB300189C80 /* WMFContentGroup+WMFFeedContentDisplaying.m */; }; + D84C36521F33866C00895FA1 /* WMF Framework.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D84C36511F33866C00895FA1 /* WMF Framework.xcassets */; }; + D84DAA161EEEF527008E4B18 /* SWStepSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84DAA091EEEF527008E4B18 /* SWStepSlider.swift */; }; + D84DAA171EEEF527008E4B18 /* SWStepSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84DAA091EEEF527008E4B18 /* SWStepSlider.swift */; }; + D84DAA181EEEF527008E4B18 /* SWStepSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84DAA091EEEF527008E4B18 /* SWStepSlider.swift */; }; + D84DAA191EEEF527008E4B18 /* SWStepSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84DAA091EEEF527008E4B18 /* SWStepSlider.swift */; }; + D84F2BFB1D2FEE6300963D42 /* WMFRandomDiceButton.html in Resources */ = {isa = PBXBuildFile; fileRef = D84F2BF81D2FEE6300963D42 /* WMFRandomDiceButton.html */; }; + D84F2BFC1D2FEE6300963D42 /* WMFRandomDiceButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D84F2BF91D2FEE6300963D42 /* WMFRandomDiceButton.m */; }; + D84F2BFD1D2FEE6300963D42 /* WMFRandomDiceButtonRoll.js in Resources */ = {isa = PBXBuildFile; fileRef = D84F2BFA1D2FEE6300963D42 /* WMFRandomDiceButtonRoll.js */; }; + D84F2C031D30162700963D42 /* WMFFirstRandomViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D84F2C021D30162700963D42 /* WMFFirstRandomViewController.m */; }; + D850A53A1F8686DE006FD295 /* WMFThemeableNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = D850A5391F8686DE006FD295 /* WMFThemeableNavigationController.m */; }; + D850A53B1F8686DE006FD295 /* WMFThemeableNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = D850A5391F8686DE006FD295 /* WMFThemeableNavigationController.m */; }; + D850A53C1F8686DE006FD295 /* WMFThemeableNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = D850A5391F8686DE006FD295 /* WMFThemeableNavigationController.m */; }; + D850A53D1F8686DE006FD295 /* WMFThemeableNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = D850A5391F8686DE006FD295 /* WMFThemeableNavigationController.m */; }; + D8533ED51ECF581600E44F86 /* NewsCollectionViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8533ED31ECF581600E44F86 /* NewsCollectionViewHeader.swift */; }; + D8533ED61ECF581600E44F86 /* NewsCollectionViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8533ED31ECF581600E44F86 /* NewsCollectionViewHeader.swift */; }; + D8533ED71ECF581600E44F86 /* NewsCollectionViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8533ED31ECF581600E44F86 /* NewsCollectionViewHeader.swift */; }; + D8533ED81ECF581600E44F86 /* NewsCollectionViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8533ED31ECF581600E44F86 /* NewsCollectionViewHeader.swift */; }; + D8533ED91ECF581600E44F86 /* NewsCollectionViewHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8533ED41ECF581600E44F86 /* NewsCollectionViewHeader.xib */; }; + D8533EDA1ECF581600E44F86 /* NewsCollectionViewHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8533ED41ECF581600E44F86 /* NewsCollectionViewHeader.xib */; }; + D8533EDB1ECF581600E44F86 /* NewsCollectionViewHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8533ED41ECF581600E44F86 /* NewsCollectionViewHeader.xib */; }; + D8533EDC1ECF581600E44F86 /* NewsCollectionViewHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8533ED41ECF581600E44F86 /* NewsCollectionViewHeader.xib */; }; + D8543231218879D000E895B5 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8543230218879D000E895B5 /* Configuration.swift */; }; + D858A7DF1DA6A04A009C3DEB /* WMFDateCalculationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D858A7DE1DA6A04A009C3DEB /* WMFDateCalculationTests.m */; }; + D858C7B6210B91CD0039E0C9 /* PassthroughView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D858C7B5210B91CD0039E0C9 /* PassthroughView.swift */; }; + D858C7B7210B91CE0039E0C9 /* PassthroughView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D858C7B5210B91CD0039E0C9 /* PassthroughView.swift */; }; + D858C7B8210B91CE0039E0C9 /* PassthroughView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D858C7B5210B91CD0039E0C9 /* PassthroughView.swift */; }; + D858C7B9210B91CE0039E0C9 /* PassthroughView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D858C7B5210B91CD0039E0C9 /* PassthroughView.swift */; }; + D85BD2461F8F9D6900D0D478 /* NSManagedObjectContext+WMFKeyValue.h in Headers */ = {isa = PBXBuildFile; fileRef = D85BD2441F8F9D6900D0D478 /* NSManagedObjectContext+WMFKeyValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D85BD2471F8F9D6900D0D478 /* NSManagedObjectContext+WMFKeyValue.m in Sources */ = {isa = PBXBuildFile; fileRef = D85BD2451F8F9D6900D0D478 /* NSManagedObjectContext+WMFKeyValue.m */; }; + D85F56A2219C45C900AF3E13 /* URLComponents+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85F56A1219C45C900AF3E13 /* URLComponents+Extensions.swift */; }; + D8619BA41FBB10240045C8BC /* ReadingList+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8619B9E1FBB10240045C8BC /* ReadingList+CoreDataClass.swift */; }; + D8619BA51FBB10240045C8BC /* ReadingList+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8619B9F1FBB10240045C8BC /* ReadingList+CoreDataProperties.swift */; }; + D8619BA61FBB10240045C8BC /* ReadingListEntry+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8619BA01FBB10240045C8BC /* ReadingListEntry+CoreDataClass.swift */; }; + D8619BA71FBB10240045C8BC /* ReadingListEntry+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8619BA11FBB10240045C8BC /* ReadingListEntry+CoreDataProperties.swift */; }; + D8635AE8216E2BFC001A7C00 /* HTTPCookieStorage+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8635AE7216E2BFC001A7C00 /* HTTPCookieStorage+Migration.swift */; }; + D864D68C1DA3EA3800B86934 /* NumberFormatterExtrasTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D864D68B1DA3EA3800B86934 /* NumberFormatterExtrasTests.swift */; }; + D8650B7B20350FEE0044DFFA /* NSString+SHA256.h in Headers */ = {isa = PBXBuildFile; fileRef = D8650B7920350FEE0044DFFA /* NSString+SHA256.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8650B7C20350FEE0044DFFA /* NSString+SHA256.m in Sources */ = {isa = PBXBuildFile; fileRef = D8650B7A20350FEE0044DFFA /* NSString+SHA256.m */; }; + D87234011E1FF0A500751E83 /* PlacesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87233FF1E1FF0A500751E83 /* PlacesViewController.swift */; }; + D87234041E1FF18100751E83 /* Places.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D87234031E1FF18100751E83 /* Places.storyboard */; }; + D8726D431EBA052900A107D0 /* Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8726D421EBA052900A107D0 /* Localization.swift */; }; + D8733C8B1ECA10930011E379 /* LabelGroupAccessibilityElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8733C8A1ECA10930011E379 /* LabelGroupAccessibilityElement.swift */; }; + D8733C921ECA16580011E379 /* UIView+SemanticContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8733C911ECA16580011E379 /* UIView+SemanticContent.swift */; }; + D8733C941ECA16940011E379 /* HasText.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8733C931ECA16940011E379 /* HasText.swift */; }; + D87647481F1F9C2500D02CA4 /* CommonStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87647471F1F9C2500D02CA4 /* CommonStrings.swift */; }; + D876769F21E7B73C00491039 /* ToolbarSeparatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D876769E21E7B73C00491039 /* ToolbarSeparatorView.swift */; }; + D87676A021E7B73C00491039 /* ToolbarSeparatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D876769E21E7B73C00491039 /* ToolbarSeparatorView.swift */; }; + D87676A121E7B73C00491039 /* ToolbarSeparatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D876769E21E7B73C00491039 /* ToolbarSeparatorView.swift */; }; + D87676A221E7B73C00491039 /* ToolbarSeparatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D876769E21E7B73C00491039 /* ToolbarSeparatorView.swift */; }; + D87914DD1DFA04E10012C5DA /* NSUserDefaults+WMFApplicationDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87914DC1DFA04E10012C5DA /* NSUserDefaults+WMFApplicationDefaults.swift */; }; + D87B13A61F276B0F00B27227 /* ShareActivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA8203E11F15B4CC00925E93 /* ShareActivityController.swift */; }; + D87B13A71F276B1000B27227 /* ShareActivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA8203E11F15B4CC00925E93 /* ShareActivityController.swift */; }; + D87B13A81F276B1000B27227 /* ShareActivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA8203E11F15B4CC00925E93 /* ShareActivityController.swift */; }; + D87F1D3D1EC0ACC400575CF8 /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87F1D3C1EC0ACC400575CF8 /* AsyncOperation.swift */; }; + D8800CB11E2FF5B70035D2DB /* QuadKeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8800CB01E2FF5B70035D2DB /* QuadKeyTests.swift */; }; + D880652F218C732800BF7B91 /* WorkerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D880652E218C732800BF7B91 /* WorkerController.swift */; }; + D881B1101E326ABA00D33F62 /* WMFKeyValue+CoreDataProperties.h in Headers */ = {isa = PBXBuildFile; fileRef = D8987E021E325C7A00E75DA6 /* WMFKeyValue+CoreDataProperties.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D881B1111E326ABE00D33F62 /* WMFKeyValue+CoreDataClass.h in Headers */ = {isa = PBXBuildFile; fileRef = D8987E001E325C7900E75DA6 /* WMFKeyValue+CoreDataClass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D881B1131E32874500D33F62 /* WMFArticle+QuadKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = D881B1121E32874500D33F62 /* WMFArticle+QuadKey.swift */; }; + D88C70181EE595E90022A26A /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88C70171EE595E90022A26A /* MapView.swift */; }; + D88C70191EE595E90022A26A /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88C70171EE595E90022A26A /* MapView.swift */; }; + D88C701A1EE595E90022A26A /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88C70171EE595E90022A26A /* MapView.swift */; }; + D88C701B1EE595E90022A26A /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88C70171EE595E90022A26A /* MapView.swift */; }; + D88E0E1D1EBB5A97005B8E9E /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88E0E1C1EBB5A97005B8E9E /* Bundle.swift */; }; + D88FCADF1E4B74D300505A9F /* WikidataFetcher+Places.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88FCADE1E4B74D300505A9F /* WikidataFetcher+Places.swift */; }; + D88FCAE11E4B776600505A9F /* MapUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88FCAE01E4B776600505A9F /* MapUtilities.swift */; }; + D890C85D1D772ED3007132C9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D890C85B1D772ED3007132C9 /* InfoPlist.strings */; }; + D8940CEF1DB56C8A00E17F9E /* NewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8940CED1DB56C8A00E17F9E /* NewsViewController.swift */; }; + D896C7961D6F2E4E007101DF /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = D896C7951D6F2E4E007101DF /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m */; }; + D89845221ECB3F6C00849DA4 /* CGRect+Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D89845211ECB3F6C00849DA4 /* CGRect+Layout.swift */; }; + D8987E061E325D8A00E75DA6 /* QuadKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FB46A11E26BC6600F2620F /* QuadKey.swift */; }; + D89D44021D74D3ED00F7862E /* MWKSearchResult.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807CB1C0CF04A0065EBC0 /* MWKSearchResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D89D44031D74D40100F7862E /* MWKSearchResult.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807CC1C0CF04A0065EBC0 /* MWKSearchResult.m */; }; + D89DAE1B1D6CC6410089F7E1 /* MWKTitleLanguageController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EBCA7411C162ECF004F1FD9 /* MWKTitleLanguageController.m */; }; + D8A42A571E815A9C00D8E281 /* UserLocationAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6BAEC1E4C9BF400A981C8 /* UserLocationAnnotationView.swift */; }; + D8A42A581E815A9C00D8E281 /* WMFSearchFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805991C0CE2E40065EBC0 /* WMFSearchFunnel.m */; }; + D8A42A591E815A9C00D8E281 /* WMFImageURLActivitySource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC0447E1C797DC20033D773 /* WMFImageURLActivitySource.swift */; }; + D8A42A5E1E815A9C00D8E281 /* WMFCaptchaResetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F92C6E1E3C580900B72802 /* WMFCaptchaResetter.swift */; }; + D8A42A621E815A9C00D8E281 /* PlacesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87233FF1E1FF0A500751E83 /* PlacesViewController.swift */; }; + D8A42A641E815A9C00D8E281 /* WMFAuthButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027FD271E678F5C005644A9 /* WMFAuthButton.swift */; }; + D8A42A651E815A9C00D8E281 /* WMFReferencePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01490A41DB96EA7007F5391 /* WMFReferencePanelViewController.swift */; }; + D8A42A681E815A9C00D8E281 /* WikidataFetcher+Places.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88FCADE1E4B74D300505A9F /* WikidataFetcher+Places.swift */; }; + D8A42A691E815A9C00D8E281 /* UIButton+WMFButton.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802BD1C0CD2360065EBC0 /* UIButton+WMFButton.m */; }; + D8A42A6A1E815A9C00D8E281 /* TableOfContentsAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E803701C0CD9A80065EBC0 /* TableOfContentsAnimator.swift */; }; + D8A42A6D1E815A9C00D8E281 /* WMFReferencePageBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0338A831DBF0AF20012F588 /* WMFReferencePageBackgroundView.swift */; }; + D8A42A6F1E815A9C00D8E281 /* UIApplication+RTL.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00050131C52D73800515F70 /* UIApplication+RTL.swift */; }; + D8A42A721E815A9C00D8E281 /* WMFWelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083375C1DB16A09002860D2 /* WMFWelcomePanelViewController.swift */; }; + D8A42A751E815A9C00D8E281 /* UIViewController+WMFAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84BF62E1DBE616D00E0C85E /* UIViewController+WMFAlerts.swift */; }; + D8A42A761E815A9C00D8E281 /* NSDate+WMFPOTDTitle.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCB813C1C110702008BC602 /* NSDate+WMFPOTDTitle.m */; }; + D8A42A771E815A9C00D8E281 /* WMFTableHeaderFooterLabelView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EE2438C1DC9889B00066CBD /* WMFTableHeaderFooterLabelView.m */; }; + D8A42A7C1E815A9C00D8E281 /* WMFDatabaseHousekeeper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37B38EF1E5F432F00FE11BD /* WMFDatabaseHousekeeper.swift */; }; + D8A42A7E1E815A9C00D8E281 /* PlaceSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEC1E438C0C00A3E89C /* PlaceSearch.swift */; }; + D8A42A801E815A9C00D8E281 /* PlaceSearchSuggestionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEA1E438BE300A3E89C /* PlaceSearchSuggestionController.swift */; }; + D8A42A821E815A9C00D8E281 /* WMFPageHistoryRevision.m in Sources */ = {isa = PBXBuildFile; fileRef = B09B03EA1CE0FB2600009083 /* WMFPageHistoryRevision.m */; }; + D8A42A891E815A9C00D8E281 /* WMFEmptyView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF863501C19E4F100006D2D /* WMFEmptyView.m */; }; + D8A42A8C1E815A9C00D8E281 /* WMFSettingsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B02B82731C696ECA00B19309 /* WMFSettingsTableViewCell.m */; }; + D8A42A8D1E815A9C00D8E281 /* UIView+WMFSubviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEDA1DB4B76B00615FA2 /* UIView+WMFSubviews.swift */; }; + D8A42A8F1E815A9C00D8E281 /* WMFReferencePopoverMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0379A2A1D8B756C00D973CF /* WMFReferencePopoverMessageViewController.m */; }; + D8A42A901E815A9C00D8E281 /* WMFSearchFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803431C0CD7980065EBC0 /* WMFSearchFetcher.m */; }; + D8A42A921E815A9C00D8E281 /* LoggingDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA15AE41C0E213300D0A3EA /* LoggingDefaults.swift */; }; + D8A42A971E815A9C00D8E281 /* MWKTitleLanguageController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EBCA7411C162ECF004F1FD9 /* MWKTitleLanguageController.m */; }; + D8A42A981E815A9C00D8E281 /* UIView+WMFSnapshotting.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803901C0CDABE0065EBC0 /* UIView+WMFSnapshotting.m */; }; + D8A42A9A1E815A9C00D8E281 /* UIViewController+WMFStoryboardUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803E51C0CDD450065EBC0 /* UIViewController+WMFStoryboardUtilities.m */; }; + D8A42A9B1E815A9C00D8E281 /* UIVIewController+WMFCommonRotationSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED2E9F91CB2B3B300D1C844 /* UIVIewController+WMFCommonRotationSupport.swift */; }; + D8A42A9D1E815A9C00D8E281 /* WMFTitleInsetRespectingButton.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803CB1C0CDC9B0065EBC0 /* WMFTitleInsetRespectingButton.m */; }; + D8A42A9E1E815A9C00D8E281 /* WMFChangePasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE561E4526A40033BD6E /* WMFChangePasswordViewController.swift */; }; + D8A42AA11E815A9C00D8E281 /* WMFMapsActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B389CFCD1E6F238300483C06 /* WMFMapsActivity.swift */; }; + D8A42AA61E815A9C00D8E281 /* WMFArticleRevisionFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4DC1C223DCB00B5AFDE /* WMFArticleRevisionFetcher.m */; }; + D8A42AA71E815A9C00D8E281 /* WMFArticleLanguagesSectionHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B4CF091CC0500E0051DF7A /* WMFArticleLanguagesSectionHeader.m */; }; + D8A42AAD1E815A9C00D8E281 /* WMFTwoFactorPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0ED17331E4912EB008B70AD /* WMFTwoFactorPasswordViewController.swift */; }; + D8A42AAF1E815A9C00D8E281 /* SavedPagesFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805861C0CE2C60065EBC0 /* SavedPagesFunnel.m */; }; + D8A42AB11E815A9C00D8E281 /* WMFChange.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FEECCC1DE3729400B883F0 /* WMFChange.m */; }; + D8A42AB31E815A9C00D8E281 /* MWKSearchRedirectMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807CA1C0CF04A0065EBC0 /* MWKSearchRedirectMapping.m */; }; + D8A42AB41E815A9C00D8E281 /* MWKImageInfoFetcher+PicOfTheDayInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = BC62FFBF1C11064200533DA9 /* MWKImageInfoFetcher+PicOfTheDayInfo.m */; }; + D8A42AB61E815A9C00D8E281 /* RoundedCornerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CB32AC1E79D8A0008A0966 /* RoundedCornerView.swift */; }; + D8A42ABA1E815A9C00D8E281 /* WMFScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027447C1E6253E200E7B248 /* WMFScrollViewController.swift */; }; + D8A42AC01E815A9C00D8E281 /* PageHistorySection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03EC1CE0FB4200009083 /* PageHistorySection.swift */; }; + D8A42AC41E815A9C00D8E281 /* TableOfContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E8036C1C0CD98B0065EBC0 /* TableOfContentsViewController.swift */; }; + D8A42AC61E815A9C00D8E281 /* ProtectedEditAttemptFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805821C0CE2C60065EBC0 /* ProtectedEditAttemptFunnel.m */; }; + D8A42AC71E815A9C00D8E281 /* WKWebView+WMFWebViewControllerJavascript.m in Sources */ = {isa = PBXBuildFile; fileRef = B0DF6F801CFE1D0B0046E507 /* WKWebView+WMFWebViewControllerJavascript.m */; }; + D8A42ACB1E815A9C00D8E281 /* WMFArticleTextActivitySource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EC044781C7917860033D773 /* WMFArticleTextActivitySource.m */; }; + D8A42ACC1E815A9C00D8E281 /* WMFAuthLinkLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B010E1A71E723E3600CFE1CD /* WMFAuthLinkLabel.swift */; }; + D8A42AD11E815A9C00D8E281 /* WMFPasswordResetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0267CF21E32A0CB006B6D8D /* WMFPasswordResetter.swift */; }; + D8A42AD41E815A9C00D8E281 /* UIBarButtonItem+WMFButtonConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802B71C0CD2140065EBC0 /* UIBarButtonItem+WMFButtonConvenience.m */; }; + D8A42AD51E815A9C00D8E281 /* UIViewController+WMFEmptyView.m in Sources */ = {isa = PBXBuildFile; fileRef = B08E7E9A1C1FA57A00EC3C99 /* UIViewController+WMFEmptyView.m */; }; + D8A42ADD1E815A9C00D8E281 /* NSUserDefaults+WMFApplicationDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87914DC1DFA04E10012C5DA /* NSUserDefaults+WMFApplicationDefaults.swift */; }; + D8A42ADF1E815A9C00D8E281 /* AboutViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806931C0CEA7B0065EBC0 /* AboutViewController.m */; }; + D8A42AE31E815A9C00D8E281 /* WMFArticleLanguagesSectionFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = B0866F441CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.m */; }; + D8A42AE51E815A9C00D8E281 /* WMFWelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEDC1DB591C400615FA2 /* WMFWelcomeContainerViewController.swift */; }; + D8A42AE61E815A9C00D8E281 /* WMFAccountCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE471E428C940033BD6E /* WMFAccountCreator.swift */; }; + D8A42AE71E815A9C00D8E281 /* UIViewController+WMFHideKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B066F0D41E513DAA00A199F8 /* UIViewController+WMFHideKeyboard.swift */; }; + D8A42AE91E815A9C00D8E281 /* WeakScriptMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B069FA2D1CEACB8400083D59 /* WeakScriptMessageDelegate.swift */; }; + D8A42AEF1E815A9C00D8E281 /* UIView+Animations.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E294CC1DB2CF4300861D04 /* UIView+Animations.swift */; }; + D8A42AF11E815A9C00D8E281 /* MWKLanguageLinkFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806621C0CE9030065EBC0 /* MWKLanguageLinkFetcher.m */; }; + D8A42AF51E815A9C00D8E281 /* WMFImageTextActivitySource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC0447A1C796FEF0033D773 /* WMFImageTextActivitySource.swift */; }; + D8A42AF91E815A9C00D8E281 /* ToCInteractionFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805881C0CE2C60065EBC0 /* ToCInteractionFunnel.m */; }; + D8A42AFC1E815A9C00D8E281 /* WMFCompassView.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8031B1C0CD6820065EBC0 /* WMFCompassView.m */; }; + D8A42AFD1E815A9C00D8E281 /* WMFWelcomeIntroductionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E294D21DB2DC8900861D04 /* WMFWelcomeIntroductionViewController.swift */; }; + D8A42B031E815A9C00D8E281 /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = D896C7951D6F2E4E007101DF /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m */; }; + D8A42B051E815A9C00D8E281 /* UIViewController+WMFChildViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027447E1E665D9F00E7B248 /* UIViewController+WMFChildViewController.swift */; }; + D8A42B091E815A9C00D8E281 /* UIView+WMFFrameUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805201C0CE0DC0065EBC0 /* UIView+WMFFrameUtils.m */; }; + D8A42B0A1E815A9C00D8E281 /* WMFWelcomeAnimationViewControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEE21DB5B16E00615FA2 /* WMFWelcomeAnimationViewControllers.swift */; }; + D8A42B0B1E815A9C00D8E281 /* TableOfContentsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E803721C0CD9C10065EBC0 /* TableOfContentsCell.swift */; }; + D8A42B0D1E815A9C00D8E281 /* WMFSettingsMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B0EC211C6999A9006F0D9C /* WMFSettingsMenuItem.m */; }; + D8A42B0F1E815A9C00D8E281 /* UIScrollView+ScrollSubviewToLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805091C0CE0DC0065EBC0 /* UIScrollView+ScrollSubviewToLocation.m */; }; + D8A42B101E815A9C00D8E281 /* MTLValueTransformer+WMFNumericValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8062A1C0CE7670065EBC0 /* MTLValueTransformer+WMFNumericValueTransformer.m */; }; + D8A42B161E815A9C00D8E281 /* TableOfContentsHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84692DE1D5E1E3F000A7058 /* TableOfContentsHeader.swift */; }; + D8A42B191E815A9C00D8E281 /* UIViewController+WMFScrollToTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE489021D4ADCA00088505C /* UIViewController+WMFScrollToTop.swift */; }; + D8A42B1F1E815A9C00D8E281 /* UIApplicationShortcutItem+WMFShortcutItem.m in Sources */ = {isa = PBXBuildFile; fileRef = B0EF42CF1C43FDD200D125A8 /* UIApplicationShortcutItem+WMFShortcutItem.m */; }; + D8A42B231E815A9C00D8E281 /* WMFAuthAccountCreationInfoFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F92C811E3FFEB900B72802 /* WMFAuthAccountCreationInfoFetcher.swift */; }; + D8A42B251E815A9C00D8E281 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF5BB6C1C110C2100DE75E1 /* AppDelegate.m */; }; + D8A42B271E815A9C00D8E281 /* ArticlePlace.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82972821E3950100061550A /* ArticlePlace.swift */; }; + D8A42B281E815A9C00D8E281 /* String?+WMFExtras.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01CFC601E71069000B3546A /* String?+WMFExtras.swift */; }; + D8A42B2A1E815A9C00D8E281 /* WMFSearchResults.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803471C0CD7AA0065EBC0 /* WMFSearchResults.m */; }; + D8A42B2C1E815A9C00D8E281 /* WMFBarButtonItemPopoverMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0DE92291D6E272B00EC76A7 /* WMFBarButtonItemPopoverMessageViewController.m */; }; + D8A42B2E1E815A9C00D8E281 /* WMFReferencePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01490A21DB96E5F007F5391 /* WMFReferencePageViewController.swift */; }; + D8A42B371E815A9C00D8E281 /* WikiTextSectionUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806571C0CE84B0065EBC0 /* WikiTextSectionUploader.m */; }; + D8A42B3B1E815A9C00D8E281 /* UIViewController+WMFDynamicHeightPopoverMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B02376B31D6ECCF9007DBA73 /* UIViewController+WMFDynamicHeightPopoverMessage.m */; }; + D8A42B411E815A9C00D8E281 /* WMFWelcomeAnalyticsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08337601DB17DC5002860D2 /* WMFWelcomeAnalyticsViewController.swift */; }; + D8A42B421E815A9C00D8E281 /* WMFRevisionQueryResults.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4E41C22429100B5AFDE /* WMFRevisionQueryResults.m */; }; + D8A42B431E815A9C00D8E281 /* WMFAppViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802C01C0CD27F0065EBC0 /* WMFAppViewController.m */; }; + D8A42B471E815A9C00D8E281 /* UIView+IBExtras.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E805181C0CE0DC0065EBC0 /* UIView+IBExtras.swift */; }; + D8A42B491E815A9C00D8E281 /* WMFHamburgerMenuFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8058A1C0CE2C60065EBC0 /* WMFHamburgerMenuFunnel.m */; }; + D8A42B4A1E815A9C00D8E281 /* UIViewController+WMFWelcomeStoryboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B30D01DB817660012281F /* UIViewController+WMFWelcomeStoryboard.swift */; }; + D8A42B4B1E815A9C00D8E281 /* WMFLanguageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF224991CC5536200FDF78E /* WMFLanguageCell.m */; }; + D8A42B4E1E815A9C00D8E281 /* Array+WMFAllFieldsFilled.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04C444A1E56966B00C6DFB0 /* Array+WMFAllFieldsFilled.swift */; }; + D8A42B4F1E815A9C00D8E281 /* NewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8940CED1DB56C8A00E17F9E /* NewsViewController.swift */; }; + D8A42B501E815A9C00D8E281 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8071E1C0CEC8A0065EBC0 /* main.m */; }; + D8A42B531E815A9C00D8E281 /* WMFDailyStatsLoggingFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DC0941C74F0D700622CBD /* WMFDailyStatsLoggingFunnel.m */; }; + D8A42B541E815A9C00D8E281 /* WMFRandomDiceButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D84F2BF91D2FEE6300963D42 /* WMFRandomDiceButton.m */; }; + D8A42B5B1E815A9C00D8E281 /* WMFFirstRandomViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D84F2C021D30162700963D42 /* WMFFirstRandomViewController.m */; }; + D8A42B5D1E815A9C00D8E281 /* ArticlePopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82972861E3A49980061550A /* ArticlePopoverViewController.swift */; }; + D8A42B5E1E815A9C00D8E281 /* WMFLogFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807351C0CED810065EBC0 /* WMFLogFormatter.m */; }; + D8A42B611E815A9C00D8E281 /* NSString+FormattedAttributedString.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804A71C0CE0B40065EBC0 /* NSString+FormattedAttributedString.m */; }; + D8A42B651E815A9C00D8E281 /* WMFDeleteBackwardReportingTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01CFC5E1E70B00100B3546A /* WMFDeleteBackwardReportingTextField.swift */; }; + D8A42B671E815A9C00D8E281 /* WMFForgotPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0267CEC1E316AE3006B6D8D /* WMFForgotPasswordViewController.swift */; }; + D8A42B691E815A9C00D8E281 /* WMFImageGalleryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E4A34711CBBFCD400A400F6 /* WMFImageGalleryViewController.m */; }; + D8A42B6D1E815A9C00D8E281 /* WMFAccountCreationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE411E413B3F0033BD6E /* WMFAccountCreationViewController.swift */; }; + D8A42B701E815A9C00D8E281 /* NSAttributedString+WMFModify.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804841C0CE0B40065EBC0 /* NSAttributedString+WMFModify.m */; }; + D8A42B711E815A9C00D8E281 /* WMFSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806C11C0CEB380065EBC0 /* WMFSettingsViewController.m */; }; + D8A42B731E815A9C00D8E281 /* UIScrollView+WMFContentOffsetUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8050B1C0CE0DC0065EBC0 /* UIScrollView+WMFContentOffsetUtils.m */; }; + D8A42B751E815A9C00D8E281 /* WMFImageGalleryDetailOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E9B9E301CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.m */; }; + D8A42B781E815A9C00D8E281 /* UIApplication+SystemSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81E5F871E5F2C8400E1A80C /* UIApplication+SystemSettings.swift */; }; + D8A42B7B1E815A9C00D8E281 /* WMFArticleRevision.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4E11C223FAE00B5AFDE /* WMFArticleRevision.m */; }; + D8A42B7C1E815A9C00D8E281 /* ArticlePlaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6BAEE1E4C9C0700A981C8 /* ArticlePlaceView.swift */; }; + D8A42B7D1E815A9C00D8E281 /* WMFCaptchaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE491E42D19D0033BD6E /* WMFCaptchaViewController.swift */; }; + D8A42B7E1E815A9C00D8E281 /* DDLog+WMFLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804741C0CE0B40065EBC0 /* DDLog+WMFLogger.m */; }; + D8A42B801E815A9C00D8E281 /* WMFLegacyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E281A321DC263DE00FA1AB1 /* WMFLegacyReference.swift */; }; + D8A42B811E815A9C00D8E281 /* WMFWelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083371D1DADB251002860D2 /* WMFWelcomePageViewController.swift */; }; + D8A42B821E815A9C00D8E281 /* MKCoordinateRegion+Dimensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEE1E438C5100A3E89C /* MKCoordinateRegion+Dimensions.swift */; }; + D8A42B841E815A9C00D8E281 /* WMFAlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBCA7471C176389004F1FD9 /* WMFAlertManager.swift */; }; + D8A42B851E815A9C00D8E281 /* WMFWelcomeLanguageTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083375E1DB17DA5002860D2 /* WMFWelcomeLanguageTableViewController.swift */; }; + D8A42B871E815A9C00D8E281 /* WMFLoginFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805801C0CE2C60065EBC0 /* WMFLoginFunnel.m */; }; + D8A42B891E815A9C00D8E281 /* MapUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88FCAE01E4B776600505A9F /* MapUtilities.swift */; }; + D8A42B8B1E815A9C00D8E281 /* WKWebView+ElementLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8052E1C0CE0DC0065EBC0 /* WKWebView+ElementLocation.m */; }; + D8A42B8D1E815A9C00D8E281 /* UIViewController+WMFStoryboardUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B30CE1DB813760012281F /* UIViewController+WMFStoryboardUtilities.swift */; }; + D8A42B8F1E815A9C00D8E281 /* TableOfContentsPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E8036E1C0CD99A0065EBC0 /* TableOfContentsPresentationController.swift */; }; + D8A42B921E815A9C00D8E281 /* WMFLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE3F1E4068C60033BD6E /* WMFLoginViewController.swift */; }; + D8A42B961E815A9C00D8E281 /* PageHistoryFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03F11CE0FB6300009083 /* PageHistoryFetcher.swift */; }; + D8A42B981E815A9C00D8E281 /* CreateAccountFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805781C0CE2C60065EBC0 /* CreateAccountFunnel.m */; }; + D8A42B9A1E815A9C00D8E281 /* WMFShareFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8059C1C0CE2F50065EBC0 /* WMFShareFunnel.m */; }; + D8A42B9D1E815A9C00D8E281 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8D553611DF1B63200B90177 /* QuartzCore.framework */; settings = {ATTRIBUTES = (Required, ); }; }; + D8A42BA71E815A9C00D8E281 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 040E5C4E184566F4007AFE6F /* CoreData.framework */; }; + D8A42BA81E815A9C00D8E281 /* WMF.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; }; + D8A42BAA1E815A9C00D8E281 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D499143A181D51DE00E6073C /* CoreGraphics.framework */; }; + D8A42BAD1E815A9C00D8E281 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D499143C181D51DE00E6073C /* UIKit.framework */; }; + D8A42BAF1E815A9C00D8E281 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4991438181D51DE00E6073C /* Foundation.framework */; }; + D8A42BB01E815A9C00D8E281 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 041EFC361996A1F800B2CB28 /* MapKit.framework */; }; + D8A42BB61E815A9C00D8E281 /* WMFCaptchaViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E803FD1C0CDE480065EBC0 /* WMFCaptchaViewController.storyboard */; }; + D8A42BBB1E815A9C00D8E281 /* AboutViewController.plist in Resources */ = {isa = PBXBuildFile; fileRef = B0E806941C0CEA7B0065EBC0 /* AboutViewController.plist */; }; + D8A42BBC1E815A9C00D8E281 /* NotificationBackgroundMessage.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4D1DF22E1700B95311 /* NotificationBackgroundMessage.png */; }; + D8A42BC01E815A9C00D8E281 /* WMFRandomDiceButtonRoll.js in Resources */ = {isa = PBXBuildFile; fileRef = D84F2BFA1D2FEE6300963D42 /* WMFRandomDiceButtonRoll.js */; }; + D8A42BC21E815A9C00D8E281 /* NotificationBackgroundSuccess.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4F1DF22E1700B95311 /* NotificationBackgroundSuccess.png */; }; + D8A42BC31E815A9C00D8E281 /* NotificationBackgroundErrorIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4C1DF22E1700B95311 /* NotificationBackgroundErrorIcon@2x.png */; }; + D8A42BC51E815A9C00D8E281 /* WMFArticleLanguagesSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0866F451CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.xib */; }; + D8A42BC61E815A9C00D8E281 /* NotificationBackgroundErrorIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4B1DF22E1700B95311 /* NotificationBackgroundErrorIcon.png */; }; + D8A42BC71E815A9C00D8E281 /* WMFLanguageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E4D071C1CC5526200AE968B /* WMFLanguageCell.xib */; }; + D8A42BCC1E815A9C00D8E281 /* NotificationBackgroundSuccessIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B521DF22E1700B95311 /* NotificationBackgroundSuccessIcon@2x.png */; }; + D8A42BCD1E815A9C00D8E281 /* WMFTwoFactorPasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0ED173C1E49831B008B70AD /* WMFTwoFactorPasswordViewController.storyboard */; }; + D8A42BD11E815A9C00D8E281 /* TableOfContentsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0E803731C0CD9C10065EBC0 /* TableOfContentsCell.xib */; }; + D8A42BD21E815A9C00D8E281 /* WMFReferencePopoverMessageViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0379A361D8B9D2400D973CF /* WMFReferencePopoverMessageViewController.storyboard */; }; + D8A42BD71E815A9C00D8E281 /* WMFEmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0EF8634D1C19E02700006D2D /* WMFEmptyView.xib */; }; + D8A42BD81E815A9C00D8E281 /* NotificationBackgroundMessage@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4E1DF22E1700B95311 /* NotificationBackgroundMessage@2x.png */; }; + D8A42BD91E815A9C00D8E281 /* NotificationBackgroundWarningIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B551DF22E1700B95311 /* NotificationBackgroundWarningIcon.png */; }; + D8A42BDA1E815A9C00D8E281 /* WMFLoginViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804001C0CDE480065EBC0 /* WMFLoginViewController.storyboard */; }; + D8A42BDC1E815A9C00D8E281 /* WMFWelcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E26B0741C0F4F720004D687 /* WMFWelcome.storyboard */; }; + D8A42BE41E815A9C00D8E281 /* NotificationBackgroundWarning@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B541DF22E1700B95311 /* NotificationBackgroundWarning@2x.png */; }; + D8A42BE51E815A9C00D8E281 /* NotificationBackgroundSuccess@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B501DF22E1700B95311 /* NotificationBackgroundSuccess@2x.png */; }; + D8A42BE61E815A9C00D8E281 /* WMFChangePasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0C6BE521E4526810033BD6E /* WMFChangePasswordViewController.storyboard */; }; + D8A42BE81E815A9C00D8E281 /* NotificationBackgroundError.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B491DF22E1700B95311 /* NotificationBackgroundError.png */; }; + D8A42BE91E815A9C00D8E281 /* WMFReferencePanels.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B01490A01DB96BD6007F5391 /* WMFReferencePanels.storyboard */; }; + D8A42BEA1E815A9C00D8E281 /* Places.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D87234031E1FF18100751E83 /* Places.storyboard */; }; + D8A42BEC1E815A9C00D8E281 /* WMFAccountCreationViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E803FC1C0CDE480065EBC0 /* WMFAccountCreationViewController.storyboard */; }; + D8A42BED1E815A9C00D8E281 /* NotificationBackgroundSuccessIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B511DF22E1700B95311 /* NotificationBackgroundSuccessIcon.png */; }; + D8A42BEE1E815A9C00D8E281 /* WMFSettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B02B82741C696ECA00B19309 /* WMFSettingsTableViewCell.xib */; }; + D8A42BF11E815A9C00D8E281 /* NotificationBackgroundWarningIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B561DF22E1700B95311 /* NotificationBackgroundWarningIcon@2x.png */; }; + D8A42BF41E815A9C00D8E281 /* TableOfContentsHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = D84692DF1D5E1E3F000A7058 /* TableOfContentsHeader.xib */; }; + D8A42BF71E815A9C00D8E281 /* WMFBarButtonItemPopoverMessageViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0DE92261D6E26C700EC76A7 /* WMFBarButtonItemPopoverMessageViewController.storyboard */; }; + D8A42BF91E815A9C00D8E281 /* TSMessagesDefaultDesign.json in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B591DF22E1700B95311 /* TSMessagesDefaultDesign.json */; }; + D8A42BFA1E815A9C00D8E281 /* WMFArticleLanguagesSectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0B4CF0B1CC0501B0051DF7A /* WMFArticleLanguagesSectionHeader.xib */; }; + D8A42BFC1E815A9C00D8E281 /* ArticlePopoverViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D82972871E3A49980061550A /* ArticlePopoverViewController.xib */; }; + D8A42BFD1E815A9C00D8E281 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E69CD5A1C8773410095918B /* Launch Screen.storyboard */; }; + D8A42BFE1E815A9C00D8E281 /* EditSaveViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804031C0CDE480065EBC0 /* EditSaveViewController.storyboard */; }; + D8A42BFF1E815A9C00D8E281 /* NotificationBackgroundWarning.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B531DF22E1700B95311 /* NotificationBackgroundWarning.png */; }; + D8A42C011E815A9C00D8E281 /* NotificationBackgroundError@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4A1DF22E1700B95311 /* NotificationBackgroundError@2x.png */; }; + D8A42C041E815A9C00D8E281 /* WMFImageGalleryDetailOverlayView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E9B9E2F1CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.xib */; }; + D8A42C081E815A9C00D8E281 /* WMFRandomDiceButton.html in Resources */ = {isa = PBXBuildFile; fileRef = D84F2BF81D2FEE6300963D42 /* WMFRandomDiceButton.html */; }; + D8A42C0D1E815A9C00D8E281 /* WMFForgotPasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0267CE81E316A79006B6D8D /* WMFForgotPasswordViewController.storyboard */; }; + D8A42C101E815A9C00D8E281 /* WMFTableHeaderFooterLabelView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0EE2438E1DC988AA00066CBD /* WMFTableHeaderFooterLabelView.xib */; }; + D8A42C131E815A9C00D8E281 /* NotificationButtonBackground.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B571DF22E1700B95311 /* NotificationButtonBackground.png */; }; + D8A42C151E815A9C00D8E281 /* WMFSettingsViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804091C0CDE480065EBC0 /* WMFSettingsViewController.storyboard */; }; + D8A42C161E815A9C00D8E281 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D4991453181D51DE00E6073C /* Images.xcassets */; }; + D8A42C171E815A9C00D8E281 /* ReadingThemesControlsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B09B03F41CE0FB7700009083 /* ReadingThemesControlsViewController.xib */; }; + D8A42C181E815A9C00D8E281 /* NotificationButtonBackground@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B581DF22E1700B95311 /* NotificationButtonBackground@2x.png */; }; + D8A42C1E1E815A9C00D8E281 /* WMF.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8A47C8523D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8423D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift */; }; + D8A47C8623D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8423D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift */; }; + D8A47C8723D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8423D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift */; }; + D8A47C8823D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8423D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift */; }; + D8A47C8A23D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8923D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift */; }; + D8A47C8B23D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8923D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift */; }; + D8A47C8C23D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8923D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift */; }; + D8A47C8D23D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8923D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift */; }; + D8A47C8F23D7338C002AA823 /* ArticleViewController+TableOfContents.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8E23D7338C002AA823 /* ArticleViewController+TableOfContents.swift */; }; + D8A47C9023D7338C002AA823 /* ArticleViewController+TableOfContents.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8E23D7338C002AA823 /* ArticleViewController+TableOfContents.swift */; }; + D8A47C9123D7338C002AA823 /* ArticleViewController+TableOfContents.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8E23D7338C002AA823 /* ArticleViewController+TableOfContents.swift */; }; + D8A47C9223D7338C002AA823 /* ArticleViewController+TableOfContents.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A47C8E23D7338C002AA823 /* ArticleViewController+TableOfContents.swift */; }; + D8A6BAED1E4C9BF400A981C8 /* UserLocationAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6BAEC1E4C9BF400A981C8 /* UserLocationAnnotationView.swift */; }; + D8A6BAEF1E4C9C0700A981C8 /* ArticlePlaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6BAEE1E4C9C0700A981C8 /* ArticlePlaceView.swift */; }; + D8AAF6B81FE93DE9005760E6 /* UIScrollView+Limits.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8AAF6B71FE93DE9005760E6 /* UIScrollView+Limits.swift */; }; + D8AC391D1D6F2324007E3C14 /* UIScreen+WMFImageWidth.h in Headers */ = {isa = PBXBuildFile; fileRef = BCA15B151C0F48EF00D0A3EA /* UIScreen+WMFImageWidth.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8AC391E1D6F2328007E3C14 /* UIScreen+WMFImageWidth.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA15B161C0F48EF00D0A3EA /* UIScreen+WMFImageWidth.m */; }; + D8B166851FD97A0500097D8B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B166841FD97A0500097D8B /* ViewController.swift */; }; + D8B166861FD97A0500097D8B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B166841FD97A0500097D8B /* ViewController.swift */; }; + D8B166871FD97A0500097D8B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B166841FD97A0500097D8B /* ViewController.swift */; }; + D8B166881FD97A0500097D8B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B166841FD97A0500097D8B /* ViewController.swift */; }; + D8B1668C1FD97FE000097D8B /* WMFViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8B1668B1FD97FE000097D8B /* WMFViewController.m */; }; + D8B1668D1FD97FE000097D8B /* WMFViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8B1668B1FD97FE000097D8B /* WMFViewController.m */; }; + D8B1668E1FD97FE000097D8B /* WMFViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8B1668B1FD97FE000097D8B /* WMFViewController.m */; }; + D8B1668F1FD97FE000097D8B /* WMFViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8B1668B1FD97FE000097D8B /* WMFViewController.m */; }; + D8B3D7661EC34F5B00930C21 /* SaveButtonsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B3D7651EC34F5B00930C21 /* SaveButtonsController.swift */; }; + D8B3D7671EC34F5B00930C21 /* SaveButtonsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B3D7651EC34F5B00930C21 /* SaveButtonsController.swift */; }; + D8B3D7681EC34F5B00930C21 /* SaveButtonsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B3D7651EC34F5B00930C21 /* SaveButtonsController.swift */; }; + D8B3D7691EC34F5B00930C21 /* SaveButtonsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B3D7651EC34F5B00930C21 /* SaveButtonsController.swift */; }; + D8BD63BF1EA7E28700BBC082 /* SummaryExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8BD63BE1EA7E28700BBC082 /* SummaryExtensions.swift */; }; + D8BDA8BE1E71B8C90031F4BF /* WMFDeleteBackwardReportingTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01CFC5E1E70B00100B3546A /* WMFDeleteBackwardReportingTextField.swift */; }; + D8BDA8BF1E71B8D10031F4BF /* String?+WMFExtras.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01CFC601E71069000B3546A /* String?+WMFExtras.swift */; }; + D8BDA8C11E71C0760031F4BF /* WMFBlocksKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D8BDA8C01E71C0760031F4BF /* WMFBlocksKitTests.m */; }; + D8C41DDB23FC09EE00353DCE /* NSManagedObjectContext+History.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8C41DDA23FC09EE00353DCE /* NSManagedObjectContext+History.swift */; }; + D8C4D3D31FD5D9260089CEC2 /* TUSafariActivity.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D8C4D3D01FD5D9250089CEC2 /* TUSafariActivity.bundle */; }; + D8C4D3D41FD5D9260089CEC2 /* TUSafariActivity.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D8C4D3D01FD5D9250089CEC2 /* TUSafariActivity.bundle */; }; + D8C4D3D51FD5D9260089CEC2 /* TUSafariActivity.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D8C4D3D01FD5D9250089CEC2 /* TUSafariActivity.bundle */; }; + D8C4D3D61FD5D9260089CEC2 /* TUSafariActivity.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D8C4D3D01FD5D9250089CEC2 /* TUSafariActivity.bundle */; }; + D8C4D3D81FD5D9260089CEC2 /* TUSafariActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = D8C4D3D21FD5D9250089CEC2 /* TUSafariActivity.m */; }; + D8C4D3D91FD5D9260089CEC2 /* TUSafariActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = D8C4D3D21FD5D9250089CEC2 /* TUSafariActivity.m */; }; + D8C4D3DA1FD5D9260089CEC2 /* TUSafariActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = D8C4D3D21FD5D9250089CEC2 /* TUSafariActivity.m */; }; + D8C4D3DB1FD5D9260089CEC2 /* TUSafariActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = D8C4D3D21FD5D9250089CEC2 /* TUSafariActivity.m */; }; + D8CB32AD1E79D8A0008A0966 /* RoundedCornerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CB32AC1E79D8A0008A0966 /* RoundedCornerView.swift */; }; + D8CB32AE1E79D8A0008A0966 /* RoundedCornerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CB32AC1E79D8A0008A0966 /* RoundedCornerView.swift */; }; + D8CC94DA217897FB007293E7 /* NSManagedObject+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CC94D9217897FB007293E7 /* NSManagedObject+Extensions.swift */; }; + D8CD975E1E83F65700ECCA9D /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D895D0841D9C1EB8005418C1 /* UserNotifications.framework */; }; + D8CD97651E83FAB400ECCA9D /* Cache.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D8CD97631E83FAB400ECCA9D /* Cache.xcdatamodeld */; }; + D8CE24E11E698E2400DAE2E0 /* UserLocationAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6BAEC1E4C9BF400A981C8 /* UserLocationAnnotationView.swift */; }; + D8CE24E21E698E2400DAE2E0 /* WMFSearchFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805991C0CE2E40065EBC0 /* WMFSearchFunnel.m */; }; + D8CE24E31E698E2400DAE2E0 /* WMFImageURLActivitySource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC0447E1C797DC20033D773 /* WMFImageURLActivitySource.swift */; }; + D8CE24E81E698E2400DAE2E0 /* WMFCaptchaResetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F92C6E1E3C580900B72802 /* WMFCaptchaResetter.swift */; }; + D8CE24EC1E698E2400DAE2E0 /* PlacesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87233FF1E1FF0A500751E83 /* PlacesViewController.swift */; }; + D8CE24EE1E698E2400DAE2E0 /* WMFReferencePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01490A41DB96EA7007F5391 /* WMFReferencePanelViewController.swift */; }; + D8CE24F11E698E2400DAE2E0 /* WikidataFetcher+Places.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88FCADE1E4B74D300505A9F /* WikidataFetcher+Places.swift */; }; + D8CE24F21E698E2400DAE2E0 /* UIButton+WMFButton.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802BD1C0CD2360065EBC0 /* UIButton+WMFButton.m */; }; + D8CE24F31E698E2400DAE2E0 /* TableOfContentsAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E803701C0CD9A80065EBC0 /* TableOfContentsAnimator.swift */; }; + D8CE24F61E698E2400DAE2E0 /* WMFReferencePageBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0338A831DBF0AF20012F588 /* WMFReferencePageBackgroundView.swift */; }; + D8CE24F81E698E2400DAE2E0 /* UIApplication+RTL.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00050131C52D73800515F70 /* UIApplication+RTL.swift */; }; + D8CE24FB1E698E2400DAE2E0 /* WMFWelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083375C1DB16A09002860D2 /* WMFWelcomePanelViewController.swift */; }; + D8CE24FE1E698E2400DAE2E0 /* UIViewController+WMFAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84BF62E1DBE616D00E0C85E /* UIViewController+WMFAlerts.swift */; }; + D8CE24FF1E698E2400DAE2E0 /* NSDate+WMFPOTDTitle.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCB813C1C110702008BC602 /* NSDate+WMFPOTDTitle.m */; }; + D8CE25001E698E2400DAE2E0 /* WMFTableHeaderFooterLabelView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EE2438C1DC9889B00066CBD /* WMFTableHeaderFooterLabelView.m */; }; + D8CE25051E698E2400DAE2E0 /* WMFDatabaseHousekeeper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37B38EF1E5F432F00FE11BD /* WMFDatabaseHousekeeper.swift */; }; + D8CE25071E698E2400DAE2E0 /* PlaceSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEC1E438C0C00A3E89C /* PlaceSearch.swift */; }; + D8CE25091E698E2400DAE2E0 /* PlaceSearchSuggestionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEA1E438BE300A3E89C /* PlaceSearchSuggestionController.swift */; }; + D8CE250B1E698E2400DAE2E0 /* WMFPageHistoryRevision.m in Sources */ = {isa = PBXBuildFile; fileRef = B09B03EA1CE0FB2600009083 /* WMFPageHistoryRevision.m */; }; + D8CE25121E698E2400DAE2E0 /* WMFEmptyView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF863501C19E4F100006D2D /* WMFEmptyView.m */; }; + D8CE25151E698E2400DAE2E0 /* WMFSettingsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B02B82731C696ECA00B19309 /* WMFSettingsTableViewCell.m */; }; + D8CE25161E698E2400DAE2E0 /* UIView+WMFSubviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEDA1DB4B76B00615FA2 /* UIView+WMFSubviews.swift */; }; + D8CE25181E698E2400DAE2E0 /* WMFReferencePopoverMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0379A2A1D8B756C00D973CF /* WMFReferencePopoverMessageViewController.m */; }; + D8CE25191E698E2400DAE2E0 /* WMFSearchFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803431C0CD7980065EBC0 /* WMFSearchFetcher.m */; }; + D8CE251B1E698E2400DAE2E0 /* LoggingDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA15AE41C0E213300D0A3EA /* LoggingDefaults.swift */; }; + D8CE25201E698E2400DAE2E0 /* MWKTitleLanguageController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EBCA7411C162ECF004F1FD9 /* MWKTitleLanguageController.m */; }; + D8CE25211E698E2400DAE2E0 /* UIView+WMFSnapshotting.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803901C0CDABE0065EBC0 /* UIView+WMFSnapshotting.m */; }; + D8CE25221E698E2400DAE2E0 /* UIViewController+WMFStoryboardUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803E51C0CDD450065EBC0 /* UIViewController+WMFStoryboardUtilities.m */; }; + D8CE25231E698E2400DAE2E0 /* UIVIewController+WMFCommonRotationSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED2E9F91CB2B3B300D1C844 /* UIVIewController+WMFCommonRotationSupport.swift */; }; + D8CE25241E698E2400DAE2E0 /* (null) in Sources */ = {isa = PBXBuildFile; }; + D8CE25261E698E2400DAE2E0 /* WMFTitleInsetRespectingButton.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803CB1C0CDC9B0065EBC0 /* WMFTitleInsetRespectingButton.m */; }; + D8CE25271E698E2400DAE2E0 /* WMFChangePasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE561E4526A40033BD6E /* WMFChangePasswordViewController.swift */; }; + D8CE252E1E698E2400DAE2E0 /* WMFArticleRevisionFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4DC1C223DCB00B5AFDE /* WMFArticleRevisionFetcher.m */; }; + D8CE252F1E698E2400DAE2E0 /* WMFArticleLanguagesSectionHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B4CF091CC0500E0051DF7A /* WMFArticleLanguagesSectionHeader.m */; }; + D8CE25351E698E2400DAE2E0 /* WMFTwoFactorPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0ED17331E4912EB008B70AD /* WMFTwoFactorPasswordViewController.swift */; }; + D8CE25371E698E2400DAE2E0 /* SavedPagesFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805861C0CE2C60065EBC0 /* SavedPagesFunnel.m */; }; + D8CE25391E698E2400DAE2E0 /* WMFChange.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FEECCC1DE3729400B883F0 /* WMFChange.m */; }; + D8CE253B1E698E2400DAE2E0 /* MWKSearchRedirectMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807CA1C0CF04A0065EBC0 /* MWKSearchRedirectMapping.m */; }; + D8CE253C1E698E2400DAE2E0 /* MWKImageInfoFetcher+PicOfTheDayInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = BC62FFBF1C11064200533DA9 /* MWKImageInfoFetcher+PicOfTheDayInfo.m */; }; + D8CE25411E698E2400DAE2E0 /* WMFScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027447C1E6253E200E7B248 /* WMFScrollViewController.swift */; }; + D8CE25471E698E2400DAE2E0 /* PageHistorySection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03EC1CE0FB4200009083 /* PageHistorySection.swift */; }; + D8CE254B1E698E2400DAE2E0 /* TableOfContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E8036C1C0CD98B0065EBC0 /* TableOfContentsViewController.swift */; }; + D8CE254D1E698E2400DAE2E0 /* ProtectedEditAttemptFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805821C0CE2C60065EBC0 /* ProtectedEditAttemptFunnel.m */; }; + D8CE254E1E698E2400DAE2E0 /* WKWebView+WMFWebViewControllerJavascript.m in Sources */ = {isa = PBXBuildFile; fileRef = B0DF6F801CFE1D0B0046E507 /* WKWebView+WMFWebViewControllerJavascript.m */; }; + D8CE25521E698E2400DAE2E0 /* WMFArticleTextActivitySource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EC044781C7917860033D773 /* WMFArticleTextActivitySource.m */; }; + D8CE25571E698E2400DAE2E0 /* WMFPasswordResetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0267CF21E32A0CB006B6D8D /* WMFPasswordResetter.swift */; }; + D8CE255A1E698E2400DAE2E0 /* UIBarButtonItem+WMFButtonConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802B71C0CD2140065EBC0 /* UIBarButtonItem+WMFButtonConvenience.m */; }; + D8CE255B1E698E2400DAE2E0 /* UIViewController+WMFEmptyView.m in Sources */ = {isa = PBXBuildFile; fileRef = B08E7E9A1C1FA57A00EC3C99 /* UIViewController+WMFEmptyView.m */; }; + D8CE25631E698E2400DAE2E0 /* NSUserDefaults+WMFApplicationDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87914DC1DFA04E10012C5DA /* NSUserDefaults+WMFApplicationDefaults.swift */; }; + D8CE25651E698E2400DAE2E0 /* AboutViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806931C0CEA7B0065EBC0 /* AboutViewController.m */; }; + D8CE25691E698E2400DAE2E0 /* WMFArticleLanguagesSectionFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = B0866F441CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.m */; }; + D8CE256B1E698E2400DAE2E0 /* WMFWelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEDC1DB591C400615FA2 /* WMFWelcomeContainerViewController.swift */; }; + D8CE256C1E698E2400DAE2E0 /* WMFAccountCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE471E428C940033BD6E /* WMFAccountCreator.swift */; }; + D8CE256D1E698E2400DAE2E0 /* UIViewController+WMFHideKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B066F0D41E513DAA00A199F8 /* UIViewController+WMFHideKeyboard.swift */; }; + D8CE256F1E698E2400DAE2E0 /* WeakScriptMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B069FA2D1CEACB8400083D59 /* WeakScriptMessageDelegate.swift */; }; + D8CE25751E698E2400DAE2E0 /* UIView+Animations.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E294CC1DB2CF4300861D04 /* UIView+Animations.swift */; }; + D8CE25771E698E2400DAE2E0 /* MWKLanguageLinkFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806621C0CE9030065EBC0 /* MWKLanguageLinkFetcher.m */; }; + D8CE257B1E698E2400DAE2E0 /* WMFImageTextActivitySource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC0447A1C796FEF0033D773 /* WMFImageTextActivitySource.swift */; }; + D8CE257F1E698E2400DAE2E0 /* ToCInteractionFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805881C0CE2C60065EBC0 /* ToCInteractionFunnel.m */; }; + D8CE25821E698E2400DAE2E0 /* WMFCompassView.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8031B1C0CD6820065EBC0 /* WMFCompassView.m */; }; + D8CE25831E698E2400DAE2E0 /* WMFWelcomeIntroductionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E294D21DB2DC8900861D04 /* WMFWelcomeIntroductionViewController.swift */; }; + D8CE25891E698E2400DAE2E0 /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = D896C7951D6F2E4E007101DF /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m */; }; + D8CE258E1E698E2400DAE2E0 /* UIView+WMFFrameUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805201C0CE0DC0065EBC0 /* UIView+WMFFrameUtils.m */; }; + D8CE258F1E698E2400DAE2E0 /* WMFWelcomeAnimationViewControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEE21DB5B16E00615FA2 /* WMFWelcomeAnimationViewControllers.swift */; }; + D8CE25901E698E2400DAE2E0 /* TableOfContentsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E803721C0CD9C10065EBC0 /* TableOfContentsCell.swift */; }; + D8CE25931E698E2400DAE2E0 /* WMFSettingsMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B0EC211C6999A9006F0D9C /* WMFSettingsMenuItem.m */; }; + D8CE25951E698E2400DAE2E0 /* UIScrollView+ScrollSubviewToLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805091C0CE0DC0065EBC0 /* UIScrollView+ScrollSubviewToLocation.m */; }; + D8CE25961E698E2400DAE2E0 /* MTLValueTransformer+WMFNumericValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8062A1C0CE7670065EBC0 /* MTLValueTransformer+WMFNumericValueTransformer.m */; }; + D8CE259C1E698E2400DAE2E0 /* TableOfContentsHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84692DE1D5E1E3F000A7058 /* TableOfContentsHeader.swift */; }; + D8CE259F1E698E2400DAE2E0 /* UIViewController+WMFScrollToTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE489021D4ADCA00088505C /* UIViewController+WMFScrollToTop.swift */; }; + D8CE25A51E698E2400DAE2E0 /* UIApplicationShortcutItem+WMFShortcutItem.m in Sources */ = {isa = PBXBuildFile; fileRef = B0EF42CF1C43FDD200D125A8 /* UIApplicationShortcutItem+WMFShortcutItem.m */; }; + D8CE25A91E698E2400DAE2E0 /* WMFAuthAccountCreationInfoFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F92C811E3FFEB900B72802 /* WMFAuthAccountCreationInfoFetcher.swift */; }; + D8CE25AB1E698E2400DAE2E0 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF5BB6C1C110C2100DE75E1 /* AppDelegate.m */; }; + D8CE25AD1E698E2400DAE2E0 /* ArticlePlace.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82972821E3950100061550A /* ArticlePlace.swift */; }; + D8CE25AF1E698E2400DAE2E0 /* WMFSearchResults.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803471C0CD7AA0065EBC0 /* WMFSearchResults.m */; }; + D8CE25B11E698E2400DAE2E0 /* WMFBarButtonItemPopoverMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0DE92291D6E272B00EC76A7 /* WMFBarButtonItemPopoverMessageViewController.m */; }; + D8CE25B31E698E2400DAE2E0 /* WMFReferencePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01490A21DB96E5F007F5391 /* WMFReferencePageViewController.swift */; }; + D8CE25BC1E698E2400DAE2E0 /* WikiTextSectionUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806571C0CE84B0065EBC0 /* WikiTextSectionUploader.m */; }; + D8CE25C01E698E2400DAE2E0 /* UIViewController+WMFDynamicHeightPopoverMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B02376B31D6ECCF9007DBA73 /* UIViewController+WMFDynamicHeightPopoverMessage.m */; }; + D8CE25C61E698E2400DAE2E0 /* WMFWelcomeAnalyticsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08337601DB17DC5002860D2 /* WMFWelcomeAnalyticsViewController.swift */; }; + D8CE25C71E698E2400DAE2E0 /* WMFRevisionQueryResults.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4E41C22429100B5AFDE /* WMFRevisionQueryResults.m */; }; + D8CE25C81E698E2400DAE2E0 /* WMFAppViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802C01C0CD27F0065EBC0 /* WMFAppViewController.m */; }; + D8CE25CC1E698E2400DAE2E0 /* UIView+IBExtras.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E805181C0CE0DC0065EBC0 /* UIView+IBExtras.swift */; }; + D8CE25CE1E698E2400DAE2E0 /* WMFHamburgerMenuFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8058A1C0CE2C60065EBC0 /* WMFHamburgerMenuFunnel.m */; }; + D8CE25CF1E698E2400DAE2E0 /* UIViewController+WMFWelcomeStoryboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B30D01DB817660012281F /* UIViewController+WMFWelcomeStoryboard.swift */; }; + D8CE25D01E698E2400DAE2E0 /* WMFLanguageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF224991CC5536200FDF78E /* WMFLanguageCell.m */; }; + D8CE25D31E698E2400DAE2E0 /* Array+WMFAllFieldsFilled.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04C444A1E56966B00C6DFB0 /* Array+WMFAllFieldsFilled.swift */; }; + D8CE25D41E698E2400DAE2E0 /* NewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8940CED1DB56C8A00E17F9E /* NewsViewController.swift */; }; + D8CE25D51E698E2400DAE2E0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8071E1C0CEC8A0065EBC0 /* main.m */; }; + D8CE25D71E698E2400DAE2E0 /* WMFDailyStatsLoggingFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DC0941C74F0D700622CBD /* WMFDailyStatsLoggingFunnel.m */; }; + D8CE25D81E698E2400DAE2E0 /* WMFRandomDiceButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D84F2BF91D2FEE6300963D42 /* WMFRandomDiceButton.m */; }; + D8CE25DF1E698E2400DAE2E0 /* WMFFirstRandomViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D84F2C021D30162700963D42 /* WMFFirstRandomViewController.m */; }; + D8CE25E11E698E2400DAE2E0 /* ArticlePopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82972861E3A49980061550A /* ArticlePopoverViewController.swift */; }; + D8CE25E21E698E2400DAE2E0 /* WMFLogFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807351C0CED810065EBC0 /* WMFLogFormatter.m */; }; + D8CE25E51E698E2400DAE2E0 /* NSString+FormattedAttributedString.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804A71C0CE0B40065EBC0 /* NSString+FormattedAttributedString.m */; }; + D8CE25EA1E698E2400DAE2E0 /* WMFForgotPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0267CEC1E316AE3006B6D8D /* WMFForgotPasswordViewController.swift */; }; + D8CE25EC1E698E2400DAE2E0 /* WMFImageGalleryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E4A34711CBBFCD400A400F6 /* WMFImageGalleryViewController.m */; }; + D8CE25F01E698E2400DAE2E0 /* WMFAccountCreationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE411E413B3F0033BD6E /* WMFAccountCreationViewController.swift */; }; + D8CE25F31E698E2400DAE2E0 /* NSAttributedString+WMFModify.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804841C0CE0B40065EBC0 /* NSAttributedString+WMFModify.m */; }; + D8CE25F41E698E2400DAE2E0 /* WMFSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806C11C0CEB380065EBC0 /* WMFSettingsViewController.m */; }; + D8CE25F61E698E2400DAE2E0 /* UIScrollView+WMFContentOffsetUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8050B1C0CE0DC0065EBC0 /* UIScrollView+WMFContentOffsetUtils.m */; }; + D8CE25F81E698E2400DAE2E0 /* WMFImageGalleryDetailOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E9B9E301CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.m */; }; + D8CE25FB1E698E2400DAE2E0 /* UIApplication+SystemSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81E5F871E5F2C8400E1A80C /* UIApplication+SystemSettings.swift */; }; + D8CE25FE1E698E2400DAE2E0 /* WMFArticleRevision.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4E11C223FAE00B5AFDE /* WMFArticleRevision.m */; }; + D8CE25FF1E698E2400DAE2E0 /* ArticlePlaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6BAEE1E4C9C0700A981C8 /* ArticlePlaceView.swift */; }; + D8CE26001E698E2400DAE2E0 /* WMFCaptchaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE491E42D19D0033BD6E /* WMFCaptchaViewController.swift */; }; + D8CE26011E698E2400DAE2E0 /* DDLog+WMFLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804741C0CE0B40065EBC0 /* DDLog+WMFLogger.m */; }; + D8CE26031E698E2400DAE2E0 /* WMFLegacyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E281A321DC263DE00FA1AB1 /* WMFLegacyReference.swift */; }; + D8CE26041E698E2400DAE2E0 /* WMFWelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083371D1DADB251002860D2 /* WMFWelcomePageViewController.swift */; }; + D8CE26051E698E2400DAE2E0 /* MKCoordinateRegion+Dimensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEE1E438C5100A3E89C /* MKCoordinateRegion+Dimensions.swift */; }; + D8CE26071E698E2400DAE2E0 /* WMFAlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBCA7471C176389004F1FD9 /* WMFAlertManager.swift */; }; + D8CE26081E698E2400DAE2E0 /* WMFWelcomeLanguageTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083375E1DB17DA5002860D2 /* WMFWelcomeLanguageTableViewController.swift */; }; + D8CE260A1E698E2400DAE2E0 /* WMFLoginFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805801C0CE2C60065EBC0 /* WMFLoginFunnel.m */; }; + D8CE260C1E698E2400DAE2E0 /* MapUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88FCAE01E4B776600505A9F /* MapUtilities.swift */; }; + D8CE260E1E698E2400DAE2E0 /* WKWebView+ElementLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8052E1C0CE0DC0065EBC0 /* WKWebView+ElementLocation.m */; }; + D8CE26101E698E2400DAE2E0 /* UIViewController+WMFStoryboardUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B30CE1DB813760012281F /* UIViewController+WMFStoryboardUtilities.swift */; }; + D8CE26121E698E2400DAE2E0 /* TableOfContentsPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E8036E1C0CD99A0065EBC0 /* TableOfContentsPresentationController.swift */; }; + D8CE26151E698E2400DAE2E0 /* WMFLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE3F1E4068C60033BD6E /* WMFLoginViewController.swift */; }; + D8CE26191E698E2400DAE2E0 /* PageHistoryFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03F11CE0FB6300009083 /* PageHistoryFetcher.swift */; }; + D8CE261B1E698E2400DAE2E0 /* CreateAccountFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805781C0CE2C60065EBC0 /* CreateAccountFunnel.m */; }; + D8CE261D1E698E2400DAE2E0 /* WMFShareFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8059C1C0CE2F50065EBC0 /* WMFShareFunnel.m */; }; + D8CE26201E698E2400DAE2E0 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8D553611DF1B63200B90177 /* QuartzCore.framework */; settings = {ATTRIBUTES = (Required, ); }; }; + D8CE262B1E698E2400DAE2E0 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 040E5C4E184566F4007AFE6F /* CoreData.framework */; }; + D8CE262C1E698E2400DAE2E0 /* WMF.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; }; + D8CE262E1E698E2400DAE2E0 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D499143A181D51DE00E6073C /* CoreGraphics.framework */; }; + D8CE26321E698E2400DAE2E0 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D499143C181D51DE00E6073C /* UIKit.framework */; }; + D8CE26351E698E2400DAE2E0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4991438181D51DE00E6073C /* Foundation.framework */; }; + D8CE26361E698E2400DAE2E0 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 041EFC361996A1F800B2CB28 /* MapKit.framework */; }; + D8CE263D1E698E2400DAE2E0 /* WMFCaptchaViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E803FD1C0CDE480065EBC0 /* WMFCaptchaViewController.storyboard */; }; + D8CE26421E698E2400DAE2E0 /* AboutViewController.plist in Resources */ = {isa = PBXBuildFile; fileRef = B0E806941C0CEA7B0065EBC0 /* AboutViewController.plist */; }; + D8CE26431E698E2400DAE2E0 /* NotificationBackgroundMessage.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4D1DF22E1700B95311 /* NotificationBackgroundMessage.png */; }; + D8CE26471E698E2400DAE2E0 /* WMFRandomDiceButtonRoll.js in Resources */ = {isa = PBXBuildFile; fileRef = D84F2BFA1D2FEE6300963D42 /* WMFRandomDiceButtonRoll.js */; }; + D8CE26491E698E2400DAE2E0 /* NotificationBackgroundSuccess.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4F1DF22E1700B95311 /* NotificationBackgroundSuccess.png */; }; + D8CE264A1E698E2400DAE2E0 /* NotificationBackgroundErrorIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4C1DF22E1700B95311 /* NotificationBackgroundErrorIcon@2x.png */; }; + D8CE264C1E698E2400DAE2E0 /* WMFArticleLanguagesSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0866F451CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.xib */; }; + D8CE264D1E698E2400DAE2E0 /* NotificationBackgroundErrorIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4B1DF22E1700B95311 /* NotificationBackgroundErrorIcon.png */; }; + D8CE264E1E698E2400DAE2E0 /* WMFLanguageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E4D071C1CC5526200AE968B /* WMFLanguageCell.xib */; }; + D8CE26531E698E2400DAE2E0 /* NotificationBackgroundSuccessIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B521DF22E1700B95311 /* NotificationBackgroundSuccessIcon@2x.png */; }; + D8CE26541E698E2400DAE2E0 /* WMFTwoFactorPasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0ED173C1E49831B008B70AD /* WMFTwoFactorPasswordViewController.storyboard */; }; + D8CE26581E698E2400DAE2E0 /* TableOfContentsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0E803731C0CD9C10065EBC0 /* TableOfContentsCell.xib */; }; + D8CE26591E698E2400DAE2E0 /* WMFReferencePopoverMessageViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0379A361D8B9D2400D973CF /* WMFReferencePopoverMessageViewController.storyboard */; }; + D8CE265E1E698E2400DAE2E0 /* WMFEmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0EF8634D1C19E02700006D2D /* WMFEmptyView.xib */; }; + D8CE265F1E698E2400DAE2E0 /* NotificationBackgroundMessage@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4E1DF22E1700B95311 /* NotificationBackgroundMessage@2x.png */; }; + D8CE26601E698E2400DAE2E0 /* NotificationBackgroundWarningIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B551DF22E1700B95311 /* NotificationBackgroundWarningIcon.png */; }; + D8CE26611E698E2400DAE2E0 /* WMFLoginViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804001C0CDE480065EBC0 /* WMFLoginViewController.storyboard */; }; + D8CE26631E698E2400DAE2E0 /* WMFWelcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E26B0741C0F4F720004D687 /* WMFWelcome.storyboard */; }; + D8CE266B1E698E2400DAE2E0 /* NotificationBackgroundWarning@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B541DF22E1700B95311 /* NotificationBackgroundWarning@2x.png */; }; + D8CE266C1E698E2400DAE2E0 /* NotificationBackgroundSuccess@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B501DF22E1700B95311 /* NotificationBackgroundSuccess@2x.png */; }; + D8CE266D1E698E2400DAE2E0 /* WMFChangePasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0C6BE521E4526810033BD6E /* WMFChangePasswordViewController.storyboard */; }; + D8CE266F1E698E2400DAE2E0 /* NotificationBackgroundError.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B491DF22E1700B95311 /* NotificationBackgroundError.png */; }; + D8CE26701E698E2400DAE2E0 /* WMFReferencePanels.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B01490A01DB96BD6007F5391 /* WMFReferencePanels.storyboard */; }; + D8CE26711E698E2400DAE2E0 /* Places.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D87234031E1FF18100751E83 /* Places.storyboard */; }; + D8CE26731E698E2400DAE2E0 /* WMFAccountCreationViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E803FC1C0CDE480065EBC0 /* WMFAccountCreationViewController.storyboard */; }; + D8CE26741E698E2400DAE2E0 /* NotificationBackgroundSuccessIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B511DF22E1700B95311 /* NotificationBackgroundSuccessIcon.png */; }; + D8CE26751E698E2400DAE2E0 /* WMFSettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B02B82741C696ECA00B19309 /* WMFSettingsTableViewCell.xib */; }; + D8CE26781E698E2400DAE2E0 /* NotificationBackgroundWarningIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B561DF22E1700B95311 /* NotificationBackgroundWarningIcon@2x.png */; }; + D8CE267B1E698E2400DAE2E0 /* TableOfContentsHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = D84692DF1D5E1E3F000A7058 /* TableOfContentsHeader.xib */; }; + D8CE267E1E698E2400DAE2E0 /* WMFBarButtonItemPopoverMessageViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0DE92261D6E26C700EC76A7 /* WMFBarButtonItemPopoverMessageViewController.storyboard */; }; + D8CE26801E698E2400DAE2E0 /* TSMessagesDefaultDesign.json in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B591DF22E1700B95311 /* TSMessagesDefaultDesign.json */; }; + D8CE26811E698E2400DAE2E0 /* WMFArticleLanguagesSectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0B4CF0B1CC0501B0051DF7A /* WMFArticleLanguagesSectionHeader.xib */; }; + D8CE26831E698E2400DAE2E0 /* ArticlePopoverViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D82972871E3A49980061550A /* ArticlePopoverViewController.xib */; }; + D8CE26841E698E2400DAE2E0 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E69CD5A1C8773410095918B /* Launch Screen.storyboard */; }; + D8CE26851E698E2400DAE2E0 /* EditSaveViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804031C0CDE480065EBC0 /* EditSaveViewController.storyboard */; }; + D8CE26861E698E2400DAE2E0 /* NotificationBackgroundWarning.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B531DF22E1700B95311 /* NotificationBackgroundWarning.png */; }; + D8CE26881E698E2400DAE2E0 /* NotificationBackgroundError@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4A1DF22E1700B95311 /* NotificationBackgroundError@2x.png */; }; + D8CE268B1E698E2400DAE2E0 /* WMFImageGalleryDetailOverlayView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E9B9E2F1CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.xib */; }; + D8CE268F1E698E2400DAE2E0 /* WMFRandomDiceButton.html in Resources */ = {isa = PBXBuildFile; fileRef = D84F2BF81D2FEE6300963D42 /* WMFRandomDiceButton.html */; }; + D8CE26941E698E2400DAE2E0 /* WMFForgotPasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0267CE81E316A79006B6D8D /* WMFForgotPasswordViewController.storyboard */; }; + D8CE26971E698E2400DAE2E0 /* WMFTableHeaderFooterLabelView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0EE2438E1DC988AA00066CBD /* WMFTableHeaderFooterLabelView.xib */; }; + D8CE269A1E698E2400DAE2E0 /* NotificationButtonBackground.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B571DF22E1700B95311 /* NotificationButtonBackground.png */; }; + D8CE269C1E698E2400DAE2E0 /* WMFSettingsViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804091C0CDE480065EBC0 /* WMFSettingsViewController.storyboard */; }; + D8CE269D1E698E2400DAE2E0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D4991453181D51DE00E6073C /* Images.xcassets */; }; + D8CE269E1E698E2400DAE2E0 /* ReadingThemesControlsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B09B03F41CE0FB7700009083 /* ReadingThemesControlsViewController.xib */; }; + D8CE269F1E698E2400DAE2E0 /* NotificationButtonBackground@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B581DF22E1700B95311 /* NotificationButtonBackground@2x.png */; }; + D8CE26A31E698E2400DAE2E0 /* ContinueReadingWidget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0E8380631D64989F0076EDE4 /* ContinueReadingWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + D8CE26A51E698E2400DAE2E0 /* WMF.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8CE9B031FDEBB1900AE7D49 /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 831835301FD1AC490025DD3D /* NavigationBar.swift */; }; + D8CE9B041FDEBB2C00AE7D49 /* NavigationBarHider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CE9AFD1FDEB14E00AE7D49 /* NavigationBarHider.swift */; }; + D8D365151E953C7100593A38 /* ImageControllerCompletionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D365141E953C7100593A38 /* ImageControllerCompletionManager.swift */; }; + D8D550811DF0D2BD00B90177 /* NSArray+WMFMatching.m in Sources */ = {isa = PBXBuildFile; fileRef = D8D550801DF0D2BD00B90177 /* NSArray+WMFMatching.m */; }; + D8D553621DF1B63200B90177 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8D553611DF1B63200B90177 /* QuartzCore.framework */; settings = {ATTRIBUTES = (Required, ); }; }; + D8D92B5A1DF22E1700B95311 /* NotificationBackgroundError.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B491DF22E1700B95311 /* NotificationBackgroundError.png */; }; + D8D92B5B1DF22E1700B95311 /* NotificationBackgroundError@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4A1DF22E1700B95311 /* NotificationBackgroundError@2x.png */; }; + D8D92B5C1DF22E1700B95311 /* NotificationBackgroundErrorIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4B1DF22E1700B95311 /* NotificationBackgroundErrorIcon.png */; }; + D8D92B5D1DF22E1700B95311 /* NotificationBackgroundErrorIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4C1DF22E1700B95311 /* NotificationBackgroundErrorIcon@2x.png */; }; + D8D92B5E1DF22E1700B95311 /* NotificationBackgroundMessage.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4D1DF22E1700B95311 /* NotificationBackgroundMessage.png */; }; + D8D92B5F1DF22E1700B95311 /* NotificationBackgroundMessage@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4E1DF22E1700B95311 /* NotificationBackgroundMessage@2x.png */; }; + D8D92B601DF22E1700B95311 /* NotificationBackgroundSuccess.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4F1DF22E1700B95311 /* NotificationBackgroundSuccess.png */; }; + D8D92B611DF22E1700B95311 /* NotificationBackgroundSuccess@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B501DF22E1700B95311 /* NotificationBackgroundSuccess@2x.png */; }; + D8D92B621DF22E1700B95311 /* NotificationBackgroundSuccessIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B511DF22E1700B95311 /* NotificationBackgroundSuccessIcon.png */; }; + D8D92B631DF22E1700B95311 /* NotificationBackgroundSuccessIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B521DF22E1700B95311 /* NotificationBackgroundSuccessIcon@2x.png */; }; + D8D92B641DF22E1700B95311 /* NotificationBackgroundWarning.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B531DF22E1700B95311 /* NotificationBackgroundWarning.png */; }; + D8D92B651DF22E1700B95311 /* NotificationBackgroundWarning@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B541DF22E1700B95311 /* NotificationBackgroundWarning@2x.png */; }; + D8D92B661DF22E1700B95311 /* NotificationBackgroundWarningIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B551DF22E1700B95311 /* NotificationBackgroundWarningIcon.png */; }; + D8D92B671DF22E1700B95311 /* NotificationBackgroundWarningIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B561DF22E1700B95311 /* NotificationBackgroundWarningIcon@2x.png */; }; + D8D92B681DF22E1700B95311 /* NotificationButtonBackground.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B571DF22E1700B95311 /* NotificationButtonBackground.png */; }; + D8D92B691DF22E1700B95311 /* NotificationButtonBackground@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B581DF22E1700B95311 /* NotificationButtonBackground@2x.png */; }; + D8D92B6A1DF22E1700B95311 /* TSMessagesDefaultDesign.json in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B591DF22E1700B95311 /* TSMessagesDefaultDesign.json */; }; + D8DC16F31D6F6F2C00D6D9FB /* NSUserDefaults+WMFExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8494ADC1D6C85C500337433 /* NSUserDefaults+WMFExtensions.swift */; }; + D8E27BA11F82B38100F9D2B3 /* RMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E27B9E1F82AFE600F9D2B3 /* RMessageView.m */; }; + D8E27BA21F82B38100F9D2B3 /* RMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E27B9E1F82AFE600F9D2B3 /* RMessageView.m */; }; + D8E27BA31F82B38100F9D2B3 /* RMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E27B9E1F82AFE600F9D2B3 /* RMessageView.m */; }; + D8E27BA41F82B38200F9D2B3 /* RMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E27B9E1F82AFE600F9D2B3 /* RMessageView.m */; }; + D8E27BA61F82B38500F9D2B3 /* RMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E27B9D1F82AFE600F9D2B3 /* RMessage.m */; }; + D8E27BA71F82B38500F9D2B3 /* RMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E27B9D1F82AFE600F9D2B3 /* RMessage.m */; }; + D8E27BA81F82B38600F9D2B3 /* RMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E27B9D1F82AFE600F9D2B3 /* RMessage.m */; }; + D8E27BA91F82B38700F9D2B3 /* RMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E27B9D1F82AFE600F9D2B3 /* RMessage.m */; }; + D8E27BAD1F82B54D00F9D2B3 /* RMessageDefaultDesign.json in Resources */ = {isa = PBXBuildFile; fileRef = D8E27BAC1F82B4FF00F9D2B3 /* RMessageDefaultDesign.json */; }; + D8E27BAE1F82B54D00F9D2B3 /* RMessageDefaultDesign.json in Resources */ = {isa = PBXBuildFile; fileRef = D8E27BAC1F82B4FF00F9D2B3 /* RMessageDefaultDesign.json */; }; + D8E27BAF1F82B54D00F9D2B3 /* RMessageDefaultDesign.json in Resources */ = {isa = PBXBuildFile; fileRef = D8E27BAC1F82B4FF00F9D2B3 /* RMessageDefaultDesign.json */; }; + D8E27BB01F82B54E00F9D2B3 /* RMessageDefaultDesign.json in Resources */ = {isa = PBXBuildFile; fileRef = D8E27BAC1F82B4FF00F9D2B3 /* RMessageDefaultDesign.json */; }; + D8E27BB41F82B5DB00F9D2B3 /* RMessageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8E27BB21F82B5CB00F9D2B3 /* RMessageView.xib */; }; + D8E27BB51F82B5DB00F9D2B3 /* RMessageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8E27BB21F82B5CB00F9D2B3 /* RMessageView.xib */; }; + D8E27BB61F82B5DC00F9D2B3 /* RMessageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8E27BB21F82B5CB00F9D2B3 /* RMessageView.xib */; }; + D8E27BB71F82B5DC00F9D2B3 /* RMessageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8E27BB21F82B5CB00F9D2B3 /* RMessageView.xib */; }; + D8E2B0F31D6CC5DE006FFB24 /* WMFImageURLParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807331C0CED810065EBC0 /* WMFImageURLParsing.m */; }; + D8E4CCAA1D931CE100EB6C61 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = BCF012321AD2FA38008D3675 /* assets */; }; + D8E6FF6724054FA100686272 /* ArticleViewController+LinkPreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF6624054FA100686272 /* ArticleViewController+LinkPreviewing.swift */; }; + D8E6FF6824054FA100686272 /* ArticleViewController+LinkPreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF6624054FA100686272 /* ArticleViewController+LinkPreviewing.swift */; }; + D8E6FF6924054FA100686272 /* ArticleViewController+LinkPreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF6624054FA100686272 /* ArticleViewController+LinkPreviewing.swift */; }; + D8E6FF6A24054FA100686272 /* ArticleViewController+LinkPreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF6624054FA100686272 /* ArticleViewController+LinkPreviewing.swift */; }; + D8E6FF6C24056AC300686272 /* ArticleViewController+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF6B24056AC300686272 /* ArticleViewController+ContextMenu.swift */; }; + D8E6FF6D24056AC300686272 /* ArticleViewController+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF6B24056AC300686272 /* ArticleViewController+ContextMenu.swift */; }; + D8E6FF6E24056AC300686272 /* ArticleViewController+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF6B24056AC300686272 /* ArticleViewController+ContextMenu.swift */; }; + D8E6FF6F24056AC300686272 /* ArticleViewController+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF6B24056AC300686272 /* ArticleViewController+ContextMenu.swift */; }; + D8E6FF7724058AC600686272 /* WMFWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF7624058AC600686272 /* WMFWebView.m */; }; + D8E6FF7824058AC600686272 /* WMFWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF7624058AC600686272 /* WMFWebView.m */; }; + D8E6FF7924058AC600686272 /* WMFWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF7624058AC600686272 /* WMFWebView.m */; }; + D8E6FF7A24058AC600686272 /* WMFWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF7624058AC600686272 /* WMFWebView.m */; }; + D8E6FF7C2405AAC400686272 /* ArticleViewController+Analytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF7B2405AAC400686272 /* ArticleViewController+Analytics.swift */; }; + D8E6FF7D2405AAC400686272 /* ArticleViewController+Analytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF7B2405AAC400686272 /* ArticleViewController+Analytics.swift */; }; + D8E6FF7E2405AAC400686272 /* ArticleViewController+Analytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF7B2405AAC400686272 /* ArticleViewController+Analytics.swift */; }; + D8E6FF7F2405AAC400686272 /* ArticleViewController+Analytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E6FF7B2405AAC400686272 /* ArticleViewController+Analytics.swift */; }; + D8E78FA41FB4C8250094B968 /* ReadingListsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E78FA31FB4C8250094B968 /* ReadingListsController.swift */; }; + D8E78FA61FB4C8740094B968 /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E78FA51FB4C8740094B968 /* Session.swift */; }; + D8E892252176124F00587F61 /* PeriodicWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E892242176124F00587F61 /* PeriodicWorker.swift */; }; + D8EBD1B81FBB13EE00AA7DA9 /* ReadingList+JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8EBD1B71FBB13EE00AA7DA9 /* ReadingList+JSON.swift */; }; + D8EBD1BC1FBB177D00AA7DA9 /* ReadingListEntry+JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8EBD1BB1FBB177D00AA7DA9 /* ReadingListEntry+JSON.swift */; }; + D8EC3DD81E9BDA35006712EB /* UserLocationAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6BAEC1E4C9BF400A981C8 /* UserLocationAnnotationView.swift */; }; + D8EC3DD91E9BDA35006712EB /* WMFSearchFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805991C0CE2E40065EBC0 /* WMFSearchFunnel.m */; }; + D8EC3DDA1E9BDA35006712EB /* WMFImageURLActivitySource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC0447E1C797DC20033D773 /* WMFImageURLActivitySource.swift */; }; + D8EC3DDF1E9BDA35006712EB /* WMFCaptchaResetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F92C6E1E3C580900B72802 /* WMFCaptchaResetter.swift */; }; + D8EC3DE31E9BDA35006712EB /* PlacesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87233FF1E1FF0A500751E83 /* PlacesViewController.swift */; }; + D8EC3DE51E9BDA35006712EB /* WMFReferencePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01490A41DB96EA7007F5391 /* WMFReferencePanelViewController.swift */; }; + D8EC3DE81E9BDA35006712EB /* WikidataFetcher+Places.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88FCADE1E4B74D300505A9F /* WikidataFetcher+Places.swift */; }; + D8EC3DE91E9BDA35006712EB /* UIButton+WMFButton.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802BD1C0CD2360065EBC0 /* UIButton+WMFButton.m */; }; + D8EC3DEA1E9BDA35006712EB /* TableOfContentsAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E803701C0CD9A80065EBC0 /* TableOfContentsAnimator.swift */; }; + D8EC3DED1E9BDA35006712EB /* WMFReferencePageBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0338A831DBF0AF20012F588 /* WMFReferencePageBackgroundView.swift */; }; + D8EC3DEF1E9BDA35006712EB /* UIApplication+RTL.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00050131C52D73800515F70 /* UIApplication+RTL.swift */; }; + D8EC3DF21E9BDA35006712EB /* WMFWelcomePanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083375C1DB16A09002860D2 /* WMFWelcomePanelViewController.swift */; }; + D8EC3DF51E9BDA35006712EB /* UIViewController+WMFAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84BF62E1DBE616D00E0C85E /* UIViewController+WMFAlerts.swift */; }; + D8EC3DF61E9BDA35006712EB /* NSDate+WMFPOTDTitle.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCB813C1C110702008BC602 /* NSDate+WMFPOTDTitle.m */; }; + D8EC3DF71E9BDA35006712EB /* WMFTableHeaderFooterLabelView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EE2438C1DC9889B00066CBD /* WMFTableHeaderFooterLabelView.m */; }; + D8EC3DFC1E9BDA35006712EB /* WMFDatabaseHousekeeper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B37B38EF1E5F432F00FE11BD /* WMFDatabaseHousekeeper.swift */; }; + D8EC3DFE1E9BDA35006712EB /* PlaceSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEC1E438C0C00A3E89C /* PlaceSearch.swift */; }; + D8EC3E001E9BDA35006712EB /* PlaceSearchSuggestionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEA1E438BE300A3E89C /* PlaceSearchSuggestionController.swift */; }; + D8EC3E021E9BDA35006712EB /* WMFPageHistoryRevision.m in Sources */ = {isa = PBXBuildFile; fileRef = B09B03EA1CE0FB2600009083 /* WMFPageHistoryRevision.m */; }; + D8EC3E091E9BDA35006712EB /* WMFEmptyView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF863501C19E4F100006D2D /* WMFEmptyView.m */; }; + D8EC3E0C1E9BDA35006712EB /* WMFSettingsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B02B82731C696ECA00B19309 /* WMFSettingsTableViewCell.m */; }; + D8EC3E0D1E9BDA35006712EB /* UIView+WMFSubviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEDA1DB4B76B00615FA2 /* UIView+WMFSubviews.swift */; }; + D8EC3E0F1E9BDA35006712EB /* WMFReferencePopoverMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0379A2A1D8B756C00D973CF /* WMFReferencePopoverMessageViewController.m */; }; + D8EC3E101E9BDA35006712EB /* WMFSearchFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803431C0CD7980065EBC0 /* WMFSearchFetcher.m */; }; + D8EC3E121E9BDA35006712EB /* LoggingDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA15AE41C0E213300D0A3EA /* LoggingDefaults.swift */; }; + D8EC3E171E9BDA35006712EB /* MWKTitleLanguageController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EBCA7411C162ECF004F1FD9 /* MWKTitleLanguageController.m */; }; + D8EC3E181E9BDA35006712EB /* UIView+WMFSnapshotting.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803901C0CDABE0065EBC0 /* UIView+WMFSnapshotting.m */; }; + D8EC3E191E9BDA35006712EB /* UIViewController+WMFStoryboardUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803E51C0CDD450065EBC0 /* UIViewController+WMFStoryboardUtilities.m */; }; + D8EC3E1A1E9BDA35006712EB /* UIVIewController+WMFCommonRotationSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED2E9F91CB2B3B300D1C844 /* UIVIewController+WMFCommonRotationSupport.swift */; }; + D8EC3E1B1E9BDA35006712EB /* (null) in Sources */ = {isa = PBXBuildFile; }; + D8EC3E1D1E9BDA35006712EB /* WMFTitleInsetRespectingButton.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803CB1C0CDC9B0065EBC0 /* WMFTitleInsetRespectingButton.m */; }; + D8EC3E1E1E9BDA35006712EB /* WMFChangePasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE561E4526A40033BD6E /* WMFChangePasswordViewController.swift */; }; + D8EC3E251E9BDA35006712EB /* WMFArticleRevisionFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4DC1C223DCB00B5AFDE /* WMFArticleRevisionFetcher.m */; }; + D8EC3E261E9BDA35006712EB /* WMFArticleLanguagesSectionHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B4CF091CC0500E0051DF7A /* WMFArticleLanguagesSectionHeader.m */; }; + D8EC3E2C1E9BDA35006712EB /* WMFTwoFactorPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0ED17331E4912EB008B70AD /* WMFTwoFactorPasswordViewController.swift */; }; + D8EC3E2E1E9BDA35006712EB /* SavedPagesFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805861C0CE2C60065EBC0 /* SavedPagesFunnel.m */; }; + D8EC3E301E9BDA35006712EB /* WMFChange.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FEECCC1DE3729400B883F0 /* WMFChange.m */; }; + D8EC3E321E9BDA35006712EB /* MWKSearchRedirectMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807CA1C0CF04A0065EBC0 /* MWKSearchRedirectMapping.m */; }; + D8EC3E331E9BDA35006712EB /* MWKImageInfoFetcher+PicOfTheDayInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = BC62FFBF1C11064200533DA9 /* MWKImageInfoFetcher+PicOfTheDayInfo.m */; }; + D8EC3E381E9BDA35006712EB /* WMFScrollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027447C1E6253E200E7B248 /* WMFScrollViewController.swift */; }; + D8EC3E3E1E9BDA35006712EB /* PageHistorySection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03EC1CE0FB4200009083 /* PageHistorySection.swift */; }; + D8EC3E421E9BDA35006712EB /* TableOfContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E8036C1C0CD98B0065EBC0 /* TableOfContentsViewController.swift */; }; + D8EC3E441E9BDA35006712EB /* ProtectedEditAttemptFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805821C0CE2C60065EBC0 /* ProtectedEditAttemptFunnel.m */; }; + D8EC3E451E9BDA35006712EB /* WKWebView+WMFWebViewControllerJavascript.m in Sources */ = {isa = PBXBuildFile; fileRef = B0DF6F801CFE1D0B0046E507 /* WKWebView+WMFWebViewControllerJavascript.m */; }; + D8EC3E491E9BDA35006712EB /* WMFArticleTextActivitySource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EC044781C7917860033D773 /* WMFArticleTextActivitySource.m */; }; + D8EC3E4C1E9BDA35006712EB /* String?+WMFExtras.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01CFC601E71069000B3546A /* String?+WMFExtras.swift */; }; + D8EC3E4F1E9BDA35006712EB /* WMFPasswordResetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0267CF21E32A0CB006B6D8D /* WMFPasswordResetter.swift */; }; + D8EC3E521E9BDA35006712EB /* UIBarButtonItem+WMFButtonConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802B71C0CD2140065EBC0 /* UIBarButtonItem+WMFButtonConvenience.m */; }; + D8EC3E531E9BDA35006712EB /* UIViewController+WMFEmptyView.m in Sources */ = {isa = PBXBuildFile; fileRef = B08E7E9A1C1FA57A00EC3C99 /* UIViewController+WMFEmptyView.m */; }; + D8EC3E5B1E9BDA35006712EB /* NSUserDefaults+WMFApplicationDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87914DC1DFA04E10012C5DA /* NSUserDefaults+WMFApplicationDefaults.swift */; }; + D8EC3E5D1E9BDA35006712EB /* AboutViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806931C0CEA7B0065EBC0 /* AboutViewController.m */; }; + D8EC3E611E9BDA35006712EB /* WMFArticleLanguagesSectionFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = B0866F441CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.m */; }; + D8EC3E631E9BDA35006712EB /* WMFWelcomeContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEDC1DB591C400615FA2 /* WMFWelcomeContainerViewController.swift */; }; + D8EC3E641E9BDA35006712EB /* WMFAccountCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE471E428C940033BD6E /* WMFAccountCreator.swift */; }; + D8EC3E651E9BDA35006712EB /* UIViewController+WMFHideKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B066F0D41E513DAA00A199F8 /* UIViewController+WMFHideKeyboard.swift */; }; + D8EC3E671E9BDA35006712EB /* WeakScriptMessageDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B069FA2D1CEACB8400083D59 /* WeakScriptMessageDelegate.swift */; }; + D8EC3E6D1E9BDA35006712EB /* UIView+Animations.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E294CC1DB2CF4300861D04 /* UIView+Animations.swift */; }; + D8EC3E6E1E9BDA35006712EB /* WMFDeleteBackwardReportingTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01CFC5E1E70B00100B3546A /* WMFDeleteBackwardReportingTextField.swift */; }; + D8EC3E701E9BDA35006712EB /* MWKLanguageLinkFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806621C0CE9030065EBC0 /* MWKLanguageLinkFetcher.m */; }; + D8EC3E741E9BDA35006712EB /* WMFImageTextActivitySource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC0447A1C796FEF0033D773 /* WMFImageTextActivitySource.swift */; }; + D8EC3E7A1E9BDA35006712EB /* ToCInteractionFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805881C0CE2C60065EBC0 /* ToCInteractionFunnel.m */; }; + D8EC3E7D1E9BDA35006712EB /* UIViewController+WMFChildViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027447E1E665D9F00E7B248 /* UIViewController+WMFChildViewController.swift */; }; + D8EC3E7E1E9BDA35006712EB /* WMFCompassView.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8031B1C0CD6820065EBC0 /* WMFCompassView.m */; }; + D8EC3E7F1E9BDA35006712EB /* WMFWelcomeIntroductionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E294D21DB2DC8900861D04 /* WMFWelcomeIntroductionViewController.swift */; }; + D8EC3E851E9BDA35006712EB /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = D896C7951D6F2E4E007101DF /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m */; }; + D8EC3E8A1E9BDA35006712EB /* UIView+WMFFrameUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805201C0CE0DC0065EBC0 /* UIView+WMFFrameUtils.m */; }; + D8EC3E8B1E9BDA35006712EB /* WMFWelcomeAnimationViewControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00DDEE21DB5B16E00615FA2 /* WMFWelcomeAnimationViewControllers.swift */; }; + D8EC3E8C1E9BDA35006712EB /* TableOfContentsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E803721C0CD9C10065EBC0 /* TableOfContentsCell.swift */; }; + D8EC3E8E1E9BDA35006712EB /* WMFSettingsMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = B0B0EC211C6999A9006F0D9C /* WMFSettingsMenuItem.m */; }; + D8EC3E901E9BDA35006712EB /* UIScrollView+ScrollSubviewToLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805091C0CE0DC0065EBC0 /* UIScrollView+ScrollSubviewToLocation.m */; }; + D8EC3E911E9BDA35006712EB /* MTLValueTransformer+WMFNumericValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8062A1C0CE7670065EBC0 /* MTLValueTransformer+WMFNumericValueTransformer.m */; }; + D8EC3E971E9BDA35006712EB /* TableOfContentsHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84692DE1D5E1E3F000A7058 /* TableOfContentsHeader.swift */; }; + D8EC3E9A1E9BDA35006712EB /* UIViewController+WMFScrollToTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE489021D4ADCA00088505C /* UIViewController+WMFScrollToTop.swift */; }; + D8EC3E9C1E9BDA35006712EB /* WMFAuthButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B027FD271E678F5C005644A9 /* WMFAuthButton.swift */; }; + D8EC3EA11E9BDA35006712EB /* UIApplicationShortcutItem+WMFShortcutItem.m in Sources */ = {isa = PBXBuildFile; fileRef = B0EF42CF1C43FDD200D125A8 /* UIApplicationShortcutItem+WMFShortcutItem.m */; }; + D8EC3EA51E9BDA35006712EB /* WMFMapsActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B389CFCD1E6F238300483C06 /* WMFMapsActivity.swift */; }; + D8EC3EA61E9BDA35006712EB /* WMFAuthAccountCreationInfoFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F92C811E3FFEB900B72802 /* WMFAuthAccountCreationInfoFetcher.swift */; }; + D8EC3EA81E9BDA35006712EB /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF5BB6C1C110C2100DE75E1 /* AppDelegate.m */; }; + D8EC3EAA1E9BDA35006712EB /* ArticlePlace.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82972821E3950100061550A /* ArticlePlace.swift */; }; + D8EC3EAC1E9BDA35006712EB /* WMFSearchResults.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E803471C0CD7AA0065EBC0 /* WMFSearchResults.m */; }; + D8EC3EAE1E9BDA35006712EB /* WMFBarButtonItemPopoverMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0DE92291D6E272B00EC76A7 /* WMFBarButtonItemPopoverMessageViewController.m */; }; + D8EC3EB01E9BDA35006712EB /* WMFReferencePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01490A21DB96E5F007F5391 /* WMFReferencePageViewController.swift */; }; + D8EC3EB91E9BDA35006712EB /* WikiTextSectionUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806571C0CE84B0065EBC0 /* WikiTextSectionUploader.m */; }; + D8EC3EBB1E9BDA35006712EB /* WMFAuthLinkLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B010E1A71E723E3600CFE1CD /* WMFAuthLinkLabel.swift */; }; + D8EC3EBE1E9BDA35006712EB /* UIViewController+WMFDynamicHeightPopoverMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B02376B31D6ECCF9007DBA73 /* UIViewController+WMFDynamicHeightPopoverMessage.m */; }; + D8EC3EC41E9BDA35006712EB /* WMFWelcomeAnalyticsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08337601DB17DC5002860D2 /* WMFWelcomeAnalyticsViewController.swift */; }; + D8EC3EC51E9BDA35006712EB /* WMFRevisionQueryResults.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4E41C22429100B5AFDE /* WMFRevisionQueryResults.m */; }; + D8EC3EC61E9BDA35006712EB /* WMFAppViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E802C01C0CD27F0065EBC0 /* WMFAppViewController.m */; }; + D8EC3ECA1E9BDA35006712EB /* UIView+IBExtras.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E805181C0CE0DC0065EBC0 /* UIView+IBExtras.swift */; }; + D8EC3ECC1E9BDA35006712EB /* WMFHamburgerMenuFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8058A1C0CE2C60065EBC0 /* WMFHamburgerMenuFunnel.m */; }; + D8EC3ECD1E9BDA35006712EB /* UIViewController+WMFWelcomeStoryboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B30D01DB817660012281F /* UIViewController+WMFWelcomeStoryboard.swift */; }; + D8EC3ECE1E9BDA35006712EB /* WMFLanguageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EF224991CC5536200FDF78E /* WMFLanguageCell.m */; }; + D8EC3ED11E9BDA35006712EB /* Array+WMFAllFieldsFilled.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04C444A1E56966B00C6DFB0 /* Array+WMFAllFieldsFilled.swift */; }; + D8EC3ED21E9BDA35006712EB /* NewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8940CED1DB56C8A00E17F9E /* NewsViewController.swift */; }; + D8EC3ED31E9BDA35006712EB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8071E1C0CEC8A0065EBC0 /* main.m */; }; + D8EC3ED51E9BDA35006712EB /* WMFDailyStatsLoggingFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DC0941C74F0D700622CBD /* WMFDailyStatsLoggingFunnel.m */; }; + D8EC3ED61E9BDA35006712EB /* WMFRandomDiceButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D84F2BF91D2FEE6300963D42 /* WMFRandomDiceButton.m */; }; + D8EC3EDD1E9BDA35006712EB /* WMFFirstRandomViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D84F2C021D30162700963D42 /* WMFFirstRandomViewController.m */; }; + D8EC3EDF1E9BDA35006712EB /* ArticlePopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82972861E3A49980061550A /* ArticlePopoverViewController.swift */; }; + D8EC3EE01E9BDA35006712EB /* WMFLogFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807351C0CED810065EBC0 /* WMFLogFormatter.m */; }; + D8EC3EE11E9BDA35006712EB /* RoundedCornerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CB32AC1E79D8A0008A0966 /* RoundedCornerView.swift */; }; + D8EC3EE41E9BDA35006712EB /* NSString+FormattedAttributedString.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804A71C0CE0B40065EBC0 /* NSString+FormattedAttributedString.m */; }; + D8EC3EE91E9BDA35006712EB /* WMFForgotPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0267CEC1E316AE3006B6D8D /* WMFForgotPasswordViewController.swift */; }; + D8EC3EEB1E9BDA35006712EB /* WMFImageGalleryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E4A34711CBBFCD400A400F6 /* WMFImageGalleryViewController.m */; }; + D8EC3EEF1E9BDA35006712EB /* WMFAccountCreationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE411E413B3F0033BD6E /* WMFAccountCreationViewController.swift */; }; + D8EC3EF21E9BDA35006712EB /* NSAttributedString+WMFModify.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804841C0CE0B40065EBC0 /* NSAttributedString+WMFModify.m */; }; + D8EC3EF31E9BDA35006712EB /* WMFSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E806C11C0CEB380065EBC0 /* WMFSettingsViewController.m */; }; + D8EC3EF51E9BDA35006712EB /* UIScrollView+WMFContentOffsetUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8050B1C0CE0DC0065EBC0 /* UIScrollView+WMFContentOffsetUtils.m */; }; + D8EC3EF71E9BDA35006712EB /* WMFImageGalleryDetailOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E9B9E301CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.m */; }; + D8EC3EFA1E9BDA35006712EB /* UIApplication+SystemSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81E5F871E5F2C8400E1A80C /* UIApplication+SystemSettings.swift */; }; + D8EC3EFD1E9BDA35006712EB /* WMFArticleRevision.m in Sources */ = {isa = PBXBuildFile; fileRef = BC23E4E11C223FAE00B5AFDE /* WMFArticleRevision.m */; }; + D8EC3EFE1E9BDA35006712EB /* ArticlePlaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6BAEE1E4C9C0700A981C8 /* ArticlePlaceView.swift */; }; + D8EC3EFF1E9BDA35006712EB /* WMFCaptchaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE491E42D19D0033BD6E /* WMFCaptchaViewController.swift */; }; + D8EC3F001E9BDA35006712EB /* DDLog+WMFLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804741C0CE0B40065EBC0 /* DDLog+WMFLogger.m */; }; + D8EC3F021E9BDA35006712EB /* WMFLegacyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E281A321DC263DE00FA1AB1 /* WMFLegacyReference.swift */; }; + D8EC3F031E9BDA35006712EB /* WMFWelcomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083371D1DADB251002860D2 /* WMFWelcomePageViewController.swift */; }; + D8EC3F041E9BDA35006712EB /* MKCoordinateRegion+Dimensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D808DCEE1E438C5100A3E89C /* MKCoordinateRegion+Dimensions.swift */; }; + D8EC3F061E9BDA35006712EB /* WMFAlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBCA7471C176389004F1FD9 /* WMFAlertManager.swift */; }; + D8EC3F071E9BDA35006712EB /* WMFWelcomeLanguageTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B083375E1DB17DA5002860D2 /* WMFWelcomeLanguageTableViewController.swift */; }; + D8EC3F091E9BDA35006712EB /* WMFLoginFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805801C0CE2C60065EBC0 /* WMFLoginFunnel.m */; }; + D8EC3F0B1E9BDA35006712EB /* MapUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88FCAE01E4B776600505A9F /* MapUtilities.swift */; }; + D8EC3F0D1E9BDA35006712EB /* WKWebView+ElementLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8052E1C0CE0DC0065EBC0 /* WKWebView+ElementLocation.m */; }; + D8EC3F0F1E9BDA35006712EB /* UIViewController+WMFStoryboardUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B30CE1DB813760012281F /* UIViewController+WMFStoryboardUtilities.swift */; }; + D8EC3F111E9BDA35006712EB /* TableOfContentsPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E8036E1C0CD99A0065EBC0 /* TableOfContentsPresentationController.swift */; }; + D8EC3F141E9BDA35006712EB /* WMFLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6BE3F1E4068C60033BD6E /* WMFLoginViewController.swift */; }; + D8EC3F181E9BDA35006712EB /* PageHistoryFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09B03F11CE0FB6300009083 /* PageHistoryFetcher.swift */; }; + D8EC3F1A1E9BDA35006712EB /* CreateAccountFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805781C0CE2C60065EBC0 /* CreateAccountFunnel.m */; }; + D8EC3F1C1E9BDA35006712EB /* WMFShareFunnel.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8059C1C0CE2F50065EBC0 /* WMFShareFunnel.m */; }; + D8EC3F241E9BDA35006712EB /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8D553611DF1B63200B90177 /* QuartzCore.framework */; settings = {ATTRIBUTES = (Required, ); }; }; + D8EC3F2D1E9BDA35006712EB /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 040E5C4E184566F4007AFE6F /* CoreData.framework */; }; + D8EC3F2E1E9BDA35006712EB /* WMF.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; }; + D8EC3F301E9BDA35006712EB /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D499143A181D51DE00E6073C /* CoreGraphics.framework */; }; + D8EC3F341E9BDA35006712EB /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D499143C181D51DE00E6073C /* UIKit.framework */; }; + D8EC3F361E9BDA35006712EB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4991438181D51DE00E6073C /* Foundation.framework */; }; + D8EC3F371E9BDA35006712EB /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 041EFC361996A1F800B2CB28 /* MapKit.framework */; }; + D8EC3F3D1E9BDA35006712EB /* WMFCaptchaViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E803FD1C0CDE480065EBC0 /* WMFCaptchaViewController.storyboard */; }; + D8EC3F421E9BDA35006712EB /* AboutViewController.plist in Resources */ = {isa = PBXBuildFile; fileRef = B0E806941C0CEA7B0065EBC0 /* AboutViewController.plist */; }; + D8EC3F431E9BDA35006712EB /* NotificationBackgroundMessage.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4D1DF22E1700B95311 /* NotificationBackgroundMessage.png */; }; + D8EC3F471E9BDA35006712EB /* WMFRandomDiceButtonRoll.js in Resources */ = {isa = PBXBuildFile; fileRef = D84F2BFA1D2FEE6300963D42 /* WMFRandomDiceButtonRoll.js */; }; + D8EC3F491E9BDA35006712EB /* NotificationBackgroundSuccess.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4F1DF22E1700B95311 /* NotificationBackgroundSuccess.png */; }; + D8EC3F4A1E9BDA35006712EB /* NotificationBackgroundErrorIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4C1DF22E1700B95311 /* NotificationBackgroundErrorIcon@2x.png */; }; + D8EC3F4C1E9BDA35006712EB /* WMFArticleLanguagesSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0866F451CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.xib */; }; + D8EC3F4D1E9BDA35006712EB /* NotificationBackgroundErrorIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4B1DF22E1700B95311 /* NotificationBackgroundErrorIcon.png */; }; + D8EC3F4E1E9BDA35006712EB /* WMFLanguageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E4D071C1CC5526200AE968B /* WMFLanguageCell.xib */; }; + D8EC3F531E9BDA35006712EB /* NotificationBackgroundSuccessIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B521DF22E1700B95311 /* NotificationBackgroundSuccessIcon@2x.png */; }; + D8EC3F541E9BDA35006712EB /* WMFTwoFactorPasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0ED173C1E49831B008B70AD /* WMFTwoFactorPasswordViewController.storyboard */; }; + D8EC3F581E9BDA35006712EB /* TableOfContentsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0E803731C0CD9C10065EBC0 /* TableOfContentsCell.xib */; }; + D8EC3F591E9BDA35006712EB /* WMFReferencePopoverMessageViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0379A361D8B9D2400D973CF /* WMFReferencePopoverMessageViewController.storyboard */; }; + D8EC3F5E1E9BDA35006712EB /* WMFEmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0EF8634D1C19E02700006D2D /* WMFEmptyView.xib */; }; + D8EC3F5F1E9BDA35006712EB /* NotificationBackgroundMessage@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4E1DF22E1700B95311 /* NotificationBackgroundMessage@2x.png */; }; + D8EC3F601E9BDA35006712EB /* NotificationBackgroundWarningIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B551DF22E1700B95311 /* NotificationBackgroundWarningIcon.png */; }; + D8EC3F611E9BDA35006712EB /* WMFLoginViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804001C0CDE480065EBC0 /* WMFLoginViewController.storyboard */; }; + D8EC3F631E9BDA35006712EB /* WMFWelcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E26B0741C0F4F720004D687 /* WMFWelcome.storyboard */; }; + D8EC3F6B1E9BDA35006712EB /* NotificationBackgroundWarning@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B541DF22E1700B95311 /* NotificationBackgroundWarning@2x.png */; }; + D8EC3F6C1E9BDA35006712EB /* NotificationBackgroundSuccess@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B501DF22E1700B95311 /* NotificationBackgroundSuccess@2x.png */; }; + D8EC3F6D1E9BDA35006712EB /* WMFChangePasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0C6BE521E4526810033BD6E /* WMFChangePasswordViewController.storyboard */; }; + D8EC3F6F1E9BDA35006712EB /* NotificationBackgroundError.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B491DF22E1700B95311 /* NotificationBackgroundError.png */; }; + D8EC3F701E9BDA35006712EB /* WMFReferencePanels.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B01490A01DB96BD6007F5391 /* WMFReferencePanels.storyboard */; }; + D8EC3F711E9BDA35006712EB /* Places.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D87234031E1FF18100751E83 /* Places.storyboard */; }; + D8EC3F731E9BDA35006712EB /* WMFAccountCreationViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E803FC1C0CDE480065EBC0 /* WMFAccountCreationViewController.storyboard */; }; + D8EC3F741E9BDA35006712EB /* NotificationBackgroundSuccessIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B511DF22E1700B95311 /* NotificationBackgroundSuccessIcon.png */; }; + D8EC3F751E9BDA35006712EB /* WMFSettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B02B82741C696ECA00B19309 /* WMFSettingsTableViewCell.xib */; }; + D8EC3F781E9BDA35006712EB /* NotificationBackgroundWarningIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B561DF22E1700B95311 /* NotificationBackgroundWarningIcon@2x.png */; }; + D8EC3F7B1E9BDA35006712EB /* TableOfContentsHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = D84692DF1D5E1E3F000A7058 /* TableOfContentsHeader.xib */; }; + D8EC3F7E1E9BDA35006712EB /* WMFBarButtonItemPopoverMessageViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0DE92261D6E26C700EC76A7 /* WMFBarButtonItemPopoverMessageViewController.storyboard */; }; + D8EC3F801E9BDA35006712EB /* TSMessagesDefaultDesign.json in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B591DF22E1700B95311 /* TSMessagesDefaultDesign.json */; }; + D8EC3F811E9BDA35006712EB /* WMFArticleLanguagesSectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = B0B4CF0B1CC0501B0051DF7A /* WMFArticleLanguagesSectionHeader.xib */; }; + D8EC3F831E9BDA35006712EB /* ArticlePopoverViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D82972871E3A49980061550A /* ArticlePopoverViewController.xib */; }; + D8EC3F841E9BDA35006712EB /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E69CD5A1C8773410095918B /* Launch Screen.storyboard */; }; + D8EC3F851E9BDA35006712EB /* EditSaveViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804031C0CDE480065EBC0 /* EditSaveViewController.storyboard */; }; + D8EC3F861E9BDA35006712EB /* NotificationBackgroundWarning.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B531DF22E1700B95311 /* NotificationBackgroundWarning.png */; }; + D8EC3F881E9BDA35006712EB /* NotificationBackgroundError@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B4A1DF22E1700B95311 /* NotificationBackgroundError@2x.png */; }; + D8EC3F8B1E9BDA35006712EB /* WMFImageGalleryDetailOverlayView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E9B9E2F1CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.xib */; }; + D8EC3F8F1E9BDA35006712EB /* WMFRandomDiceButton.html in Resources */ = {isa = PBXBuildFile; fileRef = D84F2BF81D2FEE6300963D42 /* WMFRandomDiceButton.html */; }; + D8EC3F941E9BDA35006712EB /* WMFForgotPasswordViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0267CE81E316A79006B6D8D /* WMFForgotPasswordViewController.storyboard */; }; + D8EC3F971E9BDA35006712EB /* WMFTableHeaderFooterLabelView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0EE2438E1DC988AA00066CBD /* WMFTableHeaderFooterLabelView.xib */; }; + D8EC3F9A1E9BDA35006712EB /* NotificationButtonBackground.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B571DF22E1700B95311 /* NotificationButtonBackground.png */; }; + D8EC3F9C1E9BDA35006712EB /* WMFSettingsViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B0E804091C0CDE480065EBC0 /* WMFSettingsViewController.storyboard */; }; + D8EC3F9D1E9BDA35006712EB /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D4991453181D51DE00E6073C /* Images.xcassets */; }; + D8EC3F9E1E9BDA35006712EB /* ReadingThemesControlsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B09B03F41CE0FB7700009083 /* ReadingThemesControlsViewController.xib */; }; + D8EC3F9F1E9BDA35006712EB /* NotificationButtonBackground@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D8D92B581DF22E1700B95311 /* NotificationButtonBackground@2x.png */; }; + D8EC3FA31E9BDA35006712EB /* ContinueReadingWidget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0E8380631D64989F0076EDE4 /* ContinueReadingWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + D8EC3FA51E9BDA35006712EB /* WMF.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8EC64031D007B1F00C286EE /* WMFLinkParsingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D8EC64021D007B1F00C286EE /* WMFLinkParsingTests.m */; }; + D8EEA0F11D6DF60600D88143 /* WMFTodayContinueReadingWidgetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85219371D6DEFBB00084796 /* WMFTodayContinueReadingWidgetViewController.swift */; }; + D8F36EFD1EEAEAF20087D4DD /* WMFQuoteMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D8F36EFC1EEAEAF20087D4DD /* WMFQuoteMacros.h */; }; + D8F36F031EEEBA130087D4DD /* Licenses.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F36F021EEEBA130087D4DD /* Licenses.swift */; }; + D8FA18AA1E1BD867009675C3 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D8D551431DF1A33D00B90177 /* metamacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18AB1E1BD86E009675C3 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = D8D551411DF1A33D00B90177 /* EXTScope.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18AC1E1BD874009675C3 /* EXTScope.m in Sources */ = {isa = PBXBuildFile; fileRef = D8D551421DF1A33D00B90177 /* EXTScope.m */; }; + D8FA18AD1E1BD891009675C3 /* NSDictionary+WMFPageViewsSortedByDate.h in Headers */ = {isa = PBXBuildFile; fileRef = D84F92401DC161DA00114C2F /* NSDictionary+WMFPageViewsSortedByDate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18AE1E1BD891009675C3 /* NSDictionary+WMFPageViewsSortedByDate.m in Sources */ = {isa = PBXBuildFile; fileRef = D84F92411DC161DA00114C2F /* NSDictionary+WMFPageViewsSortedByDate.m */; }; + D8FA18AF1E1BD891009675C3 /* NSNumberFormatter+WMFExtras.swift in Sources */ = {isa = PBXBuildFile; fileRef = D864D68D1DA3EDF900B86934 /* NSNumberFormatter+WMFExtras.swift */; }; + D8FA18B01E1BD891009675C3 /* NSBundle+WMFInfoUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804891C0CE0B40065EBC0 /* NSBundle+WMFInfoUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18B11E1BD891009675C3 /* NSBundle+WMFInfoUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8048A1C0CE0B40065EBC0 /* NSBundle+WMFInfoUtils.m */; }; + D8FA18B21E1BD891009675C3 /* WMFNumberOfExtractCharacters.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E803961C0CDB150065EBC0 /* WMFNumberOfExtractCharacters.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18B31E1BD891009675C3 /* NSCalendar+WMFCommonCalendars.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD31FFC1C6EB3EE00317D08 /* NSCalendar+WMFCommonCalendars.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18B41E1BD891009675C3 /* NSCalendar+WMFCommonCalendars.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD31FFD1C6EB3EE00317D08 /* NSCalendar+WMFCommonCalendars.m */; }; + D8FA18B51E1BD891009675C3 /* NSDictionary+WMFRequiredValueForKey.h in Headers */ = {isa = PBXBuildFile; fileRef = BCAF23141C6CF74C005F2D8D /* NSDictionary+WMFRequiredValueForKey.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18B61E1BD891009675C3 /* NSDictionary+WMFRequiredValueForKey.m in Sources */ = {isa = PBXBuildFile; fileRef = BCAF23151C6CF74C005F2D8D /* NSDictionary+WMFRequiredValueForKey.m */; }; + D8FA18B71E1BD891009675C3 /* NSDateFormatter+WMFExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8048F1C0CE0B40065EBC0 /* NSDateFormatter+WMFExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18B81E1BD891009675C3 /* NSDateFormatter+WMFExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804901C0CE0B40065EBC0 /* NSDateFormatter+WMFExtensions.m */; }; + D8FA18B91E1BD891009675C3 /* NSDate+WMFRelativeDate.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E78419A1C7CF5CE004F9D36 /* NSDate+WMFRelativeDate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18BA1E1BD891009675C3 /* NSDate+WMFRelativeDate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E78419B1C7CF5CE004F9D36 /* NSDate+WMFRelativeDate.m */; }; + D8FA18BB1E1BD891009675C3 /* NSFileManager+WMFGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = D8EEA0F81D6E21A400D88143 /* NSFileManager+WMFGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18BC1E1BD891009675C3 /* NSFileManager+WMFGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = D8EEA0F91D6E21A400D88143 /* NSFileManager+WMFGroup.m */; }; + D8FA18BD1E1BD891009675C3 /* NSFileManager+WMFExtendedFileAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = D81082F61D0983AE0070DAC3 /* NSFileManager+WMFExtendedFileAttributes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18BE1E1BD891009675C3 /* NSFileManager+WMFExtendedFileAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = D81082F71D0983AE0070DAC3 /* NSFileManager+WMFExtendedFileAttributes.m */; }; + D8FA18C11E1BD891009675C3 /* NSURL+WMFQueryParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = B01662AE1D1B8997006F4544 /* NSURL+WMFQueryParameters.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18C21E1BD891009675C3 /* NSURL+WMFQueryParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = B01662AF1D1B8997006F4544 /* NSURL+WMFQueryParameters.m */; }; + D8FA18C61E1BD891009675C3 /* NSIndexSet+BKReduce.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804951C0CE0B40065EBC0 /* NSIndexSet+BKReduce.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18C71E1BD891009675C3 /* NSIndexSet+BKReduce.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804961C0CE0B40065EBC0 /* NSIndexSet+BKReduce.m */; }; + D8FA18C81E1BD891009675C3 /* WMFGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804271C0CDF510065EBC0 /* WMFGeometry.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18C91E1BD891009675C3 /* WMFGeometry.c in Sources */ = {isa = PBXBuildFile; fileRef = B0E804261C0CDF510065EBC0 /* WMFGeometry.c */; }; + D8FA18CA1E1BD891009675C3 /* WMFRangeUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8073A1C0CED810065EBC0 /* WMFRangeUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18CB1E1BD891009675C3 /* WMFLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807361C0CED810065EBC0 /* WMFLogging.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18CD1E1BD891009675C3 /* WMFOutParamUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807391C0CED810065EBC0 /* WMFOutParamUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18D01E1BD891009675C3 /* WMFMath.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807371C0CED810065EBC0 /* WMFMath.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18D11E1BD891009675C3 /* WMFMath.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807381C0CED810065EBC0 /* WMFMath.m */; }; + D8FA18D21E1BD891009675C3 /* WMFTaskGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = D8A76D811D6F2B2E00E5A798 /* WMFTaskGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18D31E1BD891009675C3 /* WMFTaskGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = D8A76D821D6F2B2E00E5A798 /* WMFTaskGroup.m */; }; + D8FA18D41E1BD891009675C3 /* NSError+WMFExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804911C0CE0B40065EBC0 /* NSError+WMFExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18D51E1BD891009675C3 /* NSError+WMFExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804921C0CE0B40065EBC0 /* NSError+WMFExtensions.m */; }; + D8FA18D61E1BD899009675C3 /* NSURL+WMFExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804AE1C0CE0B40065EBC0 /* NSURL+WMFExtras.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18D71E1BD899009675C3 /* NSURL+WMFExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804AF1C0CE0B40065EBC0 /* NSURL+WMFExtras.m */; }; + D8FA18D81E1BD899009675C3 /* NSURL+WMFLinkParsing.h in Headers */ = {isa = PBXBuildFile; fileRef = D8494AD81D6C85C500337433 /* NSURL+WMFLinkParsing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18D91E1BD899009675C3 /* NSURL+WMFLinkParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D8494AD91D6C85C500337433 /* NSURL+WMFLinkParsing.m */; }; + D8FA18DA1E1BD899009675C3 /* NSURLComponents+WMFLinkParsing.h in Headers */ = {isa = PBXBuildFile; fileRef = D8494ADA1D6C85C500337433 /* NSURLComponents+WMFLinkParsing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18DB1E1BD899009675C3 /* NSURLComponents+WMFLinkParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = D8494ADB1D6C85C500337433 /* NSURLComponents+WMFLinkParsing.m */; }; + D8FA18DC1E1BD89C009675C3 /* WMFDeprecationMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E805761C0CE24B0065EBC0 /* WMFDeprecationMacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18E71E1BD8AF009675C3 /* NSProcessInfo+WMFOperatingSystemVersionChecks.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804A31C0CE0B40065EBC0 /* NSProcessInfo+WMFOperatingSystemVersionChecks.m */; }; + D8FA18E81E1BD8B2009675C3 /* NSProcessInfo+WMFOperatingSystemVersionChecks.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804A21C0CE0B40065EBC0 /* NSProcessInfo+WMFOperatingSystemVersionChecks.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18E91E1BD8B9009675C3 /* WMFComparison.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8072F1C0CED810065EBC0 /* WMFComparison.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18EA1E1BD8B9009675C3 /* WMFHashing.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807311C0CED810065EBC0 /* WMFHashing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18EB1E1BD8C0009675C3 /* WMFGCDHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807301C0CED810065EBC0 /* WMFGCDHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18EC1E1BD8C4009675C3 /* WMFBlockDefinitions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0EF5BB661C110BFC00DE75E1 /* WMFBlockDefinitions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA18ED1E1BDA2F009675C3 /* UIImage+WMFImageProcessing.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804FA1C0CE0DC0065EBC0 /* UIImage+WMFImageProcessing.h */; }; + D8FA18EE1E1BDA2F009675C3 /* UIImageView+WMFImageFetchingInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E807751C0CEEB00065EBC0 /* UIImageView+WMFImageFetchingInternal.h */; }; + D8FA18EF1E1BDA2F009675C3 /* UIImageView+WMFContentOffset.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E805021C0CE0DC0065EBC0 /* UIImageView+WMFContentOffset.h */; }; + D8FA18F01E1BDA2F009675C3 /* UIImage+WMFNormalization.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804FC1C0CE0DC0065EBC0 /* UIImage+WMFNormalization.h */; }; + D8FA18F11E1BDA35009675C3 /* UIImage+WMFImageProcessing.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804FB1C0CE0DC0065EBC0 /* UIImage+WMFImageProcessing.m */; }; + D8FA18F21E1BDA35009675C3 /* UIImageView+WMFImageFetchingInternal.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E807761C0CEEB00065EBC0 /* UIImageView+WMFImageFetchingInternal.m */; }; + D8FA18F31E1BDA35009675C3 /* UIImageView+WMFContentOffset.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805031C0CE0DC0065EBC0 /* UIImageView+WMFContentOffset.m */; }; + D8FA18F41E1BDA35009675C3 /* UIImage+WMFNormalization.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804FD1C0CE0DC0065EBC0 /* UIImage+WMFNormalization.m */; }; + D8FA18F51E1BDA3C009675C3 /* WMFSparklineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FA39B71D7F556400D89889 /* WMFSparklineView.swift */; }; + D8FA18F61E1BDA3F009675C3 /* UIFont+WMFDynamicType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D0CC361DF6F8C30031EDD9 /* UIFont+WMFDynamicType.swift */; }; + D8FA18F71E1BDA4C009675C3 /* GroupedAccessibilityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D803F8951DC53B0C00656F20 /* GroupedAccessibilityView.swift */; }; + D8FA18F81E1BDA4C009675C3 /* WMFArticlePreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D270391D75ED5000D093A8 /* WMFArticlePreviewViewController.swift */; }; + D8FA18F91E1BDA4C009675C3 /* UIView+WMFDefaultNib.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8051E1C0CE0DC0065EBC0 /* UIView+WMFDefaultNib.m */; }; + D8FA18FB1E1BDA4C009675C3 /* UIImage+WMFStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805011C0CE0DC0065EBC0 /* UIImage+WMFStyle.m */; }; + D8FA18FD1E1BDA4C009675C3 /* UIImageView+WMFImageFetching.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E805D61C0CE59B0065EBC0 /* UIImageView+WMFImageFetching.m */; }; + D8FA18FE1E1BDA4C009675C3 /* UIColor+WMFStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E804F31C0CE0DC0065EBC0 /* UIColor+WMFStyle.m */; }; + D8FA19011E1BDA5B009675C3 /* UIView+WMFDefaultNib.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E8051D1C0CE0DC0065EBC0 /* UIView+WMFDefaultNib.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA19031E1BDA66009675C3 /* UIImage+WMFStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E805001C0CE0DC0065EBC0 /* UIImage+WMFStyle.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA19051E1BDA66009675C3 /* UIImageView+WMFImageFetching.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E805D51C0CE59B0065EBC0 /* UIImageView+WMFImageFetching.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA19061E1BDA66009675C3 /* UIColor+WMFStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = B0E804F21C0CE0DC0065EBC0 /* UIColor+WMFStyle.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8FA190A1E1BDB79009675C3 /* WMF.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; }; + D8FA19111E1BDFC8009675C3 /* WMF.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; }; + D8FA19161E1BE069009675C3 /* WMFArticlePreviewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8D2703A1D75ED5000D093A8 /* WMFArticlePreviewViewController.xib */; }; + D8FAC7DD1D6F88AB00C2A6BC /* WMF.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D844D96C1D6CB2600042D692 /* WMF.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8FEECCD1DE3729400B883F0 /* WMFChange.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FEECCC1DE3729400B883F0 /* WMFChange.m */; }; + E1CFD6E6210C103900D8E37C /* ExploreCardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83CA612920D1675800EF0C4A /* ExploreCardViewController.swift */; }; + EB8237532970AE6A00FD629E /* WMFItemSourceExcludingActivityTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB8237522970AE6A00FD629E /* WMFItemSourceExcludingActivityTypes.swift */; }; + FF2090F02500247100849774 /* ThreeLineHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2090EE2500247100849774 /* ThreeLineHeaderView.swift */; }; + FF2B2110254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B210F254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift */; }; + FF2B2111254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B210F254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift */; }; + FF2B2112254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B210F254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift */; }; + FF2B2113254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B210F254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift */; }; + FF5555642771388F00925099 /* CollectionViewContextMenuShowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5555632771388F00925099 /* CollectionViewContextMenuShowing.swift */; }; + FF555565277287F300925099 /* CollectionViewContextMenuShowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5555632771388F00925099 /* CollectionViewContextMenuShowing.swift */; }; + FF555566277287F400925099 /* CollectionViewContextMenuShowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5555632771388F00925099 /* CollectionViewContextMenuShowing.swift */; }; + FF555567277287F500925099 /* CollectionViewContextMenuShowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5555632771388F00925099 /* CollectionViewContextMenuShowing.swift */; }; + FF59DF4D2555E0CB0048E66C /* InternalLinkPreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59DF4C2555E0CB0048E66C /* InternalLinkPreviewing.swift */; }; + FF59DF4E2555E0CB0048E66C /* InternalLinkPreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59DF4C2555E0CB0048E66C /* InternalLinkPreviewing.swift */; }; + FF59DF4F2555E0CB0048E66C /* InternalLinkPreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59DF4C2555E0CB0048E66C /* InternalLinkPreviewing.swift */; }; + FF59DF502555E0CB0048E66C /* InternalLinkPreviewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF59DF4C2555E0CB0048E66C /* InternalLinkPreviewing.swift */; }; + FF921857252E8F4F00C39A8F /* ThanksGiving.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF921856252E8F4F00C39A8F /* ThanksGiving.swift */; }; + FF92187C252F7EA300C39A8F /* ThanksGiving.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF921856252E8F4F00C39A8F /* ThanksGiving.swift */; }; + FF921887252F7EA500C39A8F /* ThanksGiving.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF921856252E8F4F00C39A8F /* ThanksGiving.swift */; }; + FF921888252F7EA500C39A8F /* ThanksGiving.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF921856252E8F4F00C39A8F /* ThanksGiving.swift */; }; + FF9416D824E203030070FEE7 /* OnThisDayWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9416D724E203030070FEE7 /* OnThisDayWidget.swift */; }; + FF9416DE24E2098C0070FEE7 /* OnThisDayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9416DD24E2098C0070FEE7 /* OnThisDayView.swift */; }; + FFA0641925A943EB00B9460B /* BasicLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA0641825A943EB00B9460B /* BasicLogger.swift */; }; + FFA0641A25A943EB00B9460B /* BasicLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA0641825A943EB00B9460B /* BasicLogger.swift */; }; + FFA0641B25A943EB00B9460B /* BasicLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA0641825A943EB00B9460B /* BasicLogger.swift */; }; + FFA0641C25A943EB00B9460B /* BasicLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA0641825A943EB00B9460B /* BasicLogger.swift */; }; + FFBA8C1927D824D8009E9B65 /* URL+ExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFBA8C1827D824D8009E9B65 /* URL+ExtensionTests.swift */; }; + FFD7B84624AEAB3F005C2471 /* ArticleScrolling.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B84524AEAB3F005C2471 /* ArticleScrolling.swift */; }; + FFD7B84724AEB049005C2471 /* ArticleScrolling.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B84524AEAB3F005C2471 /* ArticleScrolling.swift */; }; + FFD7B84824AEB04A005C2471 /* ArticleScrolling.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B84524AEAB3F005C2471 /* ArticleScrolling.swift */; }; + FFD7B84924AEB04A005C2471 /* ArticleScrolling.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B84524AEAB3F005C2471 /* ArticleScrolling.swift */; }; + FFD7B85624B3B384005C2471 /* ReferenceBackLinksViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B85524B3B384005C2471 /* ReferenceBackLinksViewControllerDelegate.swift */; }; + FFD7B85724B3B39A005C2471 /* ReferenceBackLinksViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B85524B3B384005C2471 /* ReferenceBackLinksViewControllerDelegate.swift */; }; + FFD7B85924B3CA7A005C2471 /* ReferenceShowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B85824B3CA7A005C2471 /* ReferenceShowing.swift */; }; + FFD7B85A24B3CA9F005C2471 /* ReferenceShowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B85824B3CA7A005C2471 /* ReferenceShowing.swift */; }; + FFD7B85B24B3CAA0005C2471 /* ReferenceShowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B85824B3CA7A005C2471 /* ReferenceShowing.swift */; }; + FFD7B85C24B3CAA0005C2471 /* ReferenceShowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFD7B85824B3CA7A005C2471 /* ReferenceShowing.swift */; }; + FFE891462445150B0058B642 /* AppTabBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE891452445150B0058B642 /* AppTabBarDelegate.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 00021DEC24D48EFE00476F97 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 00021DE024D48EFD00476F97; + remoteInfo = WidgetsExtension; + }; + 00AB75BF24D4E8FB0041056A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = D844D96B1D6CB2600042D692; + remoteInfo = WMF; + }; + 0E83806E1D64989F0076EDE4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0E8380621D64989F0076EDE4; + remoteInfo = ContinueReadingWidget; + }; + 676C864926D40AEB00A704C1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 676C864326D40AEA00A704C1; + remoteInfo = NotificationServiceExtension; + }; + 676C867126D416FB00A704C1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 676C864326D40AEA00A704C1; + remoteInfo = NotificationServiceExtension; + }; + 676C867426D4170100A704C1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 676C864326D40AEA00A704C1; + remoteInfo = NotificationServiceExtension; + }; + 676C868326D4545300A704C1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = D844D96B1D6CB2600042D692; + remoteInfo = WMF; + }; + B018501920BC85E400A508F1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = D844D96B1D6CB2600042D692; + remoteInfo = WMF; + }; + B0606EB320AA6FF0006EC6B9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = D4991434181D51DE00E6073C; + remoteInfo = Wikipedia; + }; + BCBDE0AD1AA76F19006BD29A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = D4991434181D51DE00E6073C; + remoteInfo = Wikipedia; + }; + D8479FB01F222FE90025FD7A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = D8479FAA1F222FE80025FD7A; + remoteInfo = "Wikipedia Stickers"; + }; + D88DBBBA1D8B322400134A50 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = D844D96B1D6CB2600042D692; + remoteInfo = WMF; + }; + D8A42A4F1E815A9C00D8E281 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = D844D96B1D6CB2600042D692; + remoteInfo = WMF; + }; + D8CE24D91E698E2400DAE2E0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = D844D96B1D6CB2600042D692; + remoteInfo = WMF; + }; + D8CE24DB1E698E2400DAE2E0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0E8380621D64989F0076EDE4; + remoteInfo = ContinueReadingWidget; + }; + D8EC3DD01E9BDA35006712EB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = D844D96B1D6CB2600042D692; + remoteInfo = WMF; + }; + D8EC3DD21E9BDA35006712EB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0E8380621D64989F0076EDE4; + remoteInfo = ContinueReadingWidget; + }; + D8FA19121E1BDFD7009675C3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D499142D181D51DE00E6073C /* Project object */; + proxyType = 1; + remoteGlobalIDString = D844D96B1D6CB2600042D692; + remoteInfo = WMF; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 00AB75C124D4E8FB0041056A /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 0E8380771D64989F0076EDE4 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 00021DEE24D48EFE00476F97 /* WidgetsExtension.appex in Embed Foundation Extensions */, + 676C864B26D40AEB00A704C1 /* NotificationServiceExtension.appex in Embed Foundation Extensions */, + 0E8380701D64989F0076EDE4 /* ContinueReadingWidget.appex in Embed Foundation Extensions */, + D8479FB21F222FE90025FD7A /* Wikipedia Stickers.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + B018501720BC847A00A508F1 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + B018501820BC84E300A508F1 /* WMF.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D84581101D67572D00B09640 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + D8FAC7DD1D6F88AB00C2A6BC /* WMF.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + D870215E1EBA63EE000D02D6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + D8A42C1D1E815A9C00D8E281 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + D8A42C1E1E815A9C00D8E281 /* WMF.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + D8B589A321CD05070027083A /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + D8CE26A01E698E2400DAE2E0 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 676C867326D4170100A704C1 /* NotificationServiceExtension.appex in Embed Foundation Extensions */, + D8CE26A31E698E2400DAE2E0 /* ContinueReadingWidget.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + D8CE26A41E698E2400DAE2E0 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + D8CE26A51E698E2400DAE2E0 /* WMF.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + D8EC3FA01E9BDA35006712EB /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 676C867026D416FB00A704C1 /* NotificationServiceExtension.appex in Embed Foundation Extensions */, + D8EC3FA31E9BDA35006712EB /* ContinueReadingWidget.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + D8EC3FA41E9BDA35006712EB /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + D8EC3FA51E9BDA35006712EB /* WMF.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 00021DE124D48EFD00476F97 /* WidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 00021DE224D48EFD00476F97 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + 00021DE424D48EFD00476F97 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + 00021DE724D48EFD00476F97 /* Widgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Widgets.swift; sourceTree = ""; usesTabs = 0; }; + 00021DE924D48EFE00476F97 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 00021DEB24D48EFE00476F97 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 00021E0324D4A42A00476F97 /* PictureOfTheDayWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PictureOfTheDayWidget.swift; sourceTree = ""; usesTabs = 0; }; + 0010F93827A49C7700D77848 /* HorizontalSpacerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HorizontalSpacerView.swift; sourceTree = ""; }; + 0015712B27D92F6B00F1EB26 /* RetryBlockTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryBlockTask.swift; sourceTree = ""; }; + 0022DD2825829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScribbleIgnoringInteractionDelegate.swift; sourceTree = ""; usesTabs = 0; }; + 002AB86F250BEFBE00ADAC87 /* PictureOfTheDayWidget+LocalizedStrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PictureOfTheDayWidget+LocalizedStrings.swift"; sourceTree = ""; }; + 0030592527DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterOnboardingHostingViewController.swift; sourceTree = ""; }; + 0033D79724F818EB00CAB5B3 /* TopReadWidget+LocalizedStrings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TopReadWidget+LocalizedStrings.swift"; sourceTree = ""; }; + 0033D79824F818EC00CAB5B3 /* TopReadWidget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopReadWidget.swift; sourceTree = ""; usesTabs = 0; }; + 0033D79B24F8193900CAB5B3 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = ""; }; + 0033D79C24F8193900CAB5B3 /* CGPoint+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGPoint+Extensions.swift"; sourceTree = ""; }; + 0033D7A024F8199300CAB5B3 /* Sparkline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sparkline.swift; sourceTree = ""; }; + 0036C8B2282C2AAA00EADB35 /* Notification+NotificationsCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+NotificationsCenter.swift"; sourceTree = ""; }; + 003AD72D2979C512005BDB90 /* EditNoticesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditNoticesViewModel.swift; sourceTree = ""; }; + 003CD3E828EF7C77000158E4 /* TalkPageFindInPageSearchController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageFindInPageSearchController.swift; sourceTree = ""; }; + 0042804025E6E395004945B3 /* FLAnimatedImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLAnimatedImage.m; sourceTree = ""; }; + 0042804125E6E395004945B3 /* FLAnimatedImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLAnimatedImageView.m; sourceTree = ""; }; + 0042804225E6E395004945B3 /* FLAnimatedImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLAnimatedImage.h; sourceTree = ""; }; + 0042804325E6E395004945B3 /* FLAnimatedImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLAnimatedImageView.h; sourceTree = ""; }; + 0042804425E6E395004945B3 /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; + 0042804625E6E395004945B3 /* NSValueTransformer+MTLPredefinedTransformerAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValueTransformer+MTLPredefinedTransformerAdditions.m"; sourceTree = ""; }; + 0042804725E6E395004945B3 /* NSError+MTLModelException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+MTLModelException.m"; sourceTree = ""; }; + 0042804825E6E395004945B3 /* NSDictionary+MTLJSONKeyPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+MTLJSONKeyPath.h"; sourceTree = ""; }; + 0042804B25E6E395004945B3 /* MTLEXTRuntimeExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLEXTRuntimeExtensions.h; sourceTree = ""; }; + 0042804C25E6E395004945B3 /* MTLEXTScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLEXTScope.h; sourceTree = ""; }; + 0042804D25E6E395004945B3 /* MTLMetamacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLMetamacros.h; sourceTree = ""; }; + 0042804E25E6E395004945B3 /* MTLEXTKeyPathCoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLEXTKeyPathCoding.h; sourceTree = ""; }; + 0042804F25E6E395004945B3 /* MTLEXTScope.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTLEXTScope.m; sourceTree = ""; }; + 0042805025E6E395004945B3 /* MTLEXTRuntimeExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTLEXTRuntimeExtensions.m; sourceTree = ""; }; + 0042805125E6E395004945B3 /* MTLJSONAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTLJSONAdapter.m; sourceTree = ""; }; + 0042805225E6E395004945B3 /* MTLModel+NSCoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MTLModel+NSCoding.m"; sourceTree = ""; }; + 0042805425E6E395004945B3 /* NSObject+MTLComparisonAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+MTLComparisonAdditions.h"; sourceTree = ""; }; + 0042805525E6E395004945B3 /* MTLValueTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLValueTransformer.h; sourceTree = ""; }; + 0042805625E6E395004945B3 /* MTLTransformerErrorHandling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLTransformerErrorHandling.h; sourceTree = ""; }; + 0042805725E6E395004945B3 /* NSValueTransformer+MTLInversionAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValueTransformer+MTLInversionAdditions.h"; sourceTree = ""; }; + 0042805825E6E395004945B3 /* NSDictionary+MTLManipulationAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+MTLManipulationAdditions.h"; sourceTree = ""; }; + 0042805925E6E395004945B3 /* Mantle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Mantle.h; sourceTree = ""; }; + 0042805A25E6E395004945B3 /* NSValueTransformer+MTLPredefinedTransformerAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValueTransformer+MTLPredefinedTransformerAdditions.h"; sourceTree = ""; }; + 0042805B25E6E395004945B3 /* MTLJSONAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLJSONAdapter.h; sourceTree = ""; }; + 0042805C25E6E395004945B3 /* MTLModel+NSCoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MTLModel+NSCoding.h"; sourceTree = ""; }; + 0042805D25E6E395004945B3 /* MTLModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLModel.h; sourceTree = ""; }; + 0042805E25E6E395004945B3 /* NSDictionary+MTLMappingAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+MTLMappingAdditions.h"; sourceTree = ""; }; + 0042805F25E6E395004945B3 /* NSArray+MTLManipulationAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+MTLManipulationAdditions.h"; sourceTree = ""; }; + 0042806025E6E395004945B3 /* NSArray+MTLManipulationAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+MTLManipulationAdditions.m"; sourceTree = ""; }; + 0042806125E6E395004945B3 /* MTLModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTLModel.m; sourceTree = ""; }; + 0042806225E6E395004945B3 /* NSDictionary+MTLMappingAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+MTLMappingAdditions.m"; sourceTree = ""; }; + 0042806325E6E395004945B3 /* MTLReflection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTLReflection.h; sourceTree = ""; }; + 0042806425E6E395004945B3 /* NSError+MTLModelException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+MTLModelException.h"; sourceTree = ""; }; + 0042806525E6E395004945B3 /* MTLValueTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTLValueTransformer.m; sourceTree = ""; }; + 0042806625E6E395004945B3 /* NSObject+MTLComparisonAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+MTLComparisonAdditions.m"; sourceTree = ""; }; + 0042806725E6E395004945B3 /* NSDictionary+MTLJSONKeyPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+MTLJSONKeyPath.m"; sourceTree = ""; }; + 0042806825E6E395004945B3 /* NSValueTransformer+MTLInversionAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValueTransformer+MTLInversionAdditions.m"; sourceTree = ""; }; + 0042806925E6E395004945B3 /* MTLTransformerErrorHandling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTLTransformerErrorHandling.m; sourceTree = ""; }; + 0042806A25E6E395004945B3 /* MTLReflection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTLReflection.m; sourceTree = ""; }; + 0042806B25E6E395004945B3 /* NSDictionary+MTLManipulationAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+MTLManipulationAdditions.m"; sourceTree = ""; }; + 004280F825E6E841004945B3 /* NYTPhotoViewer.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = NYTPhotoViewer.bundle; sourceTree = ""; }; + 004280F925E6E841004945B3 /* NYTPhotoDismissalInteractionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoDismissalInteractionController.h; sourceTree = ""; }; + 004280FA25E6E841004945B3 /* NYTPhotosOverlayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotosOverlayView.m; sourceTree = ""; }; + 004280FB25E6E841004945B3 /* NYTPhotoTransitionAnimator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotoTransitionAnimator.m; sourceTree = ""; }; + 004280FD25E6E841004945B3 /* NSBundle+NYTPhotoViewer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+NYTPhotoViewer.m"; sourceTree = ""; }; + 004280FE25E6E841004945B3 /* NSBundle+NYTPhotoViewer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+NYTPhotoViewer.h"; sourceTree = ""; }; + 004280FF25E6E841004945B3 /* NYTPhotoTransitionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotoTransitionController.m; sourceTree = ""; }; + 0042810025E6E841004945B3 /* NYTPhotoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotoViewController.m; sourceTree = ""; }; + 0042810125E6E841004945B3 /* NYTScalingImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTScalingImageView.h; sourceTree = ""; }; + 0042810225E6E841004945B3 /* NYTPhotoCaptionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotoCaptionView.m; sourceTree = ""; }; + 0042810325E6E841004945B3 /* NYTPhotoViewer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoViewer.h; sourceTree = ""; }; + 0042810425E6E841004945B3 /* NYTPhotosDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotosDataSource.m; sourceTree = ""; }; + 0042810525E6E841004945B3 /* NYTPhotosViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotosViewController.h; sourceTree = ""; }; + 0042810625E6E841004945B3 /* NYTPhotosOverlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotosOverlayView.h; sourceTree = ""; }; + 0042810725E6E841004945B3 /* NYTPhotoDismissalInteractionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotoDismissalInteractionController.m; sourceTree = ""; }; + 0042810825E6E841004945B3 /* NYTPhotoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoViewController.h; sourceTree = ""; }; + 0042810925E6E841004945B3 /* NYTPhotoTransitionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoTransitionController.h; sourceTree = ""; }; + 0042810A25E6E841004945B3 /* NYTPhotoTransitionAnimator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoTransitionAnimator.h; sourceTree = ""; }; + 0042810B25E6E841004945B3 /* NYTPhotoCaptionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoCaptionView.h; sourceTree = ""; }; + 0042810C25E6E841004945B3 /* NYTScalingImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTScalingImageView.m; sourceTree = ""; }; + 0042810D25E6E841004945B3 /* NYTPhotoViewerCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoViewerCore.h; sourceTree = ""; }; + 0042810F25E6E841004945B3 /* NYTPhotoCaptionViewLayoutWidthHinting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoCaptionViewLayoutWidthHinting.h; sourceTree = ""; }; + 0042811025E6E841004945B3 /* NYTPhotoContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoContainer.h; sourceTree = ""; }; + 0042811125E6E841004945B3 /* NYTPhotosViewControllerDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotosViewControllerDataSource.h; sourceTree = ""; }; + 0042811225E6E841004945B3 /* NYTPhoto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhoto.h; sourceTree = ""; }; + 0042811325E6E841004945B3 /* NYTPhotosViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotosViewController.m; sourceTree = ""; }; + 0042811425E6E841004945B3 /* NYTPhotosDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotosDataSource.h; sourceTree = ""; }; + 0042817125E6EFC4004945B3 /* LSStubRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSStubRequest.m; sourceTree = ""; }; + 0042817225E6EFC4004945B3 /* LSStubResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSStubResponse.m; sourceTree = ""; }; + 0042817325E6EFC4004945B3 /* LSStubResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSStubResponse.h; sourceTree = ""; }; + 0042817425E6EFC4004945B3 /* LSStubRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSStubRequest.h; sourceTree = ""; }; + 0042817525E6EFC4004945B3 /* LSNocilla.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSNocilla.m; sourceTree = ""; }; + 0042817625E6EFC4004945B3 /* Nocilla-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Nocilla-Prefix.pch"; sourceTree = ""; }; + 0042817825E6EFC4004945B3 /* LSHTTPRequestDiff.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSHTTPRequestDiff.m; sourceTree = ""; }; + 0042817925E6EFC4004945B3 /* LSHTTPRequestDiff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSHTTPRequestDiff.h; sourceTree = ""; }; + 0042817A25E6EFC4004945B3 /* Nocilla.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Nocilla.h; sourceTree = ""; }; + 0042817C25E6EFC4004945B3 /* LSHTTPClientHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSHTTPClientHook.m; sourceTree = ""; }; + 0042817D25E6EFC4004945B3 /* LSHTTPClientHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSHTTPClientHook.h; sourceTree = ""; }; + 0042817F25E6EFC4004945B3 /* NSURLRequest+LSHTTPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLRequest+LSHTTPRequest.m"; sourceTree = ""; }; + 0042818025E6EFC4004945B3 /* NSURLRequest+DSL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLRequest+DSL.h"; sourceTree = ""; }; + 0042818125E6EFC4004945B3 /* LSNSURLHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSNSURLHook.h; sourceTree = ""; }; + 0042818225E6EFC4004945B3 /* LSHTTPStubURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSHTTPStubURLProtocol.h; sourceTree = ""; }; + 0042818325E6EFC4004945B3 /* NSURLRequest+LSHTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLRequest+LSHTTPRequest.h"; sourceTree = ""; }; + 0042818425E6EFC4004945B3 /* LSNSURLHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSNSURLHook.m; sourceTree = ""; }; + 0042818525E6EFC4004945B3 /* NSURLRequest+DSL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLRequest+DSL.m"; sourceTree = ""; }; + 0042818625E6EFC4004945B3 /* LSHTTPStubURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSHTTPStubURLProtocol.m; sourceTree = ""; }; + 0042818825E6EFC4004945B3 /* LSASIHTTPRequestHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSASIHTTPRequestHook.h; sourceTree = ""; }; + 0042818925E6EFC4004945B3 /* ASIHTTPRequestStub.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIHTTPRequestStub.m; sourceTree = ""; }; + 0042818A25E6EFC4004945B3 /* LSASIHTTPRequestAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSASIHTTPRequestAdapter.h; sourceTree = ""; }; + 0042818B25E6EFC4004945B3 /* LSASIHTTPRequestHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSASIHTTPRequestHook.m; sourceTree = ""; }; + 0042818C25E6EFC4004945B3 /* LSASIHTTPRequestAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSASIHTTPRequestAdapter.m; sourceTree = ""; }; + 0042818D25E6EFC4004945B3 /* ASIHTTPRequestStub.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIHTTPRequestStub.h; sourceTree = ""; }; + 0042818F25E6EFC4004945B3 /* LSNSURLSessionHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSNSURLSessionHook.h; sourceTree = ""; }; + 0042819025E6EFC4004945B3 /* LSNSURLSessionHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSNSURLSessionHook.m; sourceTree = ""; }; + 0042819225E6EFC4004945B3 /* LSHTTPBody.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSHTTPBody.h; sourceTree = ""; }; + 0042819325E6EFC4004945B3 /* LSHTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSHTTPRequest.h; sourceTree = ""; }; + 0042819425E6EFC4004945B3 /* LSHTTPResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSHTTPResponse.h; sourceTree = ""; }; + 0042819525E6EFC4004945B3 /* LSNocilla.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSNocilla.h; sourceTree = ""; }; + 0042819725E6EFC4004945B3 /* LSMatcheable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSMatcheable.h; sourceTree = ""; }; + 0042819825E6EFC4004945B3 /* LSMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSMatcher.h; sourceTree = ""; }; + 0042819925E6EFC4004945B3 /* LSStringMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSStringMatcher.h; sourceTree = ""; }; + 0042819A25E6EFC4004945B3 /* NSRegularExpression+Matcheable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSRegularExpression+Matcheable.m"; sourceTree = ""; }; + 0042819B25E6EFC4004945B3 /* LSRegexMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSRegexMatcher.h; sourceTree = ""; }; + 0042819C25E6EFC4004945B3 /* NSString+Matcheable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Matcheable.m"; sourceTree = ""; }; + 0042819D25E6EFC4004945B3 /* NSData+Matcheable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+Matcheable.m"; sourceTree = ""; }; + 0042819E25E6EFC4004945B3 /* LSDataMatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSDataMatcher.m; sourceTree = ""; }; + 0042819F25E6EFC4004945B3 /* LSMatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSMatcher.m; sourceTree = ""; }; + 004281A025E6EFC4004945B3 /* LSRegexMatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSRegexMatcher.m; sourceTree = ""; }; + 004281A125E6EFC4004945B3 /* NSRegularExpression+Matcheable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSRegularExpression+Matcheable.h"; sourceTree = ""; }; + 004281A225E6EFC4004945B3 /* LSStringMatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSStringMatcher.m; sourceTree = ""; }; + 004281A325E6EFC4004945B3 /* NSString+Matcheable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Matcheable.h"; sourceTree = ""; }; + 004281A425E6EFC4004945B3 /* LSDataMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSDataMatcher.h; sourceTree = ""; }; + 004281A525E6EFC4004945B3 /* NSData+Matcheable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+Matcheable.h"; sourceTree = ""; }; + 004281A725E6EFC4004945B3 /* NSData+Nocilla.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+Nocilla.h"; sourceTree = ""; }; + 004281A825E6EFC4004945B3 /* NSString+Nocilla.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Nocilla.m"; sourceTree = ""; }; + 004281A925E6EFC4004945B3 /* NSData+Nocilla.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+Nocilla.m"; sourceTree = ""; }; + 004281AA25E6EFC4004945B3 /* NSString+Nocilla.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Nocilla.h"; sourceTree = ""; }; + 004281AC25E6EFC4004945B3 /* LSHTTPRequestDSLRepresentation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSHTTPRequestDSLRepresentation.m; sourceTree = ""; }; + 004281AD25E6EFC4004945B3 /* LSStubRequestDSL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSStubRequestDSL.h; sourceTree = ""; }; + 004281AE25E6EFC4004945B3 /* LSStubResponseDSL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSStubResponseDSL.m; sourceTree = ""; }; + 004281AF25E6EFC4004945B3 /* LSStubRequestDSL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSStubRequestDSL.m; sourceTree = ""; }; + 004281B025E6EFC4004945B3 /* LSHTTPRequestDSLRepresentation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSHTTPRequestDSLRepresentation.h; sourceTree = ""; }; + 004281B125E6EFC4004945B3 /* LSStubResponseDSL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSStubResponseDSL.h; sourceTree = ""; }; + 00474A2928DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageCoffeeRollViewController.swift; sourceTree = ""; }; + 00474A2E28DD1B13002E3C09 /* TalkPageCoffeeRollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageCoffeeRollView.swift; sourceTree = ""; }; + 00550D2526B1E7DB0055C496 /* Featured Article Widget Preview Content.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "Featured Article Widget Preview Content.json"; sourceTree = ""; }; + 005E004028DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageCoffeeRollViewModel.swift; sourceTree = ""; }; + 0062597224DE0A2500C95037 /* WidgetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetController.swift; sourceTree = ""; usesTabs = 0; }; + 006694FB265D9F2900E23AE4 /* WidgetSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetSettings.swift; sourceTree = ""; }; + 006694FD265D9F3A00E23AE4 /* WidgetCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetCache.swift; sourceTree = ""; }; + 006694FF265DA01000E23AE4 /* WidgetContentFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetContentFetcher.swift; sourceTree = ""; }; + 00669504265DA3D300E23AE4 /* FeaturedArticleWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturedArticleWidget.swift; sourceTree = ""; }; + 00669506265DAB7800E23AE4 /* FeaturedArticleWidget+LocalizedStrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FeaturedArticleWidget+LocalizedStrings.swift"; sourceTree = ""; }; + 0066BE2F265EC4A900512BE8 /* WidgetFeaturedContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetFeaturedContent.swift; sourceTree = ""; }; + 006ABEE72901E8F600722DF8 /* VanishAccountWarningView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VanishAccountWarningView.swift; sourceTree = ""; }; + 006ABEEC2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VanishAccountWarningViewHostingViewController.swift; sourceTree = ""; }; + 006D273424D8BAFB00947551 /* View+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extensions.swift"; sourceTree = ""; usesTabs = 0; }; + 006D273624D8D8D100947551 /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; usesTabs = 0; }; + 0072990528AC44F100DCD2E6 /* TalkPageCellTopicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageCellTopicView.swift; sourceTree = ""; }; + 0072990A28AC455500DCD2E6 /* TalkPageCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageCellViewModel.swift; sourceTree = ""; }; + 0072991428AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageCellCommentSeparator.swift; sourceTree = ""; }; + 0072991928AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageCellCommentView.swift; sourceTree = ""; }; + 0072991E28AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageCellReplyDepthIndicator.swift; sourceTree = ""; }; + 007B5FC426FA40F000180FF8 /* RemoteNotificationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteNotificationType.swift; sourceTree = ""; }; + 007CCF0026D5A10100D5EA7C /* NotificationsCenterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterViewController.swift; sourceTree = ""; }; + 007CCF0626D5A17200D5EA7C /* NotificationsCenterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterView.swift; sourceTree = ""; }; + 007CCF0B26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterViewModel.swift; sourceTree = ""; }; + 007CCF1026D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterPresentationDelegate.swift; sourceTree = ""; }; + 007F5C6C275AA74200E4B02C /* StackedImageLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackedImageLabelView.swift; sourceTree = ""; usesTabs = 0; }; + 009B8357298091BC00AABEA3 /* EditNoticesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditNoticesViewController.swift; sourceTree = ""; }; + 009B835C298091CD00AABEA3 /* EditNoticesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditNoticesView.swift; sourceTree = ""; }; + 009C8EC129071E720056A3AC /* NSString+Range.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSString+Range.swift"; sourceTree = ""; }; + 00A7946A245CA4E60063BA18 /* ArticleSurveyTimerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSurveyTimerController.swift; sourceTree = ""; usesTabs = 0; }; + 00A8F58526BDD5E700175B8E /* WidgetSampleContentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetSampleContentTests.swift; sourceTree = ""; }; + 00A988072829D92B006D800B /* PushNotificationContentIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationContentIdentifier.swift; sourceTree = ""; }; + 00AA5AA6276BF29E005295B0 /* StatusTextBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTextBarButtonItem.swift; sourceTree = ""; }; + 00AA5AAB276BF2AE005295B0 /* TextBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBarButtonItem.swift; sourceTree = ""; }; + 00B0B3CF2978745400DD7893 /* EditNoticesFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditNoticesFetcher.swift; sourceTree = ""; }; + 00B16E8D293AACC200EF847F /* UIImage+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = ""; }; + 00BCB71726DEE04D002C3F72 /* InsetLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsetLabelView.swift; sourceTree = ""; }; + 00BCB71C26DEE1C7002C3F72 /* VerticalSpacerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalSpacerView.swift; sourceTree = ""; }; + 00BCB72126DEEB1C002C3F72 /* RoundedImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedImageView.swift; sourceTree = ""; }; + 00CB6897288B0CD3002EBB0A /* TalkPageHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageHeaderView.swift; sourceTree = ""; }; + 00CF2E9F27DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterOnboardingView.swift; sourceTree = ""; }; + 00D1F58E28885BA300127169 /* TalkPageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageViewModel.swift; sourceTree = ""; }; + 00D280F6247EFFFE006BEE23 /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; usesTabs = 0; }; + 00D280FB247F019C006BEE23 /* Date+ExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+ExtensionTests.swift"; sourceTree = ""; usesTabs = 0; }; + 00D46DA42889B7F50015DE9B /* TalkPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageView.swift; sourceTree = ""; }; + 00D46DA92889B9250015DE9B /* TalkPageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageCell.swift; sourceTree = ""; }; + 00D4B1B3282996A2008C705C /* EchoModelVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EchoModelVersion.swift; sourceTree = ""; }; + 00D5593424DB152300C78F08 /* WidgetsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetsExtension.entitlements; sourceTree = ""; }; + 00D9276A29511E95004ECBEA /* PageHistoryCountsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHistoryCountsView.swift; sourceTree = ""; }; + 00DEE61828AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageCellCommentViewModel.swift; sourceTree = ""; }; + 00E2EA8826E28A9700B1A741 /* NotificationsCenterCellStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellStyle.swift; sourceTree = ""; }; + 00E2EA8D26E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellDisplayState.swift; sourceTree = ""; }; + 00E5B39E28EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageTopicReplyOnboardingView.swift; sourceTree = ""; }; + 00E5B3A328EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageTopicReplyOnboardingHostingController.swift; sourceTree = ""; }; + 00E75B5C27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewController.swift; sourceTree = ""; }; + 00E75B6127EB87DC00A45B78 /* NotificationsCenterDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailView.swift; sourceTree = ""; }; + 00E75B6627EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailActionCell.swift; sourceTree = ""; }; + 00E75B6B27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailHeaderCell.swift; sourceTree = ""; }; + 00E75B7027EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailContentCell.swift; sourceTree = ""; }; + 00E75B7527EB946D00A45B78 /* ReusableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReusableCell.swift; sourceTree = ""; }; + 00EACEC528E39D470054DDB4 /* TalkPageEmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageEmptyView.swift; sourceTree = ""; }; + 00EBB7C627D6878E002025AC /* BarButtonImageStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarButtonImageStyle.swift; sourceTree = ""; }; + 00EBB7CB27D6A86A002025AC /* SettingsPresentationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPresentationDelegate.swift; sourceTree = ""; }; + 00F5AECF27C6C80C006390A8 /* PushNotificationsSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationsSettingsViewController.swift; sourceTree = ""; }; + 00FCB2BD26D8398700F5A47A /* NotificationsCenterCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCell.swift; sourceTree = ""; }; + 00FCB2C226D839A500F5A47A /* NotificationsCenterCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModel.swift; sourceTree = ""; }; + 00FCCBC4290082C200C9ECD2 /* TalkPageFindInPageState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageFindInPageState.swift; sourceTree = ""; }; + 00FCCBC92900848300C9ECD2 /* TalkPageViewController+FindInPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TalkPageViewController+FindInPage.swift"; sourceTree = ""; }; + 00FCCBCE2900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSMutableAttributedString+Highlight.swift"; sourceTree = ""; }; + 040E5C4E184566F4007AFE6F /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + 041EFC361996A1F800B2CB28 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; + 0E10C4FD1C81046300CEB5C2 /* Wikipedia.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Wikipedia.entitlements; sourceTree = ""; }; + 0E19B99D1DA7CAC200239F3A /* WMFFeedDayResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFFeedDayResponse.h; sourceTree = ""; }; + 0E19B99E1DA7CAC200239F3A /* WMFFeedDayResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFFeedDayResponse.m; sourceTree = ""; }; + 0E19B9A01DA7CB8200239F3A /* WMFFeedArticlePreview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFFeedArticlePreview.h; sourceTree = ""; }; + 0E19B9A11DA7CB8200239F3A /* WMFFeedArticlePreview.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFFeedArticlePreview.m; sourceTree = ""; }; + 0E19B9A31DA7CE4400239F3A /* WMFFeedTopReadResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFFeedTopReadResponse.h; sourceTree = ""; }; + 0E19B9A41DA7CE4400239F3A /* WMFFeedTopReadResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFFeedTopReadResponse.m; sourceTree = ""; }; + 0E19B9A61DA7D52A00239F3A /* WMFFeedImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFFeedImage.h; sourceTree = ""; }; + 0E19B9A71DA7D52A00239F3A /* WMFFeedImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFFeedImage.m; sourceTree = ""; }; + 0E19B9A91DA7D77600239F3A /* WMFFeedNewsStory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFFeedNewsStory.h; sourceTree = ""; }; + 0E19B9AA1DA7D77600239F3A /* WMFFeedNewsStory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFFeedNewsStory.m; sourceTree = ""; }; + 0E19B9AD1DA7DC9D00239F3A /* WMFFeedContentFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFFeedContentFetcher.h; path = ../Wikipedia/Code/WMFFeedContentFetcher.h; sourceTree = ""; }; + 0E19B9AE1DA7DC9D00239F3A /* WMFFeedContentFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFFeedContentFetcher.m; path = ../Wikipedia/Code/WMFFeedContentFetcher.m; sourceTree = ""; }; + 0E19B9B01DA80C4900239F3A /* WMFFeedContentSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFFeedContentSource.h; sourceTree = ""; }; + 0E19B9B11DA80C4900239F3A /* WMFFeedContentSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = WMFFeedContentSource.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 0E19B9B41DAC574E00239F3A /* WMFRandomContentSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFRandomContentSource.h; sourceTree = ""; }; + 0E19B9B51DAC574E00239F3A /* WMFRandomContentSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFRandomContentSource.m; sourceTree = ""; }; + 0E26B0741C0F4F720004D687 /* WMFWelcome.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = WMFWelcome.storyboard; path = Wikipedia/Code/WMFWelcome.storyboard; sourceTree = SOURCE_ROOT; }; + 0E281A321DC263DE00FA1AB1 /* WMFLegacyReference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFLegacyReference.swift; path = Wikipedia/Code/WMFLegacyReference.swift; sourceTree = SOURCE_ROOT; }; + 0E28C4871D751ED6000C5919 /* WMFFeedContentDisplaying.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFFeedContentDisplaying.h; path = ../Wikipedia/Code/WMFFeedContentDisplaying.h; sourceTree = ""; }; + 0E3C5D371D664BFC00C95BA1 /* WMFContentSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFContentSource.h; sourceTree = ""; }; + 0E3C5D381D664CBF00C95BA1 /* WMFRelatedPagesContentSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFRelatedPagesContentSource.h; sourceTree = ""; }; + 0E3C5D391D664CBF00C95BA1 /* WMFRelatedPagesContentSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = WMFRelatedPagesContentSource.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 0E4A34701CBBFCD400A400F6 /* WMFImageGalleryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFImageGalleryViewController.h; path = Wikipedia/Code/WMFImageGalleryViewController.h; sourceTree = SOURCE_ROOT; }; + 0E4A34711CBBFCD400A400F6 /* WMFImageGalleryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFImageGalleryViewController.m; path = Wikipedia/Code/WMFImageGalleryViewController.m; sourceTree = SOURCE_ROOT; }; + 0E4D071C1CC5526200AE968B /* WMFLanguageCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WMFLanguageCell.xib; sourceTree = ""; }; + 0E568D471C78273B00E68FC1 /* SavedPageSpotlightManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SavedPageSpotlightManager.swift; path = ../Wikipedia/Code/SavedPageSpotlightManager.swift; sourceTree = ""; }; + 0E5DC8611C6D716100C39A6F /* NSUserActivity+WMFExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSUserActivity+WMFExtensions.h"; path = "../Wikipedia/Code/NSUserActivity+WMFExtensions.h"; sourceTree = ""; }; + 0E5DC8621C6D716100C39A6F /* NSUserActivity+WMFExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = "NSUserActivity+WMFExtensions.m"; path = "../Wikipedia/Code/NSUserActivity+WMFExtensions.m"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 0E69CD5A1C8773410095918B /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; + 0E6A6F511D9E9AB300189C80 /* WMFContentGroup+WMFFeedContentDisplaying.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "WMFContentGroup+WMFFeedContentDisplaying.h"; path = "../Wikipedia/Code/WMFContentGroup+WMFFeedContentDisplaying.h"; sourceTree = ""; }; + 0E6A6F521D9E9AB300189C80 /* WMFContentGroup+WMFFeedContentDisplaying.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "WMFContentGroup+WMFFeedContentDisplaying.m"; path = "../Wikipedia/Code/WMFContentGroup+WMFFeedContentDisplaying.m"; sourceTree = ""; }; + 0E78419A1C7CF5CE004F9D36 /* NSDate+WMFRelativeDate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDate+WMFRelativeDate.h"; path = "Wikipedia/Code/NSDate+WMFRelativeDate.h"; sourceTree = SOURCE_ROOT; }; + 0E78419B1C7CF5CE004F9D36 /* NSDate+WMFRelativeDate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDate+WMFRelativeDate.m"; path = "Wikipedia/Code/NSDate+WMFRelativeDate.m"; sourceTree = SOURCE_ROOT; }; + 0E8380631D64989F0076EDE4 /* ContinueReadingWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ContinueReadingWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 0E8380641D64989F0076EDE4 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; + 0E83806B1D64989F0076EDE4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + 0E83806D1D64989F0076EDE4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0E8380781D649DE10076EDE4 /* ContinueReadingWidget.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ContinueReadingWidget.entitlements; sourceTree = ""; }; + 0E8768341DDE002C00B8CACD /* WMFAnnouncementsContentSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFAnnouncementsContentSource.h; sourceTree = ""; }; + 0E8768351DDE002C00B8CACD /* WMFAnnouncementsContentSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = WMFAnnouncementsContentSource.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 0E8768381DDE00D600B8CACD /* WMFAnnouncementsFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFAnnouncementsFetcher.h; sourceTree = ""; }; + 0E8768391DDE00D600B8CACD /* WMFAnnouncementsFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFAnnouncementsFetcher.m; sourceTree = ""; }; + 0E87683D1DDE012300B8CACD /* WMFAnnouncement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFAnnouncement.h; sourceTree = ""; }; + 0E87683E1DDE012300B8CACD /* WMFAnnouncement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFAnnouncement.m; sourceTree = ""; }; + 0E8DC0931C74F0D700622CBD /* WMFDailyStatsLoggingFunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFDailyStatsLoggingFunnel.h; path = Wikipedia/Code/WMFDailyStatsLoggingFunnel.h; sourceTree = SOURCE_ROOT; }; + 0E8DC0941C74F0D700622CBD /* WMFDailyStatsLoggingFunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFDailyStatsLoggingFunnel.m; path = Wikipedia/Code/WMFDailyStatsLoggingFunnel.m; sourceTree = SOURCE_ROOT; }; + 0E9880601DA2C7CF0058D7F2 /* WMFNearbyContentSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFNearbyContentSource.h; sourceTree = ""; }; + 0E9880611DA2C7CF0058D7F2 /* WMFNearbyContentSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFNearbyContentSource.m; sourceTree = ""; }; + 0E9880631DA303070058D7F2 /* WMFContinueReadingContentSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFContinueReadingContentSource.h; sourceTree = ""; }; + 0E9880641DA303070058D7F2 /* WMFContinueReadingContentSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = WMFContinueReadingContentSource.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 0E9B9E2F1CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = WMFImageGalleryDetailOverlayView.xib; path = Wikipedia/Code/WMFImageGalleryDetailOverlayView.xib; sourceTree = SOURCE_ROOT; }; + 0E9B9E301CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFImageGalleryDetailOverlayView.m; path = Wikipedia/Code/WMFImageGalleryDetailOverlayView.m; sourceTree = SOURCE_ROOT; }; + 0E9B9E311CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFImageGalleryDetailOverlayView.h; path = Wikipedia/Code/WMFImageGalleryDetailOverlayView.h; sourceTree = SOURCE_ROOT; }; + 0EBCA7411C162ECF004F1FD9 /* MWKTitleLanguageController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKTitleLanguageController.m; path = Wikipedia/Code/MWKTitleLanguageController.m; sourceTree = SOURCE_ROOT; }; + 0EBCA7421C162ECF004F1FD9 /* MWKTitleLanguageController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKTitleLanguageController.h; path = Wikipedia/Code/MWKTitleLanguageController.h; sourceTree = SOURCE_ROOT; }; + 0EBCA7441C162EE9004F1FD9 /* MWKLanguageFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKLanguageFilter.h; path = Wikipedia/Code/MWKLanguageFilter.h; sourceTree = SOURCE_ROOT; }; + 0EBCA7451C162EE9004F1FD9 /* MWKLanguageFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKLanguageFilter.m; path = Wikipedia/Code/MWKLanguageFilter.m; sourceTree = SOURCE_ROOT; }; + 0EBCA7471C176389004F1FD9 /* WMFAlertManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFAlertManager.swift; path = Wikipedia/Code/WMFAlertManager.swift; sourceTree = SOURCE_ROOT; }; + 0EC044771C7917860033D773 /* WMFArticleTextActivitySource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFArticleTextActivitySource.h; path = Wikipedia/Code/WMFArticleTextActivitySource.h; sourceTree = SOURCE_ROOT; }; + 0EC044781C7917860033D773 /* WMFArticleTextActivitySource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFArticleTextActivitySource.m; path = Wikipedia/Code/WMFArticleTextActivitySource.m; sourceTree = SOURCE_ROOT; }; + 0EC0447A1C796FEF0033D773 /* WMFImageTextActivitySource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFImageTextActivitySource.swift; path = Wikipedia/Code/WMFImageTextActivitySource.swift; sourceTree = SOURCE_ROOT; }; + 0EC0447E1C797DC20033D773 /* WMFImageURLActivitySource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFImageURLActivitySource.swift; path = Wikipedia/Code/WMFImageURLActivitySource.swift; sourceTree = SOURCE_ROOT; }; + 0ED2E9F91CB2B3B300D1C844 /* UIVIewController+WMFCommonRotationSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIVIewController+WMFCommonRotationSupport.swift"; path = "Wikipedia/Code/UIVIewController+WMFCommonRotationSupport.swift"; sourceTree = SOURCE_ROOT; }; + 0ED79A3B1CB5B507005D9AF5 /* Wikipedia-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Wikipedia-Info.plist"; sourceTree = ""; }; + 0EE2438B1DC9889B00066CBD /* WMFTableHeaderFooterLabelView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFTableHeaderFooterLabelView.h; sourceTree = ""; }; + 0EE2438C1DC9889B00066CBD /* WMFTableHeaderFooterLabelView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFTableHeaderFooterLabelView.m; sourceTree = ""; }; + 0EE2438E1DC988AA00066CBD /* WMFTableHeaderFooterLabelView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WMFTableHeaderFooterLabelView.xib; sourceTree = ""; }; + 0EE489021D4ADCA00088505C /* UIViewController+WMFScrollToTop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIViewController+WMFScrollToTop.swift"; path = "Wikipedia/Code/UIViewController+WMFScrollToTop.swift"; sourceTree = SOURCE_ROOT; }; + 0EF224991CC5536200FDF78E /* WMFLanguageCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFLanguageCell.m; sourceTree = ""; }; + 0EF5BB661C110BFC00DE75E1 /* WMFBlockDefinitions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFBlockDefinitions.h; path = Wikipedia/Code/WMFBlockDefinitions.h; sourceTree = SOURCE_ROOT; }; + 0EF5BB6B1C110C2100DE75E1 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Wikipedia/Code/AppDelegate.h; sourceTree = SOURCE_ROOT; }; + 0EF5BB6C1C110C2100DE75E1 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Wikipedia/Code/AppDelegate.m; sourceTree = SOURCE_ROOT; usesTabs = 0; }; + 0EF8634D1C19E02700006D2D /* WMFEmptyView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = WMFEmptyView.xib; path = Wikipedia/Code/WMFEmptyView.xib; sourceTree = SOURCE_ROOT; }; + 0EF8634F1C19E4F100006D2D /* WMFEmptyView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFEmptyView.h; path = Wikipedia/Code/WMFEmptyView.h; sourceTree = SOURCE_ROOT; }; + 0EF863501C19E4F100006D2D /* WMFEmptyView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFEmptyView.m; path = Wikipedia/Code/WMFEmptyView.m; sourceTree = SOURCE_ROOT; }; + 19A172FA6AE61E76FCEF4259 /* NSUserActivity+WMFExtensionsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserActivity+WMFExtensionsTest.m"; sourceTree = ""; }; + 41CCB67321CC1F9700206B47 /* SavedArticlesCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedArticlesCollectionViewController.swift; sourceTree = ""; }; + 41FCAA3521C844CB001D8411 /* ReadingListEntryCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListEntryCollectionViewController.swift; sourceTree = ""; }; + 533AB8AD259792A9003A43D9 /* wikipedia-language-variants.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "wikipedia-language-variants.json"; sourceTree = ""; }; + 53478DE425AF8CB900F31DC2 /* Wikipedia 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Wikipedia 5.xcdatamodel"; sourceTree = ""; }; + 535F16D525CE11A300875AAD /* MWKDataStore+LanguageVariantMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MWKDataStore+LanguageVariantMigration.swift"; sourceTree = ""; }; + 53A575F92602C845009835E6 /* WMFAppViewController+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WMFAppViewController+Extensions.swift"; sourceTree = ""; }; + 53BAB79925DDDEE100A5ED4E /* Wikipedia 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Wikipedia 6.xcdatamodel"; sourceTree = ""; }; + 67059DB42260D034009811AA /* SchemeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchemeHandler.swift; sourceTree = ""; }; + 6706A21622925FD2004774E2 /* InfoBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoBannerView.swift; sourceTree = ""; }; + 6706A21822927D63004774E2 /* TalkPageHintViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageHintViewController.swift; sourceTree = ""; }; + 6707C031237DBCEA0017E7B6 /* DiffRevisionTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffRevisionTransition.swift; sourceTree = ""; }; + 6707C037237F0A6E0017E7B6 /* UIFont+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Extensions.swift"; sourceTree = ""; }; + 670AF19A26C1CA38005F76D0 /* EchoSubscriptionFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EchoSubscriptionFetcher.swift; sourceTree = ""; }; + 670AF1B826C573EB005F76D0 /* RemoteNotifications 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "RemoteNotifications 3.xcdatamodel"; sourceTree = ""; }; + 670AF1CD26CA188B005F76D0 /* RemoteNotificationLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationLinks.swift; sourceTree = ""; }; + 670F765E22B0C10600D87545 /* FakeProgressLoading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeProgressLoading.swift; sourceTree = ""; }; + 67112E3C275E603B007A9850 /* NotificationsCenterInboxViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterInboxViewModel.swift; sourceTree = ""; }; + 67134A1628A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TalkPageReplyComposeController.swift; sourceTree = ""; }; + 6713519C277285B7006C07D9 /* RemoteNotificationsRefreshDeadlineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsRefreshDeadlineController.swift; sourceTree = ""; }; + 67146031243B885E008CE885 /* SurveyAnnouncementsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveyAnnouncementsController.swift; sourceTree = ""; usesTabs = 0; }; + 67146033243B8B4F008CE885 /* AnnouncementType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnouncementType.swift; sourceTree = ""; }; + 67146035243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+SurveyAnnouncements.swift"; sourceTree = ""; usesTabs = 0; }; + 6714D6CA245A2B9700CE5A4A /* ArticleCacheReadingManualTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleCacheReadingManualTests.swift; sourceTree = ""; }; + 6714D6CC245A2C1D00CE5A4A /* ArticleTestHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleTestHelpers.swift; sourceTree = ""; }; + 671AC2552226FB9B005B37F8 /* ReadingThemesControlsProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingThemesControlsProtocols.swift; sourceTree = ""; }; + 671DF9BE25F2AE4E0011799E /* ArticleDescriptionControlling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleDescriptionControlling.swift; sourceTree = ""; }; + 671DF9BF25F2AE4E0011799E /* ShortDescriptionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortDescriptionController.swift; sourceTree = ""; }; + 671DF9C025F2AE4E0011799E /* WikidataDescriptionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WikidataDescriptionController.swift; sourceTree = ""; }; + 671DF9D725F2B59A0011799E /* ShortDescriptionControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortDescriptionControllerTests.swift; sourceTree = ""; }; + 671F5E0A236B8CAF00111116 /* EmptyViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EmptyViewController.xib; sourceTree = ""; }; + 672034E227A2531F007DC24F /* RemoteNotificationsReauthenticateOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsReauthenticateOperation.swift; sourceTree = ""; }; + 672034E427A2600C007DC24F /* RemoteNotificationsProjectOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsProjectOperation.swift; sourceTree = ""; }; + 672428962362113400490629 /* DiffFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffFetcher.swift; sourceTree = ""; }; + 67282FBC24855B7B00B73E20 /* ArticleContextMenuPresenting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleContextMenuPresenting.swift; sourceTree = ""; }; + 672B127722A450F000CC85A5 /* OldTalkPageHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OldTalkPageHeaderView.xib; sourceTree = ""; }; + 672C35EA22D8E7C9007B8D46 /* EmptyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyViewController.swift; sourceTree = ""; }; + 672D69A3273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBarAppearance+Extensions.swift"; sourceTree = ""; }; + 672D69A8273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITabBarAppearance+Extensions.swift"; sourceTree = ""; }; + 672F0557222F24FB00FB1084 /* IconBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconBarButtonItem.swift; sourceTree = ""; }; + 6730FD0D28998EFD000E5F40 /* TalkPageReplyComposeContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageReplyComposeContentView.swift; sourceTree = ""; }; + 6734114F22700A95005B31DA /* TalkPageControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageControllerTests.swift; sourceTree = ""; }; + 6734115122700C47005B31DA /* TalkPageTestHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageTestHelpers.swift; sourceTree = ""; }; + 6734116322739CA2005B31DA /* TalkPageLocalHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageLocalHandler.swift; sourceTree = ""; }; + 6734116922739FD6005B31DA /* TalkPageLocalHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageLocalHandlerTests.swift; sourceTree = ""; }; + 6734116F22773122005B31DA /* OldTalkPageReplyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OldTalkPageReplyCell.swift; sourceTree = ""; }; + 6734F051227B634900BDDB94 /* ActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = ""; }; + 673612F124FD7210002A1989 /* ArticleAsLivingDocViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocViewModelTests.swift; sourceTree = ""; }; + 6739A181273061220063E0E0 /* RemoteNotificationsMarkAllAsReadOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsMarkAllAsReadOperation.swift; sourceTree = ""; }; + 673FC3CF273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WMFTableHeaderFooterLabelView+Extensions.swift"; sourceTree = ""; }; + 6741244F27E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationsCenterDetailViewModel+ActionExtensions.swift"; sourceTree = ""; }; + 6747117D250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocReferenceCollectionViewCell.swift; sourceTree = ""; }; + 674711822507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocSnippetCollectionViewCell.swift; sourceTree = ""; }; + 6747118725072D1500287951 /* IconTitleBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconTitleBadge.swift; sourceTree = ""; }; + 674E8AB82382DEFF0053D206 /* DiffTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffTransformer.swift; sourceTree = ""; }; + 675175DB276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisappearingCallbackNavigationController.swift; sourceTree = ""; }; + 67540CA824D221E3008B2894 /* LocationManagerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManagerFactory.swift; sourceTree = ""; }; + 675A7CFD227A3F7C0034D9D9 /* OldTalkPageHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OldTalkPageHeaderView.swift; sourceTree = ""; }; + 676070A1227CE60800A81F09 /* TalkPageReplyFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageReplyFooterView.swift; sourceTree = ""; }; + 676070A32280987C00A81F09 /* TalkPageTopicNewViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TalkPageTopicNewViewController.xib; sourceTree = ""; }; + 6761AED82704BA3800E47BAD /* RemoteNotification+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RemoteNotification+CoreDataClass.swift"; sourceTree = ""; }; + 6761AEDE2704CF0000E47BAD /* WikimediaProject+RemoteNotifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WikimediaProject+RemoteNotifications.swift"; sourceTree = ""; }; + 6761AEE02704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationsCenterCellViewModel+TextExtensions.swift"; sourceTree = ""; }; + 6761AEE52704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationsCenterCellViewModel+IconNameExtensions.swift"; sourceTree = ""; }; + 6761AEEA270613B400E47BAD /* SharedContainerCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedContainerCache.swift; sourceTree = ""; }; + 6761AEEC2706247800E47BAD /* PushNotificationsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationsSettings.swift; sourceTree = ""; }; + 6761AEEE2706249300E47BAD /* PushNotificationsCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationsCache.swift; sourceTree = ""; }; + 6761AEF227065DE400E47BAD /* WMFNotificationsController+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WMFNotificationsController+Extensions.swift"; sourceTree = ""; }; + 6761AEF42707BE4200E47BAD /* RemoteNotificationsRefreshOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsRefreshOperation.swift; sourceTree = ""; }; + 6761AEF62707E34F00E47BAD /* NotificationsCenterModelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterModelController.swift; sourceTree = ""; }; + 676A8A8223A4013D0084B967 /* ArticleFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ArticleFetcher.swift; path = ../Wikipedia/Code/ArticleFetcher.swift; sourceTree = ""; }; + 676C864426D40AEA00A704C1 /* NotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 676C864626D40AEB00A704C1 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; + 676C864826D40AEB00A704C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 676C868626D4545D00A704C1 /* NotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationServiceExtension.entitlements; sourceTree = ""; }; + 676C869226D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+WindowWorkarounds.swift"; sourceTree = ""; }; + 676E813229380D8A00F15258 /* TalkPagesFunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPagesFunnel.swift; sourceTree = ""; }; + 676F39272745FB1F00F4D33D /* NotificationsCenterFiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterFiltersViewModel.swift; sourceTree = ""; }; + 6771298E24FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocViewController.swift; sourceTree = ""; usesTabs = 0; }; + 6771299324FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocLargeEventCollectionViewCell.swift; sourceTree = ""; }; + 6771299F24FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocHorizontallyScrollingCell.swift; sourceTree = ""; }; + 6771C9532509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ArticleAsLivingDocHeaderView.xib; sourceTree = ""; }; + 6773B1FD240F02E40022A70E /* PermanentlyPersistableURLCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PermanentlyPersistableURLCache.swift; sourceTree = ""; }; + 6773B2012411D8600022A70E /* ArticleCacheDBWriter+SyncResources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleCacheDBWriter+SyncResources.swift"; sourceTree = ""; }; + 6773B2032411DCF50022A70E /* ArticleCacheResourceDBWriting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleCacheResourceDBWriting.swift; sourceTree = ""; }; + 6779618C29245BF300C2A65F /* PageIDToURLFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageIDToURLFetcher.swift; sourceTree = ""; }; + 6779618E29246BC900C2A65F /* NSUserActivity+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUserActivity+Extensions.swift"; sourceTree = ""; }; + 6779D45023F60903002840CA /* CacheFileWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheFileWriter.swift; sourceTree = ""; }; + 6779D45223F6EC2D002840CA /* CacheFetching.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheFetching.swift; sourceTree = ""; }; + 6780CF222967683800D45927 /* TalkPageArchivesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageArchivesViewController.swift; sourceTree = ""; }; + 6780CF272967690200D45927 /* TalkPageArchivesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageArchivesView.swift; sourceTree = ""; }; + 6780CF2C29676AB000D45927 /* ShiftingTopViewsContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShiftingTopViewsContaining.swift; sourceTree = ""; }; + 6780CF3229676DE300D45927 /* ShiftingTopViewsStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShiftingTopViewsStack.swift; sourceTree = ""; }; + 6780D5B3237A1F480087A5D1 /* DiffResponse.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = DiffResponse.json; sourceTree = ""; }; + 6780D5B9237AF8A10087A5D1 /* DiffToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffToolbarView.swift; sourceTree = ""; }; + 6780D5BF237AF8AE0087A5D1 /* DiffToolbarView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiffToolbarView.xib; sourceTree = ""; }; + 6782DB902343B6F9003FA21B /* DiffContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffContainerViewController.swift; sourceTree = ""; }; + 6782DB9C2343B7DB003FA21B /* DiffHeaderTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffHeaderTitleView.swift; sourceTree = ""; }; + 6782DBA22343B7EE003FA21B /* DiffHeaderSummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffHeaderSummaryView.swift; sourceTree = ""; }; + 6782DBA82343B7FC003FA21B /* DiffHeaderEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffHeaderEditorView.swift; sourceTree = ""; }; + 6782DBAE2343B812003FA21B /* DiffHeaderCompareView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffHeaderCompareView.swift; sourceTree = ""; }; + 6782DBBA2343B861003FA21B /* DiffListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffListViewController.swift; sourceTree = ""; }; + 6782DBC02343FDCA003FA21B /* DiffListChangeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffListChangeCell.swift; sourceTree = ""; }; + 6782DBC62343FDE4003FA21B /* DiffListContextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffListContextCell.swift; sourceTree = ""; }; + 6782DBCC2343FDF2003FA21B /* DiffListUneditedCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffListUneditedCell.swift; sourceTree = ""; }; + 6782DBD22343FE03003FA21B /* DiffListGroupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffListGroupViewModel.swift; sourceTree = ""; }; + 6782DBD82344EC86003FA21B /* DiffHeaderViewModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffHeaderViewModels.swift; sourceTree = ""; }; + 6782DBE02345053C003FA21B /* DiffHeaderExtendedView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiffHeaderExtendedView.xib; sourceTree = ""; }; + 6782DBE12345054C003FA21B /* DiffHeaderExtendedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffHeaderExtendedView.swift; sourceTree = ""; }; + 6782DBE22345370F003FA21B /* DiffHeaderTitleView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiffHeaderTitleView.xib; sourceTree = ""; }; + 6782DBE32345377B003FA21B /* DiffHeaderSummaryView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiffHeaderSummaryView.xib; sourceTree = ""; }; + 6782DBE923453787003FA21B /* DiffHeaderEditorView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiffHeaderEditorView.xib; sourceTree = ""; }; + 6782DBEF23453799003FA21B /* DiffHeaderCompareView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiffHeaderCompareView.xib; sourceTree = ""; }; + 6782DC0423453D6B003FA21B /* DiffHeaderCompareItemView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiffHeaderCompareItemView.xib; sourceTree = ""; }; + 6782DC0A23453D7D003FA21B /* DiffHeaderCompareItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffHeaderCompareItemView.swift; sourceTree = ""; }; + 6782DC102346920B003FA21B /* DiffContainerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffContainerViewModel.swift; sourceTree = ""; }; + 6782DC162347EE59003FA21B /* DiffListChangeCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiffListChangeCell.xib; sourceTree = ""; }; + 6789FA2D22E7790900E43842 /* TalkPage+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TalkPage+Extensions.swift"; sourceTree = ""; }; + 678C7C2923BE67F0001AC4D5 /* CacheController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheController.swift; sourceTree = ""; }; + 678C7C2D23BE705C001AC4D5 /* CacheDBWriting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheDBWriting.swift; sourceTree = ""; }; + 678C7C2F23BE7319001AC4D5 /* CacheDBWriterHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheDBWriterHelper.swift; sourceTree = ""; }; + 678C7C3323BE75F9001AC4D5 /* CacheFileWriterHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheFileWriterHelper.swift; sourceTree = ""; }; + 678C7C3523BE7779001AC4D5 /* FileManager+CacheExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+CacheExtensions.swift"; sourceTree = ""; }; + 678D29AB2729EAD20036C5D9 /* RemoteNotification+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RemoteNotification+CoreDataProperties.swift"; sourceTree = ""; }; + 678D29AD2729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NotificationsCenterCellViewModel+LinkExtensions.swift"; sourceTree = ""; }; + 678D29B2272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationsCenterCellViewModel+SheetActionExtensions.swift"; sourceTree = ""; }; + 678D79E3235E592F006161FF /* DiffListChangeItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffListChangeItemViewModel.swift; sourceTree = ""; }; + 678D79EF235E5979006161FF /* DiffListChangeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffListChangeViewModel.swift; sourceTree = ""; }; + 678D79F5235E599B006161FF /* DiffListContextViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffListContextViewModel.swift; sourceTree = ""; }; + 678D79FB235E59B2006161FF /* DiffListUneditedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffListUneditedViewModel.swift; sourceTree = ""; }; + 678E7E8026432F060005439C /* NavigationEventsFunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationEventsFunnel.swift; sourceTree = ""; }; + 678F511823A4B92000CE5357 /* ArticleCacheDBWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ArticleCacheDBWriter.swift; path = ../Wikipedia/Code/ArticleCacheDBWriter.swift; sourceTree = ""; }; + 679471DA275F245000621071 /* NotificationsCenterInboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterInboxView.swift; sourceTree = ""; }; + 6798035B24F94CE300D765AA /* SignificantEventsFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignificantEventsFetcher.swift; sourceTree = ""; }; + 6798036024F94CEE00D765AA /* ArticleAsLivingDocController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocController.swift; sourceTree = ""; }; + 6798036524F94D0300D765AA /* ArticleAsLivingDocViewModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocViewModels.swift; sourceTree = ""; usesTabs = 0; }; + 6798036A24F94D6700D765AA /* SignificantEventsModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignificantEventsModels.swift; sourceTree = ""; usesTabs = 0; }; + 6798331922C174ED0073CE6F /* LinkOnlyTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkOnlyTextView.swift; sourceTree = ""; }; + 6798332822C3F28A0073CE6F /* UITextView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Extensions.swift"; sourceTree = ""; }; + 67985A852524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocHintViewController.swift; sourceTree = ""; }; + 679A23F82968D865008D7686 /* ShiftingTopViewsData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShiftingTopViewsData.swift; sourceTree = ""; }; + 679A23FD2968DAB9008D7686 /* ShiftingTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShiftingTopView.swift; sourceTree = ""; }; + 679A24022968DBFC008D7686 /* ShiftingNavigationBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShiftingNavigationBarView.swift; sourceTree = ""; }; + 679A24072968E0D0008D7686 /* ShiftingScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShiftingScrollView.swift; sourceTree = ""; }; + 679F0AA82456FADE00EF4A6A /* ArticleCacheReadingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleCacheReadingTests.swift; sourceTree = ""; }; + 679F0AAC24574AD400EF4A6A /* ArticleViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleViewControllerTests.swift; sourceTree = ""; }; + 679FA103242E651C0095F3C6 /* ArticleManualPerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleManualPerformanceTests.swift; sourceTree = ""; }; + 67A5E656236775C3007749FB /* GlobalUserInfoFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalUserInfoFetcher.swift; sourceTree = ""; }; + 67A6F13723BFB75300736539 /* ImageCacheDBWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCacheDBWriter.swift; sourceTree = ""; }; + 67A6F13923BFEA0400736539 /* ImageFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageFetcher.swift; sourceTree = ""; }; + 67A6F13D23BFEF4200736539 /* ArticleCacheController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleCacheController.swift; sourceTree = ""; }; + 67A6F13F23BFF62200736539 /* ImageCacheController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCacheController.swift; sourceTree = ""; }; + 67A7CA7428665CEF008D4BF6 /* HTTPStatusCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPStatusCode.swift; sourceTree = ""; }; + 67ADEE9523A2CFFB0000CAF7 /* ArticleWebMessagingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleWebMessagingController.swift; sourceTree = ""; }; + 67B5333B28416A3B00C33E13 /* UserDataExportCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDataExportCache.swift; sourceTree = ""; }; + 67B64D562507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocSectionHeaderView.swift; sourceTree = ""; }; + 67B64D5B2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocSmallEventCollectionViewCell.swift; sourceTree = ""; }; + 67B7E7792988768C00708A81 /* MediaWikiApiErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaWikiApiErrors.swift; sourceTree = ""; }; + 67BEFFD428AD9DF000606B38 /* TalkPageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageType.swift; sourceTree = ""; }; + 67BEFFD928AEDF3600606B38 /* WikimediaProject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WikimediaProject.swift; sourceTree = ""; }; + 67C1757528AD4D6000C5ABA4 /* TalkPageDataController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageDataController.swift; sourceTree = ""; }; + 67C6F74D27E2919A00B9C864 /* RemoteNotificationsModelController+TestExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RemoteNotificationsModelController+TestExtensions.swift"; sourceTree = ""; }; + 67C6F74F27E293C700B9C864 /* NotificationsCenterViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterViewModelTests.swift; sourceTree = ""; }; + 67C6F76727E2E76E00B9C864 /* NotificationsCenterCellViewModelUserTalkMessageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModelUserTalkMessageTests.swift; sourceTree = ""; }; + 67C6F76927E2E77D00B9C864 /* NotificationsCenterCellViewModelWikidataConnectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModelWikidataConnectionTests.swift; sourceTree = ""; }; + 67C6F76A27E2E77E00B9C864 /* NotificationsCenterCellViewModelPageLinkTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModelPageLinkTests.swift; sourceTree = ""; }; + 67C6F76B27E2E77E00B9C864 /* NotificationsCenterCellViewModelGenericTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModelGenericTests.swift; sourceTree = ""; }; + 67C6F76C27E2E77F00B9C864 /* NotificationsCenterCellViewModelMentionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModelMentionTests.swift; sourceTree = ""; }; + 67C6F76D27E2E78000B9C864 /* NotificationsCenterCellViewModelEditMilestoneTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModelEditMilestoneTests.swift; sourceTree = ""; }; + 67C6F76E27E2E78100B9C864 /* NotificationsCenterCellViewModelThanksTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModelThanksTests.swift; sourceTree = ""; }; + 67C6F76F27E2E78300B9C864 /* NotificationsCenterCellViewModelUserRightsChangeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModelUserRightsChangeTests.swift; sourceTree = ""; }; + 67C6F77027E2E78400B9C864 /* NotificationsCenterCellViewModelEditRevertedTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModelEditRevertedTests.swift; sourceTree = ""; }; + 67C6F77127E2E78500B9C864 /* NotificationsCenterCellViewModelLoginIssuesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModelLoginIssuesTests.swift; sourceTree = ""; }; + 67C6F77227E2E78600B9C864 /* NotificationsCenterCellViewModelWelcomeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCellViewModelWelcomeTests.swift; sourceTree = ""; }; + 67C6F77D27E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterFlowHostingController.swift; sourceTree = ""; }; + 67C6F78227E8BC2E00B9C864 /* NotificationsCenterIconType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterIconType.swift; sourceTree = ""; }; + 67C6F79127E8C03900B9C864 /* NotificationsCenterAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterAction.swift; sourceTree = ""; }; + 67C6F79627E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterCommonViewModel.swift; sourceTree = ""; }; + 67C6F79B27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationsCenterCommonViewModel+LinkExtensions.swift"; sourceTree = ""; }; + 67C6F7A027E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationsCenterCommonViewModel+ActionExtensions.swift"; sourceTree = ""; }; + 67C6F7A527E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationsCenterCommonViewModel+TextExtensions.swift"; sourceTree = ""; }; + 67C6F7AA27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModel.swift; sourceTree = ""; }; + 67C78F7028B6DA1300AC207A /* SwiftUITextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUITextView.swift; sourceTree = ""; }; + 67C78F7528B7406E00AC207A /* VanishAccountFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VanishAccountFooterView.swift; sourceTree = ""; }; + 67C9D58E28D3689F00629165 /* WMFLocalizedDateFormatStrings+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WMFLocalizedDateFormatStrings+Extensions.swift"; sourceTree = ""; }; + 67C9D59028D36BDD00629165 /* WMFFeedNewsStory+LocalizedStrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WMFFeedNewsStory+LocalizedStrings.swift"; sourceTree = ""; }; + 67C9FBFE28C77F350065A530 /* TalkPageTopicComposeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageTopicComposeViewController.swift; sourceTree = ""; }; + 67CEF25E234FCA8100D5CA6C /* DiffListContextCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiffListContextCell.xib; sourceTree = ""; }; + 67CEF2602350C29D00D5CA6C /* DiffListUneditedCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiffListUneditedCell.xib; sourceTree = ""; }; + 67CEF262235110F700D5CA6C /* DiffNetworkModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffNetworkModels.swift; sourceTree = ""; }; + 67CEF26E2351113000D5CA6C /* DiffController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffController.swift; sourceTree = ""; }; + 67D3C452228CB54E001D5741 /* OldTalkPageReplyComposeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OldTalkPageReplyComposeView.swift; sourceTree = ""; }; + 67D6C008240581B2005709B1 /* Cache 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Cache 2.xcdatamodel"; sourceTree = ""; }; + 67D6C009240581ED005709B1 /* CacheItemMigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheItemMigrationPolicy.swift; sourceTree = ""; }; + 67D6C00B24058714005709B1 /* CacheItemMappingModel.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = CacheItemMappingModel.xcmappingmodel; sourceTree = ""; }; + 67D6C01A2405A4FB005709B1 /* CacheItem+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CacheItem+CoreDataClass.swift"; sourceTree = ""; }; + 67D6C01B2405A4FB005709B1 /* CacheItem+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CacheItem+CoreDataProperties.swift"; sourceTree = ""; }; + 67D6C01E2405B3D2005709B1 /* CacheGroup+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CacheGroup+CoreDataClass.swift"; sourceTree = ""; }; + 67D6C01F2405B3D2005709B1 /* CacheGroup+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CacheGroup+CoreDataProperties.swift"; sourceTree = ""; }; + 67D9D1EF2970D88E00BFCD4F /* DisclosureButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisclosureButton.swift; sourceTree = ""; }; + 67D9D1F52970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundHighlightingButtonStyle.swift; sourceTree = ""; }; + 67D9D1FA29711CA700BFCD4F /* Loadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Loadable.swift; sourceTree = ""; }; + 67DA31872720957A0035D40F /* RemoteNotificationsPagingOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsPagingOperation.swift; sourceTree = ""; }; + 67DAEDA023CD1BC9003AA208 /* CacheGatekeeper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheGatekeeper.swift; sourceTree = ""; }; + 67DAEDA223CE24DA003AA208 /* SavedArticlesFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedArticlesFetcher.swift; sourceTree = ""; }; + 67DAEDD827E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationsCenterDetailViewModel+TextExtensions.swift"; sourceTree = ""; }; + 67DAEDDD27E8FB5F005CF9B6 /* NotificationsCenterDetailViewModelWelcomeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModelWelcomeTests.swift; sourceTree = ""; }; + 67DAEDDE27E8FB5F005CF9B6 /* NotificationsCenterDetailViewModelLoginIssuesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModelLoginIssuesTests.swift; sourceTree = ""; }; + 67DAEDDF27E8FB5F005CF9B6 /* NotificationsCenterDetailViewModelGenericTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModelGenericTests.swift; sourceTree = ""; }; + 67DAEDE027E8FB60005CF9B6 /* NotificationsCenterDetailViewModelUserRightsChangeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModelUserRightsChangeTests.swift; sourceTree = ""; }; + 67DAEDE127E8FB60005CF9B6 /* NotificationsCenterDetailViewModelUserTalkMessageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModelUserTalkMessageTests.swift; sourceTree = ""; }; + 67DAEDE227E8FB60005CF9B6 /* NotificationsCenterDetailViewModelWikidataConnectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModelWikidataConnectionTests.swift; sourceTree = ""; }; + 67DAEDE327E8FB61005CF9B6 /* NotificationsCenterDetailViewModelEditRevertedTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModelEditRevertedTests.swift; sourceTree = ""; }; + 67DAEDE427E8FB61005CF9B6 /* NotificationsCenterDetailViewModelThanksTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModelThanksTests.swift; sourceTree = ""; }; + 67DAEDE527E8FB62005CF9B6 /* NotificationsCenterDetailViewModelMentionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModelMentionTests.swift; sourceTree = ""; }; + 67DAEDE627E8FB62005CF9B6 /* NotificationsCenterDetailViewModelEditMilestoneTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModelEditMilestoneTests.swift; sourceTree = ""; }; + 67DAEDE727E8FB62005CF9B6 /* NotificationsCenterDetailViewModelPageLinkTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsCenterDetailViewModelPageLinkTests.swift; sourceTree = ""; }; + 67DC5BE223A017CA00B03A84 /* ArticleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleViewController.swift; sourceTree = ""; usesTabs = 0; }; + 67DC5BE823A03FE700B03A84 /* ArticleToolbarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleToolbarController.swift; sourceTree = ""; usesTabs = 0; }; + 67DC5BEE23A1427C00B03A84 /* ActionHandlerScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionHandlerScript.swift; sourceTree = ""; }; + 67E069052238A396008550AC /* FindAndReplaceKeyboardBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindAndReplaceKeyboardBar.swift; sourceTree = ""; }; + 67E069072238A5A5008550AC /* WMFFindAndReplaceKeyboardBar.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WMFFindAndReplaceKeyboardBar.xib; sourceTree = ""; }; + 67E06918223B32DF008550AC /* FocusNavigationView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FocusNavigationView.xib; sourceTree = ""; }; + 67E0691A223B32F1008550AC /* FocusNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusNavigationView.swift; sourceTree = ""; }; + 67E2E48E250452E60070F12D /* ArticleAsLivingDocHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleAsLivingDocHeaderView.swift; sourceTree = ""; }; + 67E2E4932504E1C70070F12D /* TimelineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = ""; }; + 67E3992924786E2100441831 /* ReadingListManualPerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListManualPerformanceTests.swift; sourceTree = ""; }; + 67E3992B24786E6D00441831 /* TalkPageManualPerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageManualPerformanceTests.swift; sourceTree = ""; }; + 67E466F9241BED770014149B /* EditHistoryCompareFunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistoryCompareFunnel.swift; sourceTree = ""; }; + 67E50B2A27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationsCenterDetailViewModel+ImageExtensions.swift"; sourceTree = ""; }; + 67E5DA5B2761B0AB00CE827D /* NotificationsCenterFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsCenterFilterView.swift; sourceTree = ""; }; + 67E5DA6A276416A600CE827D /* RemoteNotificationsRefreshCrossWikiOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsRefreshCrossWikiOperation.swift; sourceTree = ""; }; + 67E8B0732268DA8B00537BC9 /* OldTalkPageTopicCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OldTalkPageTopicCell.swift; sourceTree = ""; }; + 67E8B0752268DE4B00537BC9 /* TalkPageContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageContainerViewController.swift; sourceTree = ""; }; + 67E8B0A4226A64CB00537BC9 /* OldTalkPagesController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OldTalkPagesController.swift; sourceTree = ""; }; + 67E8B0A7226A654D00537BC9 /* OldTalkPageFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OldTalkPageFetcher.swift; sourceTree = ""; }; + 67E8B0AB226A6DCA00537BC9 /* TalkPageNetworkDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageNetworkDataTests.swift; sourceTree = ""; }; + 67E8B0AD226A74C200537BC9 /* OldTalkPageFetcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OldTalkPageFetcherTests.swift; sourceTree = ""; }; + 67E8B0B6226F5E3800537BC9 /* Wikipedia 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Wikipedia 2.xcdatamodel"; sourceTree = ""; }; + 67E9A11B25536B6F00C5ED31 /* ABTestsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABTestsController.swift; sourceTree = ""; }; + 67ED8EB024F99FF400DD5D39 /* SignificantEventsFetcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignificantEventsFetcherTests.swift; sourceTree = ""; }; + 67F1375D23C986CD00512B61 /* CacheTaskTracking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheTaskTracking.swift; sourceTree = ""; }; + 67F35A6523E875B900C3D6C7 /* WMFLanguagesViewControllerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMFLanguagesViewControllerDelegate.h; sourceTree = ""; }; + 67F73382273C163700D7D713 /* TimeInterval+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Extensions.swift"; sourceTree = ""; }; + 67F73385273C1FBA00D7D713 /* NotificationServiceHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceHelperTests.swift; sourceTree = ""; }; + 67F73387273C26A000D7D713 /* NotificationServiceHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceHelper.swift; sourceTree = ""; }; + 67F73E6C2267B79E0079DEEF /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = ""; }; + 67F73E702267B8020079DEEF /* TalkPageTopicListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageTopicListViewController.swift; sourceTree = ""; }; + 67F73E742267B9070079DEEF /* TalkPageReplyListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageReplyListViewController.swift; sourceTree = ""; }; + 67F73E782267B9500079DEEF /* TalkPageTopicNewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageTopicNewViewController.swift; sourceTree = ""; }; + 67FBE334297056EB00A2E4AD /* TalkPageArchivesFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageArchivesFetcher.swift; sourceTree = ""; }; + 67FBE33929705FC200A2E4AD /* TalkPageArchivesItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageArchivesItem.swift; sourceTree = ""; }; + 67FF9C6A28076ADA000963D1 /* NSError+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSError+Utilities.swift"; sourceTree = ""; }; + 7004A5B9268CEE680029C46B /* MetricsClientBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsClientBridge.swift; sourceTree = ""; }; + 702096B8256C3D5700E27041 /* SamplingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SamplingController.swift; sourceTree = ""; }; + 70B798132575714100C10BCA /* EventPlatformEvents.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = EventPlatformEvents.xcdatamodel; sourceTree = ""; }; + 70B7981F257577B800C10BCA /* StorageManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageManager.swift; sourceTree = ""; }; + 70B7982A25758E6D00C10BCA /* EPEventRecord+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EPEventRecord+CoreDataClass.swift"; sourceTree = ""; }; + 70B7983525758EB800C10BCA /* EPEventRecord+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EPEventRecord+CoreDataProperties.swift"; sourceTree = ""; }; + 7616D4941C5A67D20077ADF7 /* WMFUtilityMacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMFUtilityMacros.h; sourceTree = ""; }; + 7A00D16C208FB61200A9C7BA /* BatchEditToolbarViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BatchEditToolbarViewController.xib; sourceTree = ""; }; + 7A00E0BF20A9E54400F033C8 /* SettingsFunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFunnel.swift; sourceTree = ""; }; + 7A0161B31FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListsCollectionViewCell.swift; sourceTree = ""; }; + 7A0161DF1FE8B4CA00AEDC3D /* TagCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagCollectionViewCell.swift; sourceTree = ""; }; + 7A0312F62153C4990095C953 /* RemoteNotificationsModelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsModelController.swift; sourceTree = ""; }; + 7A0312F82153DEB30095C953 /* RemoteNotificationsAPIController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsAPIController.swift; sourceTree = ""; }; + 7A0312FA215402FD0095C953 /* RemoteNotificationsImportOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsImportOperation.swift; sourceTree = ""; usesTabs = 0; }; + 7A0312FE215422960095C953 /* RemoteNotificationsMarkReadOrUnreadOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsMarkReadOrUnreadOperation.swift; sourceTree = ""; }; + 7A03130221542F5C0095C953 /* RemoteNotificationsOperationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsOperationsController.swift; sourceTree = ""; usesTabs = 0; }; + 7A06020D20EAAF5A00FBB71D /* ExploreFeedPreferencesUpdateCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreFeedPreferencesUpdateCoordinator.swift; sourceTree = ""; }; + 7A07A46720AA3F5100F7B2BB /* WMFContentGroup+EventLogging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WMFContentGroup+EventLogging.swift"; sourceTree = ""; }; + 7A07A46D20AA482C00F7B2BB /* SessionsFunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionsFunnel.swift; sourceTree = ""; }; + 7A0CD23F21DFA34000066F68 /* TextFormattingToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormattingToolbarView.swift; sourceTree = ""; }; + 7A0DE4FE20CEEC760032AB57 /* ExploreFeedSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreFeedSettingsViewController.swift; sourceTree = ""; usesTabs = 0; }; + 7A0F2588217221D10028871B /* RepeatingTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeatingTimer.swift; sourceTree = ""; }; + 7A0FF2CB230343BA00E755D4 /* PageHistoryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHistoryCollectionViewCell.swift; sourceTree = ""; }; + 7A13A8982028BB3600F28254 /* ReadingListsAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListsAlertController.swift; sourceTree = ""; }; + 7A1469BC220BBE44000A20F1 /* EditHintViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHintViewController.swift; sourceTree = ""; }; + 7A1469C4220BC223000A20F1 /* EditHintController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHintController.swift; sourceTree = ""; }; + 7A16C4E4212D941C00F0D5EC /* SubSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubSettingsViewController.swift; sourceTree = ""; }; + 7A16C4E5212D941C00F0D5EC /* SubSettingsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SubSettingsViewController.xib; sourceTree = ""; }; + 7A196F5921BF199500D9E4B5 /* SectionEditorWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionEditorWebView.swift; sourceTree = ""; }; + 7A19C64720DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseExploreFeedSettingsViewController.swift; sourceTree = ""; }; + 7A1C498E227254EC00230ED2 /* InsertMediaSearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaSearchViewController.swift; sourceTree = ""; }; + 7A1C4994227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaSelectedImageViewController.swift; sourceTree = ""; }; + 7A203F091FDEDCDD00A229EC /* ReadingListHintController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListHintController.swift; sourceTree = ""; }; + 7A20AE072057F39C005FB5DF /* UIView+Identifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Identifier.swift"; sourceTree = ""; }; + 7A2432BC1FCF401900FB4BA5 /* CreateReadingListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateReadingListViewController.swift; sourceTree = ""; }; + 7A2432BD1FCF401900FB4BA5 /* CreateReadingListViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CreateReadingListViewController.xib; sourceTree = ""; }; + 7A2432EC1FCF469100FB4BA5 /* SetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupView.swift; sourceTree = ""; }; + 7A25367521B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextualHighlightEditToolbarView.swift; sourceTree = ""; }; + 7A25367621B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ContextualHighlightEditToolbarView.xib; sourceTree = ""; }; + 7A25B1CB20A483F1008C6F29 /* LoginFunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginFunnel.swift; sourceTree = ""; }; + 7A27E85121B19767001B2D21 /* TextStyleFormattingTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextStyleFormattingTableViewController.swift; sourceTree = ""; }; + 7A27EDA12279F5270010CB24 /* InsertLinkViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertLinkViewController.swift; sourceTree = ""; }; + 7A28126120D3F84A009B42B5 /* FeedCardSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedCardSettingsViewController.swift; sourceTree = ""; }; + 7A29A5C71F6C405900E8F42B /* HistoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewController.swift; sourceTree = ""; }; + 7A29A5CD1F6C49C600E8F42B /* CollectionViewUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewUpdater.swift; sourceTree = ""; }; + 7A2BB1D321F27AC5004C0FDF /* WKWebView+OffsetHack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WKWebView+OffsetHack.swift"; sourceTree = ""; }; + 7A2FE5552051757E00F92F8F /* EraseSavedArticlesView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EraseSavedArticlesView.xib; sourceTree = ""; }; + 7A2FE55B20517BAE00F92F8F /* EraseSavedArticlesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EraseSavedArticlesView.swift; sourceTree = ""; }; + 7A3159CE206458B000143119 /* ReadingListAlertType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListAlertType.swift; sourceTree = ""; }; + 7A32078721E40193009E1677 /* SectionEditorMenuItemsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionEditorMenuItemsController.swift; sourceTree = ""; }; + 7A35CB861FD82B6300AAF3B7 /* ReadingListDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListDetailViewController.swift; sourceTree = ""; }; + 7A39327F236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHistoryComparisonSelectionViewController.swift; sourceTree = ""; }; + 7A393280236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PageHistoryComparisonSelectionViewController.xib; sourceTree = ""; }; + 7A3AD05620ADAFEF00C92E04 /* WMFCaptcha.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WMFCaptcha.swift; path = "../../WMF Framework/WMFCaptcha.swift"; sourceTree = ""; }; + 7A3EE1532267DC3800709CF6 /* Array+SafeIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+SafeIndex.swift"; sourceTree = ""; }; + 7A4170D8229EFC2A00251582 /* PageNamespace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageNamespace.swift; sourceTree = ""; }; + 7A420DB322A029780005689B /* EditFunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditFunnel.swift; sourceTree = ""; }; + 7A45AB7F20AB2A4C006A92F5 /* Dictionary+Equality.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Equality.swift"; sourceTree = ""; }; + 7A48EA0D21B5C9B20083F3DC /* EditToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditToolbarView.swift; sourceTree = ""; }; + 7A49A20021231510005C574C /* CollectionViewFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewFooter.swift; sourceTree = ""; }; + 7A4ABA8520AA8966007AA405 /* UserHistoryFunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserHistoryFunnel.swift; sourceTree = ""; }; + 7A4B333B2136EDED00C6C820 /* UnderlineButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnderlineButton.swift; sourceTree = ""; }; + 7A4D227B21B1CD8600D889BD /* TextFormattingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormattingTableViewCell.swift; sourceTree = ""; }; + 7A52C01A2150389D00A3A4A1 /* RemoteNotificationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsController.swift; sourceTree = ""; }; + 7A5357AA215552E7007998DC /* RemoteNotificationsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsOperation.swift; sourceTree = ""; }; + 7A5A0542225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaSearchResultCollectionViewCell.swift; sourceTree = ""; }; + 7A5A2776206D288C004CC837 /* NSFileManager+DirectorySize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSFileManager+DirectorySize.swift"; sourceTree = ""; }; + 7A5AB82522940CE200B91C9C /* WMFHTMLElement.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMFHTMLElement.m; sourceTree = ""; }; + 7A5AB82B22940D8500B91C9C /* WMFHTMLElement.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMFHTMLElement.h; sourceTree = ""; }; + 7A610CB6220A30C900C266AE /* HintViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HintViewController.swift; sourceTree = ""; }; + 7A610CBC220A582A00C266AE /* HintController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HintController.swift; sourceTree = ""; }; + 7A630F6B217A3FB100FC93FC /* Array+Chunked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Array+Chunked.swift"; path = "../Wikipedia/Code/Array+Chunked.swift"; sourceTree = ""; }; + 7A6CA28C2289AF2200C7FD47 /* EditLinkViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditLinkViewController.swift; sourceTree = ""; }; + 7A6CA28D2289AF2200C7FD47 /* EditLinkViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EditLinkViewController.xib; sourceTree = ""; }; + 7A6F55FE21AF508B0076D184 /* TextFormatting.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = TextFormatting.storyboard; sourceTree = ""; }; + 7A6F560421AF527A0076D184 /* TextFormattingInputViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormattingInputViewController.swift; sourceTree = ""; }; + 7A70797B223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomePanelLabelContentViewController.swift; sourceTree = ""; }; + 7A70797C223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WelcomePanelLabelContentViewController.xib; sourceTree = ""; }; + 7A71565A226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaImagePositionSettingsViewController.swift; sourceTree = ""; }; + 7A715660226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaImageTypeSettingsViewController.swift; sourceTree = ""; }; + 7A715666226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaImageSizeSettingsViewController.swift; sourceTree = ""; }; + 7A71566C22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaCustomImageSizeSettingTableViewCell.swift; sourceTree = ""; }; + 7A71566D22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InsertMediaCustomImageSizeSettingTableViewCell.xib; sourceTree = ""; }; + 7A71567822699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaLabelTableFooterView.swift; sourceTree = ""; }; + 7A73B48121E54B4200249E09 /* SectionEditorNavigationItemController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionEditorNavigationItemController.swift; sourceTree = ""; }; + 7A741DC8207FB9CB00CBAAE2 /* SearchBarExtendedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarExtendedViewController.swift; sourceTree = ""; }; + 7A741DC9207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SearchBarExtendedViewController.xib; sourceTree = ""; }; + 7A77B7801F9FCB2500753FF5 /* ArticlePeekPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticlePeekPreviewViewController.swift; sourceTree = ""; }; + 7A79A39220A24A7C00F9BDF9 /* EventLoggingStandardEventProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventLoggingStandardEventProviding.swift; sourceTree = ""; }; + 7A79CCE6200C29A10099B01F /* BatchEditToolbarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchEditToolbarViewController.swift; sourceTree = ""; }; + 7A7AC84521B6B89B003B849B /* SectionEditorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionEditorViewController.swift; sourceTree = ""; }; + 7A827657226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditPreviewInternalLinkViewController.swift; sourceTree = ""; }; + 7A827658226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EditPreviewInternalLinkViewController.xib; sourceTree = ""; }; + 7A82896621B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormattingDetailTableViewCell.swift; sourceTree = ""; }; + 7A82898A21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormattingCustomViewTableViewCell.swift; sourceTree = ""; }; + 7A8422452268BBE70074648E /* InsertMediaImageInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaImageInfoView.swift; sourceTree = ""; }; + 7A8422462268BBE70074648E /* InsertMediaImageInfoView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InsertMediaImageInfoView.xib; sourceTree = ""; }; + 7A8422512268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaSearchResultPreviewingViewController.swift; sourceTree = ""; }; + 7A8422522268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InsertMediaSearchResultPreviewingViewController.xib; sourceTree = ""; }; + 7A9133A922B162E8002AEBCF /* RemoteNotifications.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = RemoteNotifications.xcdatamodel; sourceTree = ""; }; + 7A9524C922665E6400C55CDC /* InsertMediaSettingsImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaSettingsImageView.swift; sourceTree = ""; }; + 7A9524CA22665E6400C55CDC /* InsertMediaSettingsImageView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InsertMediaSettingsImageView.xib; sourceTree = ""; }; + 7A9524D522669A8B00C55CDC /* InsertMediaSettingsButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaSettingsButtonView.swift; sourceTree = ""; }; + 7A9524D622669A8B00C55CDC /* InsertMediaSettingsButtonView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InsertMediaSettingsButtonView.xib; sourceTree = ""; }; + 7A998AC01FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CollectionViewEditControllerNavigationDelegate+Extensions.swift"; sourceTree = ""; }; + 7A9A611721124CF500403154 /* CreateNewReadingListButtonView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CreateNewReadingListButtonView.xib; sourceTree = ""; }; + 7A9A611D21124D0E00403154 /* CreateNewReadingListButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateNewReadingListButtonView.swift; sourceTree = ""; }; + 7A9F060B2266425700856321 /* InsertMediaSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaSettingsViewController.swift; sourceTree = ""; }; + 7A9F06172266432200856321 /* InsertMediaSettingsTextTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaSettingsTextTableViewCell.swift; sourceTree = ""; }; + 7A9F06182266432200856321 /* InsertMediaSettingsTextTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InsertMediaSettingsTextTableViewCell.xib; sourceTree = ""; }; + 7A9F2775225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaSearchResultsCollectionViewController.swift; sourceTree = ""; }; + 7AA96D5B21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormattingProvidingTableViewController.swift; sourceTree = ""; }; + 7AB209F822FC67B4006FECB4 /* PageHistoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHistoryViewController.swift; sourceTree = ""; }; + 7AB20A0A22FC8432006FECB4 /* PageHistoryCountsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHistoryCountsViewController.swift; sourceTree = ""; }; + 7AB20A0B22FC8432006FECB4 /* PageHistoryCountsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PageHistoryCountsViewController.xib; sourceTree = ""; }; + 7AB6F0FE22AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity.ActivityType+CustomTypes.swift"; sourceTree = ""; }; + 7AB7DEC7227203A600DD61A2 /* InsertMediaViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaViewController.swift; sourceTree = ""; }; + 7AB809CF22675B2300BFAB7C /* ThemeableTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeableTextView.swift; sourceTree = ""; }; + 7AB809DB22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaAdvancedSettingsViewController.swift; sourceTree = ""; }; + 7ABAD6B220338CFA006A364C /* ReadingListDetailUnderBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListDetailUnderBarViewController.swift; sourceTree = ""; }; + 7ABAD6B320338CFB006A364C /* ReadingListDetailUnderBarViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReadingListDetailUnderBarViewController.xib; sourceTree = ""; }; + 7ABAD6BE20349B91006A364C /* Collection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Collection.swift; sourceTree = ""; }; + 7ABE16FE2239B346006BA309 /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = ""; }; + 7ABE170A2239B5A0006BA309 /* WelcomePageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomePageViewController.swift; sourceTree = ""; }; + 7ABE17162239B8EE006BA309 /* WelcomeContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeContainerViewController.swift; sourceTree = ""; }; + 7ABE17172239B8EE006BA309 /* WelcomeContainerViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WelcomeContainerViewController.xib; sourceTree = ""; }; + 7ABE17222239BB54006BA309 /* WelcomePanelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomePanelViewController.swift; sourceTree = ""; }; + 7ABE17232239BB54006BA309 /* WelcomePanelViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WelcomePanelViewController.xib; sourceTree = ""; }; + 7ABE17342239DCF5006BA309 /* WelcomeAnimationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeAnimationViewController.swift; sourceTree = ""; }; + 7ABE173A2239DEF0006BA309 /* WelcomeAnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeAnimationView.swift; sourceTree = ""; }; + 7AC19E312301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHistoryFilterCountsViewController.swift; sourceTree = ""; }; + 7AC19E432301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHistoryFilterCountCollectionViewCell.swift; sourceTree = ""; }; + 7AC19E442301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PageHistoryFilterCountCollectionViewCell.xib; sourceTree = ""; }; + 7AC809C421DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionEditorInputViewsController.swift; sourceTree = ""; }; + 7AC92E65228D8C7B0035E7F0 /* NavigationStateController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationStateController.swift; sourceTree = ""; }; + 7AD5D452223874F600C01164 /* RelatedSearchFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelatedSearchFetcher.swift; sourceTree = ""; }; + 7ADB2A081FD1E8C400B84818 /* BatchEditSelectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BatchEditSelectView.swift; path = ../Wikipedia/Code/BatchEditSelectView.swift; sourceTree = ""; }; + 7ADEAB011FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddArticlesToReadingListViewController.swift; sourceTree = ""; }; + 7ADF497921B45CEE009EA338 /* TextFormattingPlainToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormattingPlainToolbarView.swift; sourceTree = ""; }; + 7ADF497A21B45CEE009EA338 /* TextFormattingPlainToolbarView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TextFormattingPlainToolbarView.xib; sourceTree = ""; }; + 7ADF498521B45E42009EA338 /* TextFormattingGroupedToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormattingGroupedToolbarView.swift; sourceTree = ""; }; + 7ADF498621B45E42009EA338 /* TextFormattingGroupedToolbarView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TextFormattingGroupedToolbarView.xib; sourceTree = ""; }; + 7ADF853523516CF500500ADC /* PageHistoryHintController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHistoryHintController.swift; sourceTree = ""; }; + 7AE1D3321FCD057200393471 /* Saved.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Saved.storyboard; sourceTree = ""; }; + 7AE1D3381FCD10B900393471 /* SavedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedViewController.swift; sourceTree = ""; }; + 7AE1FE2F21B4A9790068BE9F /* TextFormattingButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormattingButtonView.swift; sourceTree = ""; }; + 7AE1FE3021B4A9790068BE9F /* TextFormattingButtonView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TextFormattingButtonView.xib; sourceTree = ""; }; + 7AE5248C21383D9C00CDC817 /* WikidataFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WikidataFetcher.swift; path = Wikipedia/Code/WikidataFetcher.swift; sourceTree = SOURCE_ROOT; }; + 7AE99B2721CC4F420092BE7F /* TextSizeFormattingTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextSizeFormattingTableViewController.swift; sourceTree = ""; }; + 7AE99B2D21CC53AB0092BE7F /* TextFontFormattingTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFontFormattingTableViewController.swift; sourceTree = ""; }; + 7AEBAD442102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSFetchedResultsController+IndexPathValidation.swift"; sourceTree = ""; }; + 7AEC9857219F529000BEF62B /* DefaultEditToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultEditToolbarView.swift; sourceTree = ""; }; + 7AEC9858219F529000BEF62B /* DefaultEditToolbarView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DefaultEditToolbarView.xib; sourceTree = ""; }; + 7AEF527220ADF07100DDF791 /* KeychainCredentialsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainCredentialsManager.swift; sourceTree = ""; }; + 7AF0265422985CB9000E0A06 /* BeKindInputAccessoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeKindInputAccessoryView.swift; sourceTree = ""; }; + 7AF0265522985CB9000E0A06 /* BeKindInputAccessoryView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BeKindInputAccessoryView.xib; sourceTree = ""; }; + 7AF1B19520A1E14F000C8DFE /* ReadingListsFunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListsFunnel.swift; sourceTree = ""; }; + 7AF49F7F204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageAndSyncingSettingsViewController.swift; sourceTree = ""; }; + 7AF56C2E21DDEC1C00563A9C /* TextFormattingProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormattingProviding.swift; sourceTree = ""; }; + 7AF56C3421DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionEditorWebViewMessagingController.swift; sourceTree = ""; }; + 7AF6F76522395BEB00949393 /* EditingWelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditingWelcomeViewController.swift; sourceTree = ""; }; + 7AF8B7402102297A009772CC /* SearchSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsViewController.swift; sourceTree = ""; }; + 7AF8CEEB22653406000B7676 /* InsertMediaSelectedImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertMediaSelectedImageView.swift; sourceTree = ""; }; + 7AFA21B920110D7900E957E7 /* ReadingListHintViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListHintViewController.swift; sourceTree = ""; }; + 7AFA21BA20110D7900E957E7 /* HintViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HintViewController.xib; sourceTree = ""; }; + 7AFC79F721B0367700BB0C50 /* TextFormattingTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFormattingTableViewController.swift; sourceTree = ""; }; + 7AFEB1BB1FA236A100B8DF32 /* UIViewController+Peekable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Peekable.swift"; sourceTree = ""; }; + 7AFEB3F41FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedArticlesCollectionViewCell.swift; sourceTree = ""; }; + 830177F91FBF3E490005681C /* ReadingListsAPIController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ReadingListsAPIController.swift; path = "WMF Framework/ReadingListsAPIController.swift"; sourceTree = SOURCE_ROOT; }; + 830177FB1FBF3EF70005681C /* NSManagedObjectContext+WMFUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+WMFUtilities.swift"; sourceTree = ""; }; + 83023C0420E51DDF00EC7592 /* SearchLanguagesBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchLanguagesBarViewController.swift; sourceTree = ""; usesTabs = 0; }; + 83023C0520E51DDF00EC7592 /* SearchLanguagesBarViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SearchLanguagesBarViewController.xib; sourceTree = ""; }; + 83023C1020E6561900EC7592 /* ViewControllerTransitionsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerTransitionsController.swift; sourceTree = ""; usesTabs = 0; }; + 83023C1E20E6584F00EC7592 /* SearchTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTransition.swift; sourceTree = ""; }; + 8303783F2940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+FormattingToolbarExtension.swift"; sourceTree = ""; }; + 830AD2B824D1D615003EEFE6 /* WebPageUserScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebPageUserScript.swift; sourceTree = ""; }; + 830C0DD423D9AFBE006471C4 /* UIViewController+Push.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Push.swift"; sourceTree = ""; }; + 830C0DD923D9C218006471C4 /* Properties.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = Properties.js; sourceTree = ""; }; + 830D71C21F703C980080078B /* ArticleURLListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleURLListViewController.swift; sourceTree = ""; }; + 830D71CE1F704DD40080078B /* ArticleFetchedResultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleFetchedResultsViewController.swift; sourceTree = ""; }; + 830ECACE1FBDD8C00080B1EF /* ReadingListsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListsViewController.swift; sourceTree = ""; }; + 830ECAD51FBDE77F0080B1EF /* ReadingListsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadingListsTests.swift; sourceTree = ""; }; + 831835301FD1AC490025DD3D /* NavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBar.swift; sourceTree = ""; usesTabs = 0; }; + 831937E623E1CE80006A9FF3 /* String+LinkParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+LinkParsing.swift"; sourceTree = ""; }; + 831937E823E1CEAC006A9FF3 /* CharacterSet+LinkParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+LinkParsing.swift"; sourceTree = ""; }; + 831C15C52099EB3A001B04BF /* WMFArticle+Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WMFArticle+Errors.swift"; sourceTree = ""; }; + 8320332022B90548004A9EDA /* NSManagedObjectContext+NavigationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "NSManagedObjectContext+NavigationState.swift"; path = "WMF Framework/NSManagedObjectContext+NavigationState.swift"; sourceTree = SOURCE_ROOT; }; + 8320332222B906A0004A9EDA /* NavigationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NavigationState.swift; path = "WMF Framework/NavigationState.swift"; sourceTree = SOURCE_ROOT; }; + 8321FCC923871D8F0079F3C7 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Router.swift; path = "WMF Framework/Router.swift"; sourceTree = SOURCE_ROOT; }; + 8321FCCB2387231E0079F3C7 /* ViewControllerRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerRouter.swift; sourceTree = ""; usesTabs = 0; }; + 83222DAD1F8E554800338BE5 /* WMFContent+CoreDataProperties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WMFContent+CoreDataProperties.h"; sourceTree = ""; }; + 83222DAE1F8E554800338BE5 /* WMFContent+CoreDataClass.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WMFContent+CoreDataClass.h"; sourceTree = ""; }; + 83222DAF1F8E554800338BE5 /* WMFContent+CoreDataClass.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "WMFContent+CoreDataClass.m"; sourceTree = ""; }; + 83222DB01F8E554800338BE5 /* WMFContent+CoreDataProperties.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "WMFContent+CoreDataProperties.m"; sourceTree = ""; }; + 832289DA1F7291BA0081A5FB /* SizeThatFitsReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeThatFitsReusableView.swift; sourceTree = ""; }; + 832A7A5A23EA138C00D0A750 /* ArticleViewController+References.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+References.swift"; sourceTree = ""; }; + 832A7A5F23EAE03200D0A750 /* String+JavaScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+JavaScript.swift"; sourceTree = ""; }; + 832B2B8323D9F9420087EB5F /* NSRegularExpression+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+Utilities.swift"; sourceTree = ""; }; + 832BD3BB28996B68002623CA /* VanishAccountContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VanishAccountContentView.swift; sourceTree = ""; }; + 8330531D23EF051900123141 /* NSArray+WMFMapping.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+WMFMapping.m"; sourceTree = ""; }; + 8330531E23EF051900123141 /* NSArray+WMFMapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+WMFMapping.h"; sourceTree = ""; }; + 8330532123EF05D000123141 /* WMFBlocksKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFBlocksKit.swift; sourceTree = ""; }; + 8330532823EF0B4200123141 /* ArticleViewController+Media.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+Media.swift"; sourceTree = ""; usesTabs = 0; }; + 8330532D23EF107D00123141 /* MediaListGalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaListGalleryViewController.swift; sourceTree = ""; usesTabs = 0; }; + 8330533223F0388E00123141 /* DataStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStoreTests.swift; sourceTree = ""; }; + 8336F1422119BD6E000CDE02 /* MediaWikiAcceptLanguageMapping.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = MediaWikiAcceptLanguageMapping.json; sourceTree = ""; }; + 8338AF8B21F7B33E000C4055 /* WMFLegacyFetcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMFLegacyFetcher.h; sourceTree = ""; }; + 8338AF8C21F7B33E000C4055 /* WMFLegacyFetcher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMFLegacyFetcher.m; sourceTree = ""; }; + 833D4FFA20A9E20800B44E7C /* String+HTML.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+HTML.swift"; sourceTree = ""; }; + 833D6B47229EE872003CB650 /* TalkPageTopic+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TalkPageTopic+Extensions.swift"; sourceTree = ""; }; + 834C269B24042DBF00245BE7 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Localizable.strings; sourceTree = ""; }; + 834C269C24042DCF00245BE7 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hr; path = hr.lproj/Localizable.stringsdict; sourceTree = ""; }; + 834C269D240D49F400245BE7 /* ReferenceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReferenceViewController.swift; sourceTree = ""; }; + 834CC34A21075B7600F62818 /* UITabBar+Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITabBar+Theme.swift"; sourceTree = ""; }; + 834F47F32833D91F00F86C80 /* RemoteNotificationFilterType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationFilterType.swift; sourceTree = ""; }; + 8350FC4B20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleLocationAuthorizationCollectionViewCell.swift; sourceTree = ""; }; + 83510B0628F4CF6400B6235B /* TalkPageErrorStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageErrorStateView.swift; sourceTree = ""; }; + 8351CE7720D4424100E32FC1 /* CollectionViewHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewHeader.swift; sourceTree = ""; }; + 8356115C28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSMutableAttributedString+RemoveNewLine.swift"; sourceTree = ""; }; + 8359BAC621E4C9C1009B5E6C /* Fetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fetcher.swift; sourceTree = ""; }; + 835A042C223AD63000D4D758 /* ArticleSummaryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSummaryController.swift; sourceTree = ""; }; + 8361474A24223689003E49D3 /* ArticleViewController+Announcements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+Announcements.swift"; sourceTree = ""; }; + 8361AEC621949665006B00B0 /* ga */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ga; path = ga.lproj/Localizable.strings; sourceTree = ""; }; + 8361AEC721949670006B00B0 /* ga */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ga; path = ga.lproj/Localizable.stringsdict; sourceTree = ""; }; + 8367A27E20D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumnarCollectionViewLayoutManager.swift; sourceTree = ""; }; + 8368BB8324129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+ArticleErrorHandling.swift"; sourceTree = ""; }; + 836BF56B2869EC2600B98321 /* FeatureFlags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = ""; }; + 836BF56D2869F9C200B98321 /* TalkPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageViewController.swift; sourceTree = ""; }; + 83703A7724DC44C600EE98EA /* RemoteNotifications 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "RemoteNotifications 2.xcdatamodel"; sourceTree = ""; }; + 83703A7824DC44CD00EE98EA /* EventLogging 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "EventLogging 2.xcdatamodel"; sourceTree = ""; }; + 837A15F228DA591E00AAC3FC /* TalkPageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageCache.swift; sourceTree = ""; }; + 8380753620DC7481000D222C /* ColumnarCollectionViewLayoutInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumnarCollectionViewLayoutInfo.swift; sourceTree = ""; }; + 8380753820DC7684000D222C /* ColumarCollectionViewLayoutSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumarCollectionViewLayoutSection.swift; sourceTree = ""; }; + 8380753A20DC7D04000D222C /* ColumnarCollectionViewLayoutMetrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumnarCollectionViewLayoutMetrics.swift; sourceTree = ""; }; + 8380754420DE627D000D222C /* WMFContentGroup+Display.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WMFContentGroup+Display.swift"; sourceTree = ""; }; + 8382F8C020D8431000AE5250 /* ArticleLocationCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleLocationCollectionViewCell.swift; sourceTree = ""; }; + 8382F8CC20D9206000AE5250 /* ImageCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCollectionViewCell.swift; sourceTree = ""; }; + 8382F8D220D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumnarCollectionViewControllerLayoutCache.swift; sourceTree = ""; }; + 8382F8D820D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WMFContentGroup+DetailViewControllers.swift"; sourceTree = ""; usesTabs = 0; }; + 8383446B1F62EBD000BD5A37 /* UIView+Constraints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Constraints.swift"; sourceTree = ""; }; + 83836ECA1F615E5B007D1A05 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; + 83836ECB1F615E5B007D1A05 /* ShareViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShareViewController.xib; sourceTree = ""; }; + 8386BDE623857F87007EE89D /* URLParsingAndRoutingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLParsingAndRoutingTests.swift; sourceTree = ""; }; + 8386BDEE2386CAAB007EE89D /* ViewController+URLHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+URLHandling.swift"; sourceTree = ""; }; + 8386BDF02386D3E1007EE89D /* RequestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestError.swift; sourceTree = ""; }; + 8386BDFA2386D754007EE89D /* SinglePageWebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SinglePageWebViewController.swift; sourceTree = ""; }; + 838790B22858009000067B1D /* TalkPageFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageFetcher.swift; sourceTree = ""; }; + 8387CE8624C8C6CF00439D93 /* Wikipedia 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Wikipedia 4.xcdatamodel"; sourceTree = ""; }; + 8387CE8724C8C70A00439D93 /* WMFSecureUnarchiveFromDataTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMFSecureUnarchiveFromDataTransformer.swift; sourceTree = ""; }; + 8387CE8D24C99C2600439D93 /* WMFMTLModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMFMTLModel.h; sourceTree = ""; }; + 8387CE8E24C99C2600439D93 /* WMFMTLModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMFMTLModel.m; sourceTree = ""; }; + 83927D7A1F70570400051890 /* DisambiguationPagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisambiguationPagesViewController.swift; sourceTree = ""; }; + 83927D801F705B7B00051890 /* SearchResultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsViewController.swift; sourceTree = ""; }; + 8392E8671F557FC0007E2EE2 /* NSTextAttachment+WMFExtras.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextAttachment+WMFExtras.swift"; sourceTree = ""; }; + 8397601A2811ABC7000FE3B1 /* UNAuthorizationStatus+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNAuthorizationStatus+String.swift"; sourceTree = ""; }; + 83987ACF20E4FB2C00C92C60 /* UISearchBar+Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UISearchBar+Theme.swift"; sourceTree = ""; }; + 83A1561320DBE08C0052487B /* ColumnarCollectionViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumnarCollectionViewLayout.swift; sourceTree = ""; }; + 83A422B422786E2000BA446A /* Local-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Local-Info.plist"; sourceTree = ""; }; + 83A642742226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftKVOCrashWorkaround.swift; sourceTree = ""; }; + 83A6D44225100BEE00F9F909 /* Bundle+IsAppExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+IsAppExtension.swift"; sourceTree = ""; }; + 83A72BBE24E70BB200732493 /* localization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = localization.swift; path = "Command Line Tools/Update Localizations/localization.swift"; sourceTree = SOURCE_ROOT; }; + 83A8E33F21A431F100B3FF82 /* WMFLegacySerializer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WMFLegacySerializer.h; path = "WMF Framework/WMFLegacySerializer.h"; sourceTree = SOURCE_ROOT; }; + 83A8E34021A431F100B3FF82 /* WMFLegacySerializer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = WMFLegacySerializer.m; path = "WMF Framework/WMFLegacySerializer.m"; sourceTree = SOURCE_ROOT; }; + 83A933442514C491006EB48A /* WMFCrossProcessCoreDataSynchronizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMFCrossProcessCoreDataSynchronizer.h; sourceTree = ""; }; + 83A933452514C491006EB48A /* WMFCrossProcessCoreDataSynchronizer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMFCrossProcessCoreDataSynchronizer.m; sourceTree = ""; }; + 83ACAA9D24E6D94C003B3035 /* MWKSearchResult+PageNamespace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MWKSearchResult+PageNamespace.swift"; sourceTree = ""; }; + 83ACAAA124E6E38A003B3035 /* Wikipedia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wikipedia.swift; sourceTree = ""; }; + 83ACAAA324E6E42A003B3035 /* wikipedia-languages.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "wikipedia-languages.json"; sourceTree = ""; }; + 83ACAAA624E6E655003B3035 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = main.swift; path = "Command Line Tools/Update Localizations/main.swift"; sourceTree = SOURCE_ROOT; }; + 83ACAAAA24E6E745003B3035 /* WikipediaLookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WikipediaLookup.swift; sourceTree = ""; }; + 83ACAAAC24E6EED9003B3035 /* WikipediaSiteInfoLookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WikipediaSiteInfoLookup.swift; sourceTree = ""; }; + 83ACF8E428E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedContainerCacheHousekeeping.swift; sourceTree = ""; }; + 83AF34F624D3341D000046D6 /* BackgroundTasks.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BackgroundTasks.framework; path = System/Library/Frameworks/BackgroundTasks.framework; sourceTree = SDKROOT; }; + 83B019CD24F6ACAA0014B5EF /* WikipediaLanguageCommandLineUtility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WikipediaLanguageCommandLineUtility.swift; sourceTree = ""; }; + 83B019CE24F6ACAA0014B5EF /* WikipediaLanguageCommandLineUtilityAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WikipediaLanguageCommandLineUtilityAPI.swift; sourceTree = ""; }; + 83B019CF24F6ACAA0014B5EF /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; + 83B019D524F6C31B0014B5EF /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk/System/iOSSupport/System/Library/Frameworks/WidgetKit.framework; sourceTree = DEVELOPER_DIR; }; + 83B01F7123DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+ArticleWebMessageHandling.swift"; sourceTree = ""; usesTabs = 0; }; + 83B01F7623DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+ArticleToolbarHandling.swift"; sourceTree = ""; }; + 83B01F7B23DB0BA2001185F4 /* ArticleViewController+Editing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+Editing.swift"; sourceTree = ""; }; + 83B01F8023DB1235001185F4 /* SectionFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionFetcher.swift; sourceTree = ""; }; + 83B01F8A23DB399E001185F4 /* DescriptionEditViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DescriptionEditViewController.storyboard; sourceTree = ""; }; + 83B01F8F23DB41BE001185F4 /* ArticleViewController+Sharing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+Sharing.swift"; sourceTree = ""; }; + 83B01F9423DB41D7001185F4 /* ArticleViewController+FindInPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+FindInPage.swift"; sourceTree = ""; }; + 83B01F9923DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+ArticleInformation.swift"; sourceTree = ""; }; + 83B1218327FC8750006B8CCC /* RemoteNotificationsFunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteNotificationsFunnel.swift; sourceTree = ""; }; + 83B4CDBE20E3DCD6007D5A6E /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = ""; }; + 83B87EC61F713BC200F342F1 /* ArticleCollectionViewCell+ListDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleCollectionViewCell+ListDisplay.swift"; sourceTree = ""; }; + 83BBBE5523F56F9400AD0994 /* LocaleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocaleTests.swift; sourceTree = ""; }; + 83C0656A23D23220001821BC /* TableOfContentsItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableOfContentsItem.swift; sourceTree = ""; }; + 83C06881292EC85700DF1403 /* TalkPageFormattingToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageFormattingToolbarView.swift; sourceTree = ""; }; + 83C06886292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TalkPageFormattingToolbarViewDelegate.swift; sourceTree = ""; }; + 83C0688D292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TalkPageViewController+TalkPageFormattingToolbar.swift"; sourceTree = ""; }; + 83C06892292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift"; sourceTree = ""; }; + 83C6435222394F0300FC16BF /* RandomArticleFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomArticleFetcher.swift; sourceTree = ""; }; + 83CA612920D1675800EF0C4A /* ExploreCardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreCardViewController.swift; sourceTree = ""; }; + 83CCB287209CA4E600D31565 /* NSRegularExpression+HTML.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSRegularExpression+HTML.h"; sourceTree = ""; }; + 83CCB288209CA4E600D31565 /* NSRegularExpression+HTML.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSRegularExpression+HTML.m"; sourceTree = ""; }; + 83CDC7D325122A1700A2F8A1 /* PermanentCacheController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermanentCacheController.swift; sourceTree = ""; }; + 83CF71432326D47E009DEC00 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Localizable.strings; sourceTree = ""; }; + 83CF71442326D48A009DEC00 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sl; path = sl.lproj/Localizable.stringsdict; sourceTree = ""; }; + 83D05188246EA70D00DA92C6 /* NSMutableAttributedString+Mutations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSMutableAttributedString+Mutations.swift"; sourceTree = ""; }; + 83D3FC12223A8BCD0048384B /* ArticleSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSummary.swift; sourceTree = ""; }; + 83D5EC861F755E1F003DE6F2 /* SwipeableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeableCell.swift; sourceTree = ""; }; + 83DAA9AF23FEB611002D5716 /* ReferenceBackLinksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReferenceBackLinksViewController.swift; sourceTree = ""; }; + 83DB0A5623EEDE2100DA5F58 /* MobileviewToMobileHTMLConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MobileviewToMobileHTMLConverter.swift; sourceTree = ""; }; + 83DB0A5D23EEDE4400DA5F58 /* LegacyArticle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyArticle.swift; sourceTree = ""; }; + 83DB440F244A57590046FABE /* RootNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootNavigationController.swift; sourceTree = ""; }; + 83DE45B72449C09B00671878 /* SplashScreenViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenViewController.swift; sourceTree = ""; }; + 83DF1D1324F53878007E08D8 /* WMFPreferredLanguageInfoProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMFPreferredLanguageInfoProvider.h; sourceTree = ""; }; + 83E3E7242440F1FE00AA2E9A /* LoadingAnimationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingAnimationViewController.swift; sourceTree = ""; }; + 83E3E7292440F24300AA2E9A /* LoadingAnimationViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoadingAnimationViewController.xib; sourceTree = ""; }; + 83E52BB21F681F940045E776 /* ShareAFactViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAFactViewController.swift; sourceTree = ""; }; + 83E52BB31F681F940045E776 /* ShareAFactViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShareAFactViewController.xib; sourceTree = ""; }; + 83E52BBE1F682E3E0045E776 /* LicenseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicenseView.swift; sourceTree = ""; }; + 83E776A220FFA4D700E26A47 /* DetailTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailTransition.swift; sourceTree = ""; }; + 83E880E723EB19270087223F /* MediaList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaList.swift; sourceTree = ""; }; + 83E9A2111F56FE5E006EB091 /* FakeProgressController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeProgressController.swift; sourceTree = ""; }; + 83E9C45A2419193C006BDBC2 /* WikipediaSiteInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WikipediaSiteInfo.swift; path = "WMF Framework/WikipediaSiteInfo.swift"; sourceTree = SOURCE_ROOT; }; + 83ED2E23289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VanishAccountCustomUIHostingController.swift; sourceTree = ""; }; + 83EDC4C028B424B5007D0192 /* VanishAccountPopUpAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VanishAccountPopUpAlertView.swift; sourceTree = ""; }; + 83EE476920D019A100A21F34 /* ExploreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreViewController.swift; sourceTree = ""; usesTabs = 0; }; + 83EE476F20D01A9A00A21F34 /* ExploreCardCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreCardCollectionViewCell.swift; sourceTree = ""; }; + 83F1095623D07E3B003F3E9E /* APIURLComponentsBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIURLComponentsBuilder.swift; sourceTree = ""; }; + 83F1095E23D09F80003F3E9E /* ArticleViewController+WIconPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+WIconPopover.swift"; sourceTree = ""; }; + 83F1096323D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticlePreviewingDelegate.swift; sourceTree = ""; }; + 83F1096823D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+ArticlePreviewing.swift"; sourceTree = ""; }; + 83F1096D23D0E787003F3E9E /* RandomArticleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomArticleViewController.swift; sourceTree = ""; }; + 83F1097223D0F115003F3E9E /* HelpViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpViewController.swift; sourceTree = ""; usesTabs = 0; }; + 83F26B29220B62EC002D87A4 /* SectionEditorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionEditorButton.swift; sourceTree = ""; }; + 83FBE96E1F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAFactActivityTextItemProvider.swift; sourceTree = ""; }; + 83FBE9741F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAFactActivityImageItemProvider.swift; sourceTree = ""; }; + 83FDE798293564AC006D55FE /* Link.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Link.swift; sourceTree = ""; }; + 982800D524D302BF004B1850 /* EventPlatformClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventPlatformClient.swift; sourceTree = ""; }; + A452F9F624081A5500D8ED09 /* MockCLLocationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockCLLocationManager.swift; sourceTree = ""; }; + A452F9F724081A5500D8ED09 /* MockCLHeading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockCLHeading.swift; sourceTree = ""; }; + A452F9FA24081A7200D8ED09 /* LocationManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationManagerTests.swift; sourceTree = ""; }; + A452F9FC24081B0200D8ED09 /* MockUIDevice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockUIDevice.swift; sourceTree = ""; }; + A4C558BC2403D74100AFBFDC /* LocationManagerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationManagerProtocol.swift; sourceTree = ""; }; + A4C558BE2403D7E200AFBFDC /* LocationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = ""; }; + B00050131C52D73800515F70 /* UIApplication+RTL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIApplication+RTL.swift"; sourceTree = ""; }; + B0016CB821354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoLayoutSafeMultiLineButton.swift; sourceTree = ""; }; + B0016CBE2136105900FA1096 /* SetupButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupButton.swift; sourceTree = ""; }; + B0016CC221362DB000FA1096 /* SetupGradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetupGradientView.swift; sourceTree = ""; }; + B00DDEDA1DB4B76B00615FA2 /* UIView+WMFSubviews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+WMFSubviews.swift"; path = "Wikipedia/Code/UIView+WMFSubviews.swift"; sourceTree = SOURCE_ROOT; }; + B00DDEDC1DB591C400615FA2 /* WMFWelcomeContainerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomeContainerViewController.swift; path = Wikipedia/Code/WMFWelcomeContainerViewController.swift; sourceTree = SOURCE_ROOT; }; + B00DDEE21DB5B16E00615FA2 /* WMFWelcomeAnimationViewControllers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomeAnimationViewControllers.swift; path = Wikipedia/Code/WMFWelcomeAnimationViewControllers.swift; sourceTree = SOURCE_ROOT; }; + B010E1A71E723E3600CFE1CD /* WMFAuthLinkLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFAuthLinkLabel.swift; sourceTree = ""; }; + B01490A01DB96BD6007F5391 /* WMFReferencePanels.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = WMFReferencePanels.storyboard; path = Wikipedia/Code/WMFReferencePanels.storyboard; sourceTree = SOURCE_ROOT; }; + B01490A21DB96E5F007F5391 /* WMFReferencePageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFReferencePageViewController.swift; path = Wikipedia/Code/WMFReferencePageViewController.swift; sourceTree = SOURCE_ROOT; }; + B01490A41DB96EA7007F5391 /* WMFReferencePanelViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFReferencePanelViewController.swift; path = Wikipedia/Code/WMFReferencePanelViewController.swift; sourceTree = SOURCE_ROOT; }; + B01662AE1D1B8997006F4544 /* NSURL+WMFQueryParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSURL+WMFQueryParameters.h"; path = "Wikipedia/Code/NSURL+WMFQueryParameters.h"; sourceTree = SOURCE_ROOT; }; + B01662AF1D1B8997006F4544 /* NSURL+WMFQueryParameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSURL+WMFQueryParameters.m"; path = "Wikipedia/Code/NSURL+WMFQueryParameters.m"; sourceTree = SOURCE_ROOT; }; + B01662B11D1B8A40006F4544 /* NSURL+WMFQueryParametersTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURL+WMFQueryParametersTests.m"; sourceTree = ""; }; + B019FECE2029347200BDE9C9 /* UIStackView+SubviewVerification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStackView+SubviewVerification.swift"; sourceTree = ""; }; + B01CFC5E1E70B00100B3546A /* WMFDeleteBackwardReportingTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFDeleteBackwardReportingTextField.swift; path = Wikipedia/Code/WMFDeleteBackwardReportingTextField.swift; sourceTree = SOURCE_ROOT; }; + B01CFC601E71069000B3546A /* String?+WMFExtras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "String?+WMFExtras.swift"; path = "Wikipedia/Code/String?+WMFExtras.swift"; sourceTree = SOURCE_ROOT; }; + B01E3AF821F986750015B715 /* PreviewWebViewContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewWebViewContainer.swift; sourceTree = ""; }; + B01E3AFE21F98BFF0015B715 /* EditPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditPreviewViewController.swift; sourceTree = ""; }; + B01E54AE206479CC00374FEE /* ProgressContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressContainer.swift; sourceTree = ""; }; + B01EA07D2022856200813726 /* ScrollableEducationPanelView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ScrollableEducationPanelView.xib; sourceTree = ""; }; + B02376B21D6ECCF9007DBA73 /* UIViewController+WMFDynamicHeightPopoverMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+WMFDynamicHeightPopoverMessage.h"; sourceTree = ""; }; + B02376B31D6ECCF9007DBA73 /* UIViewController+WMFDynamicHeightPopoverMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+WMFDynamicHeightPopoverMessage.m"; sourceTree = ""; }; + B0267CE81E316A79006B6D8D /* WMFForgotPasswordViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = WMFForgotPasswordViewController.storyboard; sourceTree = ""; }; + B0267CEC1E316AE3006B6D8D /* WMFForgotPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFForgotPasswordViewController.swift; sourceTree = ""; }; + B0267CF21E32A0CB006B6D8D /* WMFPasswordResetter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFPasswordResetter.swift; sourceTree = ""; }; + B027447C1E6253E200E7B248 /* WMFScrollViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFScrollViewController.swift; sourceTree = ""; }; + B027447E1E665D9F00E7B248 /* UIViewController+WMFChildViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIViewController+WMFChildViewController.swift"; path = "Wikipedia/Code/UIViewController+WMFChildViewController.swift"; sourceTree = SOURCE_ROOT; }; + B027FD271E678F5C005644A9 /* WMFAuthButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFAuthButton.swift; sourceTree = ""; }; + B02B82721C696ECA00B19309 /* WMFSettingsTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFSettingsTableViewCell.h; path = Wikipedia/Code/WMFSettingsTableViewCell.h; sourceTree = SOURCE_ROOT; }; + B02B82731C696ECA00B19309 /* WMFSettingsTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFSettingsTableViewCell.m; path = Wikipedia/Code/WMFSettingsTableViewCell.m; sourceTree = SOURCE_ROOT; }; + B02B82741C696ECA00B19309 /* WMFSettingsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = WMFSettingsTableViewCell.xib; path = Wikipedia/Code/WMFSettingsTableViewCell.xib; sourceTree = SOURCE_ROOT; }; + B02B827A1C698FAB00B19309 /* WMFSettingsMenuItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WMFSettingsMenuItem.h; path = Wikipedia/Code/WMFSettingsMenuItem.h; sourceTree = SOURCE_ROOT; }; + B02F96651DFA11DC007DA007 /* WMFArticleListTableViewCell+DynamicTypeFontTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WMFArticleListTableViewCell+DynamicTypeFontTests.swift"; sourceTree = ""; }; + B03103261F677BB300E2FCF6 /* WMFWelcomeExplorationAnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WMFWelcomeExplorationAnimationView.swift; path = Wikipedia/Code/WMFWelcomeExplorationAnimationView.swift; sourceTree = SOURCE_ROOT; }; + B03103271F677BB400E2FCF6 /* WMFWelcomeExplorationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WMFWelcomeExplorationViewController.swift; path = Wikipedia/Code/WMFWelcomeExplorationViewController.swift; sourceTree = SOURCE_ROOT; }; + B0338A831DBF0AF20012F588 /* WMFReferencePageBackgroundView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFReferencePageBackgroundView.swift; path = Wikipedia/Code/WMFReferencePageBackgroundView.swift; sourceTree = SOURCE_ROOT; }; + B0379A291D8B756C00D973CF /* WMFReferencePopoverMessageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFReferencePopoverMessageViewController.h; path = Wikipedia/Code/WMFReferencePopoverMessageViewController.h; sourceTree = SOURCE_ROOT; }; + B0379A2A1D8B756C00D973CF /* WMFReferencePopoverMessageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFReferencePopoverMessageViewController.m; path = Wikipedia/Code/WMFReferencePopoverMessageViewController.m; sourceTree = SOURCE_ROOT; }; + B0379A361D8B9D2400D973CF /* WMFReferencePopoverMessageViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = WMFReferencePopoverMessageViewController.storyboard; path = Wikipedia/Code/WMFReferencePopoverMessageViewController.storyboard; sourceTree = SOURCE_ROOT; }; + B04034371F059243001B837B /* OnThisDayExploreCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OnThisDayExploreCollectionViewCell.swift; path = Wikipedia/Code/OnThisDayExploreCollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; + B040343D1F0592F7001B837B /* OnThisDayCollectionViewCell+WMFFeedContentDisplaying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "OnThisDayCollectionViewCell+WMFFeedContentDisplaying.swift"; path = "Wikipedia/Code/OnThisDayCollectionViewCell+WMFFeedContentDisplaying.swift"; sourceTree = SOURCE_ROOT; }; + B040343E1F0592F7001B837B /* OnThisDayExploreCollectionViewCell+WMFFeedContentDisplaying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "OnThisDayExploreCollectionViewCell+WMFFeedContentDisplaying.swift"; path = "Wikipedia/Code/OnThisDayExploreCollectionViewCell+WMFFeedContentDisplaying.swift"; sourceTree = SOURCE_ROOT; }; + B04034491F0722B3001B837B /* NewsCollectionViewCell+WMFFeedContentDisplaying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NewsCollectionViewCell+WMFFeedContentDisplaying.swift"; path = "Wikipedia/Code/NewsCollectionViewCell+WMFFeedContentDisplaying.swift"; sourceTree = SOURCE_ROOT; }; + B0408C542127F2C100AC76CE /* WMFImageGalleryGradientViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMFImageGalleryGradientViews.swift; sourceTree = ""; }; + B0421AA1206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedTabBarItemProgressBadgeManager.swift; sourceTree = ""; }; + B0432343210680A800A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WMFContentGroupKind+FeedCustomization.swift"; sourceTree = ""; }; + B04AE84B21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKWebViewWithSettableInputViews.swift; sourceTree = ""; }; + B04C444A1E56966B00C6DFB0 /* Array+WMFAllFieldsFilled.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Array+WMFAllFieldsFilled.swift"; path = "Wikipedia/Code/Array+WMFAllFieldsFilled.swift"; sourceTree = SOURCE_ROOT; }; + B0501BBC2110ED8800020BFA /* FeedFunnel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedFunnel.swift; sourceTree = ""; }; + B0524AEF2144D7BE00D8FD8D /* DescriptionHelpViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionHelpViewController.swift; sourceTree = ""; }; + B0524AF02144D7BE00D8FD8D /* DescriptionHelpViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DescriptionHelpViewController.xib; sourceTree = ""; }; + B0524B0221484FB400D8FD8D /* DescriptionWelcome.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DescriptionWelcome.storyboard; sourceTree = ""; }; + B0524B09214854E500D8FD8D /* DescriptionWelcomeInitialViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DescriptionWelcomeInitialViewController.swift; sourceTree = ""; }; + B0524B0B214854E600D8FD8D /* DescriptionWelcomePanelViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DescriptionWelcomePanelViewController.swift; sourceTree = ""; }; + B0524B11214854E700D8FD8D /* DescriptionWelcomePageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DescriptionWelcomePageViewController.swift; sourceTree = ""; }; + B0524B13214854E700D8FD8D /* DescriptionWelcomeContainerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DescriptionWelcomeContainerViewController.swift; sourceTree = ""; }; + B0524B17214854E800D8FD8D /* DescriptionWelcomeImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DescriptionWelcomeImageViewController.swift; sourceTree = ""; }; + B0524B19214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DescriptionWelcomeContentsViewController.swift; sourceTree = ""; }; + B0524B74214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+DescriptionWelcomeStoryboard.swift"; sourceTree = ""; }; + B0606EAE20AA6FF0006EC6B9 /* WikipediaUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WikipediaUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B0606EB020AA6FF0006EC6B9 /* SnapshotRecorderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotRecorderTests.swift; sourceTree = ""; }; + B0606EB220AA6FF0006EC6B9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B0606EC420AA955B006EC6B9 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = ""; }; + B066F0D11E4F00B100A199F8 /* WMFKeychainCredentials.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFKeychainCredentials.swift; path = Wikipedia/Code/WMFKeychainCredentials.swift; sourceTree = SOURCE_ROOT; }; + B066F0D41E513DAA00A199F8 /* UIViewController+WMFHideKeyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIViewController+WMFHideKeyboard.swift"; path = "Wikipedia/Code/UIViewController+WMFHideKeyboard.swift"; sourceTree = SOURCE_ROOT; }; + B068EDDF206B183500C827D1 /* Progress+ProgressUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Progress+ProgressUI.swift"; sourceTree = ""; }; + B069FA2D1CEACB8400083D59 /* WeakScriptMessageDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WeakScriptMessageDelegate.swift; path = Wikipedia/Code/WeakScriptMessageDelegate.swift; sourceTree = SOURCE_ROOT; }; + B077A51323861E2200223526 /* wikipedia-namespaces */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "wikipedia-namespaces"; sourceTree = ""; }; + B083371D1DADB251002860D2 /* WMFWelcomePageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomePageViewController.swift; path = Wikipedia/Code/WMFWelcomePageViewController.swift; sourceTree = SOURCE_ROOT; }; + B083375C1DB16A09002860D2 /* WMFWelcomePanelViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomePanelViewController.swift; path = Wikipedia/Code/WMFWelcomePanelViewController.swift; sourceTree = SOURCE_ROOT; }; + B083375E1DB17DA5002860D2 /* WMFWelcomeLanguageTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomeLanguageTableViewController.swift; path = Wikipedia/Code/WMFWelcomeLanguageTableViewController.swift; sourceTree = SOURCE_ROOT; }; + B08337601DB17DC5002860D2 /* WMFWelcomeAnalyticsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomeAnalyticsViewController.swift; path = Wikipedia/Code/WMFWelcomeAnalyticsViewController.swift; sourceTree = SOURCE_ROOT; }; + B08423DD2384E2C7005E93A0 /* WikipediaURLTranslations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WikipediaURLTranslations.swift; sourceTree = ""; }; + B0845E0F20618DA400CDD98E /* SavedProgressViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedProgressViewController.swift; sourceTree = ""; }; + B0845E1B2061B44A00CDD98E /* SavedProgressViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SavedProgressViewController.storyboard; sourceTree = ""; }; + B085536B2399E368002100F8 /* UIAccessibility+Grouping.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIAccessibility+Grouping.swift"; path = "Wikipedia/Code/UIAccessibility+Grouping.swift"; sourceTree = SOURCE_ROOT; }; + B08624301F72EA1900B770FD /* WMFWelcomeAnimationBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMFWelcomeAnimationBackgroundView.swift; sourceTree = ""; }; + B0866F431CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFArticleLanguagesSectionFooter.h; sourceTree = ""; }; + B0866F441CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFArticleLanguagesSectionFooter.m; sourceTree = ""; }; + B0866F451CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WMFArticleLanguagesSectionFooter.xib; sourceTree = ""; }; + B087F8D023E3AE3B00ACA012 /* MWKDataStore+LegacyMobileview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MWKDataStore+LegacyMobileview.swift"; sourceTree = ""; }; + B08E7E991C1FA57A00EC3C99 /* UIViewController+WMFEmptyView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+WMFEmptyView.h"; path = "Wikipedia/Code/UIViewController+WMFEmptyView.h"; sourceTree = SOURCE_ROOT; }; + B08E7E9A1C1FA57A00EC3C99 /* UIViewController+WMFEmptyView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+WMFEmptyView.m"; path = "Wikipedia/Code/UIViewController+WMFEmptyView.m"; sourceTree = SOURCE_ROOT; }; + B09705B3236B29D7006FDB5C /* DiffThanker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiffThanker.swift; sourceTree = ""; }; + B09B03E91CE0FB2600009083 /* WMFPageHistoryRevision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFPageHistoryRevision.h; path = Wikipedia/Code/WMFPageHistoryRevision.h; sourceTree = SOURCE_ROOT; }; + B09B03EA1CE0FB2600009083 /* WMFPageHistoryRevision.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFPageHistoryRevision.m; path = Wikipedia/Code/WMFPageHistoryRevision.m; sourceTree = SOURCE_ROOT; }; + B09B03EC1CE0FB4200009083 /* PageHistorySection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PageHistorySection.swift; path = Wikipedia/Code/PageHistorySection.swift; sourceTree = SOURCE_ROOT; }; + B09B03F11CE0FB6300009083 /* PageHistoryFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = PageHistoryFetcher.swift; path = Wikipedia/Code/PageHistoryFetcher.swift; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + B09B03F31CE0FB7700009083 /* ReadingThemesControlsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ReadingThemesControlsViewController.swift; path = Wikipedia/Code/ReadingThemesControlsViewController.swift; sourceTree = SOURCE_ROOT; }; + B09B03F41CE0FB7700009083 /* ReadingThemesControlsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ReadingThemesControlsViewController.xib; path = Wikipedia/Code/ReadingThemesControlsViewController.xib; sourceTree = SOURCE_ROOT; }; + B09B30CE1DB813760012281F /* UIViewController+WMFStoryboardUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIViewController+WMFStoryboardUtilities.swift"; path = "Wikipedia/Code/UIViewController+WMFStoryboardUtilities.swift"; sourceTree = SOURCE_ROOT; }; + B09B30D01DB817660012281F /* UIViewController+WMFWelcomeStoryboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIViewController+WMFWelcomeStoryboard.swift"; path = "Wikipedia/Code/UIViewController+WMFWelcomeStoryboard.swift"; sourceTree = SOURCE_ROOT; }; + B09BE6A01FB3DA45007F52E3 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; + B09CE599222F623800067D2A /* WKWebView+EditSelectionJavascript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WKWebView+EditSelectionJavascript.swift"; sourceTree = ""; }; + B0ACB13221265B930078C136 /* WMFImageGalleryDescriptionTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMFImageGalleryDescriptionTextView.swift; sourceTree = ""; }; + B0B0EC211C6999A9006F0D9C /* WMFSettingsMenuItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFSettingsMenuItem.m; path = Wikipedia/Code/WMFSettingsMenuItem.m; sourceTree = SOURCE_ROOT; }; + B0B423451EF1FEE000D3DC4C /* WMFFeedOnThisDayEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFFeedOnThisDayEvent.h; sourceTree = ""; }; + B0B423461EF1FEE000D3DC4C /* WMFFeedOnThisDayEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFFeedOnThisDayEvent.m; sourceTree = ""; }; + B0B4234A1EF2055200D3DC4C /* WMFOnThisDayEventsFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFOnThisDayEventsFetcher.h; sourceTree = ""; }; + B0B4234B1EF2055200D3DC4C /* WMFOnThisDayEventsFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFOnThisDayEventsFetcher.m; sourceTree = ""; }; + B0B4234E1EF32D2700D3DC4C /* WMFOnThisDayContentSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFOnThisDayContentSource.h; sourceTree = ""; }; + B0B4234F1EF32D2700D3DC4C /* WMFOnThisDayContentSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFOnThisDayContentSource.m; sourceTree = ""; }; + B0B423521EF47DCD00D3DC4C /* SideScrollingCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SideScrollingCollectionViewCell.swift; path = Wikipedia/Code/SideScrollingCollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; + B0B423591EF4845500D3DC4C /* OnThisDayCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OnThisDayCollectionViewCell.swift; path = Wikipedia/Code/OnThisDayCollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; + B0B423601EF9D69C00D3DC4C /* OnThisDayViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnThisDayViewController.swift; sourceTree = ""; }; + B0B423661EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnThisDayViewControllerHeader.swift; sourceTree = ""; }; + B0B423671EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OnThisDayViewControllerHeader.xib; sourceTree = ""; }; + B0B423781F0211A000D3DC4C /* WMFFeedArticlePreview+DescriptionOrSnippet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WMFFeedArticlePreview+DescriptionOrSnippet.swift"; sourceTree = ""; }; + B0B4CF081CC0500E0051DF7A /* WMFArticleLanguagesSectionHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFArticleLanguagesSectionHeader.h; sourceTree = ""; }; + B0B4CF091CC0500E0051DF7A /* WMFArticleLanguagesSectionHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFArticleLanguagesSectionHeader.m; sourceTree = ""; }; + B0B4CF0B1CC0501B0051DF7A /* WMFArticleLanguagesSectionHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WMFArticleLanguagesSectionHeader.xib; sourceTree = ""; }; + B0BCF0AA2023AC7700986F72 /* ScrollableEducationPanelViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollableEducationPanelViewController.swift; sourceTree = ""; }; + B0BCF0B8202537D800986F72 /* Panels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Panels.swift; sourceTree = ""; }; + B0BDA58120B09A090098DB65 /* XCUIApplication+SnapshotUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCUIApplication+SnapshotUtilities.swift"; sourceTree = ""; }; + B0C06B9E218240CA00E481CC /* Collection+AsyncMapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+AsyncMapTests.swift"; sourceTree = ""; }; + B0C6BE3F1E4068C60033BD6E /* WMFLoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFLoginViewController.swift; path = Wikipedia/Code/WMFLoginViewController.swift; sourceTree = SOURCE_ROOT; }; + B0C6BE411E413B3F0033BD6E /* WMFAccountCreationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFAccountCreationViewController.swift; path = Wikipedia/Code/WMFAccountCreationViewController.swift; sourceTree = SOURCE_ROOT; }; + B0C6BE471E428C940033BD6E /* WMFAccountCreator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFAccountCreator.swift; sourceTree = ""; }; + B0C6BE491E42D19D0033BD6E /* WMFCaptchaViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFCaptchaViewController.swift; path = "WMF Framework/WMFCaptchaViewController.swift"; sourceTree = SOURCE_ROOT; }; + B0C6BE521E4526810033BD6E /* WMFChangePasswordViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = WMFChangePasswordViewController.storyboard; path = Wikipedia/Code/WMFChangePasswordViewController.storyboard; sourceTree = SOURCE_ROOT; }; + B0C6BE561E4526A40033BD6E /* WMFChangePasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFChangePasswordViewController.swift; path = Wikipedia/Code/WMFChangePasswordViewController.swift; sourceTree = SOURCE_ROOT; }; + B0C7A0781F710E74008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMFWelcomeExplorationAnimationBackgroundView.swift; sourceTree = ""; }; + B0C7A07E1F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMFWelcomeLanguagesAnimationBackgroundView.swift; sourceTree = ""; }; + B0C7A0841F710EB0008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMFWelcomeAnalyticsAnimationBackgroundView.swift; sourceTree = ""; }; + B0D1B4591DDD02BB004FCAE6 /* WMFDynamicTypeExtentions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFDynamicTypeExtentions.swift; path = Wikipedia/Code/WMFDynamicTypeExtentions.swift; sourceTree = SOURCE_ROOT; }; + B0D3E70A214AF776007578BA /* DescriptionEditViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DescriptionEditViewController.swift; sourceTree = ""; }; + B0D4916E21F999A3002BBDD3 /* EditSaveViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSaveViewController.swift; sourceTree = ""; }; + B0D530EA1CE151C10078BAED /* CodeFileLocationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CodeFileLocationTests.m; sourceTree = ""; }; + B0DE92261D6E26C700EC76A7 /* WMFBarButtonItemPopoverMessageViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = WMFBarButtonItemPopoverMessageViewController.storyboard; sourceTree = ""; }; + B0DE92281D6E272B00EC76A7 /* WMFBarButtonItemPopoverMessageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFBarButtonItemPopoverMessageViewController.h; sourceTree = ""; }; + B0DE92291D6E272B00EC76A7 /* WMFBarButtonItemPopoverMessageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFBarButtonItemPopoverMessageViewController.m; sourceTree = ""; }; + B0DF6F7F1CFE1D0B0046E507 /* WKWebView+WMFWebViewControllerJavascript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "WKWebView+WMFWebViewControllerJavascript.h"; path = "Wikipedia/Code/WKWebView+WMFWebViewControllerJavascript.h"; sourceTree = SOURCE_ROOT; }; + B0DF6F801CFE1D0B0046E507 /* WKWebView+WMFWebViewControllerJavascript.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "WKWebView+WMFWebViewControllerJavascript.m"; path = "Wikipedia/Code/WKWebView+WMFWebViewControllerJavascript.m"; sourceTree = SOURCE_ROOT; }; + B0E294CC1DB2CF4300861D04 /* UIView+Animations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+Animations.swift"; path = "Wikipedia/Code/UIView+Animations.swift"; sourceTree = SOURCE_ROOT; }; + B0E294D21DB2DC8900861D04 /* WMFWelcomeIntroductionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomeIntroductionViewController.swift; path = Wikipedia/Code/WMFWelcomeIntroductionViewController.swift; sourceTree = SOURCE_ROOT; }; + B0E802B61C0CD2140065EBC0 /* UIBarButtonItem+WMFButtonConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIBarButtonItem+WMFButtonConvenience.h"; path = "Wikipedia/Code/UIBarButtonItem+WMFButtonConvenience.h"; sourceTree = SOURCE_ROOT; }; + B0E802B71C0CD2140065EBC0 /* UIBarButtonItem+WMFButtonConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIBarButtonItem+WMFButtonConvenience.m"; path = "Wikipedia/Code/UIBarButtonItem+WMFButtonConvenience.m"; sourceTree = SOURCE_ROOT; }; + B0E802BC1C0CD2360065EBC0 /* UIButton+WMFButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIButton+WMFButton.h"; path = "Wikipedia/Code/UIButton+WMFButton.h"; sourceTree = SOURCE_ROOT; }; + B0E802BD1C0CD2360065EBC0 /* UIButton+WMFButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIButton+WMFButton.m"; path = "Wikipedia/Code/UIButton+WMFButton.m"; sourceTree = SOURCE_ROOT; }; + B0E802BF1C0CD27F0065EBC0 /* WMFAppViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFAppViewController.h; path = Wikipedia/Code/WMFAppViewController.h; sourceTree = SOURCE_ROOT; }; + B0E802C01C0CD27F0065EBC0 /* WMFAppViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFAppViewController.m; path = Wikipedia/Code/WMFAppViewController.m; sourceTree = SOURCE_ROOT; usesTabs = 0; }; + B0E802FC1C0CD5000065EBC0 /* WMFLocationSearchFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFLocationSearchFetcher.h; path = Wikipedia/Code/WMFLocationSearchFetcher.h; sourceTree = SOURCE_ROOT; }; + B0E802FD1C0CD5000065EBC0 /* WMFLocationSearchFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFLocationSearchFetcher.m; path = Wikipedia/Code/WMFLocationSearchFetcher.m; sourceTree = SOURCE_ROOT; }; + B0E802FE1C0CD5000065EBC0 /* WMFLocationSearchResults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFLocationSearchResults.h; path = Wikipedia/Code/WMFLocationSearchResults.h; sourceTree = SOURCE_ROOT; }; + B0E802FF1C0CD5000065EBC0 /* WMFLocationSearchResults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFLocationSearchResults.m; path = Wikipedia/Code/WMFLocationSearchResults.m; sourceTree = SOURCE_ROOT; }; + B0E8031A1C0CD6820065EBC0 /* WMFCompassView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFCompassView.h; path = Wikipedia/Code/WMFCompassView.h; sourceTree = SOURCE_ROOT; }; + B0E8031B1C0CD6820065EBC0 /* WMFCompassView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFCompassView.m; path = Wikipedia/Code/WMFCompassView.m; sourceTree = SOURCE_ROOT; }; + B0E803411C0CD7980065EBC0 /* WMFSearchFetcher_Testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFSearchFetcher_Testing.h; path = Wikipedia/Code/WMFSearchFetcher_Testing.h; sourceTree = SOURCE_ROOT; }; + B0E803421C0CD7980065EBC0 /* WMFSearchFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFSearchFetcher.h; path = Wikipedia/Code/WMFSearchFetcher.h; sourceTree = SOURCE_ROOT; }; + B0E803431C0CD7980065EBC0 /* WMFSearchFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFSearchFetcher.m; path = Wikipedia/Code/WMFSearchFetcher.m; sourceTree = SOURCE_ROOT; }; + B0E803451C0CD7AA0065EBC0 /* WMFSearchResults_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFSearchResults_Internal.h; path = Wikipedia/Code/WMFSearchResults_Internal.h; sourceTree = SOURCE_ROOT; }; + B0E803461C0CD7AA0065EBC0 /* WMFSearchResults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFSearchResults.h; path = Wikipedia/Code/WMFSearchResults.h; sourceTree = SOURCE_ROOT; }; + B0E803471C0CD7AA0065EBC0 /* WMFSearchResults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFSearchResults.m; path = Wikipedia/Code/WMFSearchResults.m; sourceTree = SOURCE_ROOT; }; + B0E8036C1C0CD98B0065EBC0 /* TableOfContentsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TableOfContentsViewController.swift; path = Wikipedia/Code/TableOfContentsViewController.swift; sourceTree = SOURCE_ROOT; }; + B0E8036E1C0CD99A0065EBC0 /* TableOfContentsPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TableOfContentsPresentationController.swift; path = Wikipedia/Code/TableOfContentsPresentationController.swift; sourceTree = SOURCE_ROOT; }; + B0E803701C0CD9A80065EBC0 /* TableOfContentsAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TableOfContentsAnimator.swift; path = Wikipedia/Code/TableOfContentsAnimator.swift; sourceTree = SOURCE_ROOT; }; + B0E803721C0CD9C10065EBC0 /* TableOfContentsCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TableOfContentsCell.swift; path = Wikipedia/Code/TableOfContentsCell.swift; sourceTree = SOURCE_ROOT; }; + B0E803731C0CD9C10065EBC0 /* TableOfContentsCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = TableOfContentsCell.xib; path = Wikipedia/Code/TableOfContentsCell.xib; sourceTree = SOURCE_ROOT; }; + B0E8038F1C0CDABE0065EBC0 /* UIView+WMFSnapshotting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+WMFSnapshotting.h"; path = "Wikipedia/Code/UIView+WMFSnapshotting.h"; sourceTree = SOURCE_ROOT; }; + B0E803901C0CDABE0065EBC0 /* UIView+WMFSnapshotting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+WMFSnapshotting.m"; path = "Wikipedia/Code/UIView+WMFSnapshotting.m"; sourceTree = SOURCE_ROOT; }; + B0E803961C0CDB150065EBC0 /* WMFNumberOfExtractCharacters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFNumberOfExtractCharacters.h; path = Wikipedia/Code/WMFNumberOfExtractCharacters.h; sourceTree = SOURCE_ROOT; }; + B0E803CA1C0CDC9B0065EBC0 /* WMFTitleInsetRespectingButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFTitleInsetRespectingButton.h; path = Wikipedia/Code/WMFTitleInsetRespectingButton.h; sourceTree = SOURCE_ROOT; }; + B0E803CB1C0CDC9B0065EBC0 /* WMFTitleInsetRespectingButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFTitleInsetRespectingButton.m; path = Wikipedia/Code/WMFTitleInsetRespectingButton.m; sourceTree = SOURCE_ROOT; }; + B0E803E41C0CDD450065EBC0 /* UIViewController+WMFStoryboardUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+WMFStoryboardUtilities.h"; path = "Wikipedia/Code/UIViewController+WMFStoryboardUtilities.h"; sourceTree = SOURCE_ROOT; }; + B0E803E51C0CDD450065EBC0 /* UIViewController+WMFStoryboardUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+WMFStoryboardUtilities.m"; path = "Wikipedia/Code/UIViewController+WMFStoryboardUtilities.m"; sourceTree = SOURCE_ROOT; }; + B0E803FC1C0CDE480065EBC0 /* WMFAccountCreationViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = WMFAccountCreationViewController.storyboard; path = Wikipedia/Code/WMFAccountCreationViewController.storyboard; sourceTree = SOURCE_ROOT; }; + B0E803FD1C0CDE480065EBC0 /* WMFCaptchaViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = WMFCaptchaViewController.storyboard; path = Wikipedia/Code/WMFCaptchaViewController.storyboard; sourceTree = SOURCE_ROOT; }; + B0E804001C0CDE480065EBC0 /* WMFLoginViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = WMFLoginViewController.storyboard; path = Wikipedia/Code/WMFLoginViewController.storyboard; sourceTree = SOURCE_ROOT; }; + B0E804031C0CDE480065EBC0 /* EditSaveViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = EditSaveViewController.storyboard; path = Wikipedia/Code/EditSaveViewController.storyboard; sourceTree = SOURCE_ROOT; }; + B0E804091C0CDE480065EBC0 /* WMFSettingsViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = WMFSettingsViewController.storyboard; path = Wikipedia/Code/WMFSettingsViewController.storyboard; sourceTree = SOURCE_ROOT; }; + B0E804261C0CDF510065EBC0 /* WMFGeometry.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = WMFGeometry.c; path = Wikipedia/Code/WMFGeometry.c; sourceTree = SOURCE_ROOT; }; + B0E804271C0CDF510065EBC0 /* WMFGeometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFGeometry.h; path = Wikipedia/Code/WMFGeometry.h; sourceTree = SOURCE_ROOT; }; + B0E8043D1C0CDF850065EBC0 /* WMFGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFGradientView.h; path = Wikipedia/Code/WMFGradientView.h; sourceTree = SOURCE_ROOT; }; + B0E8043E1C0CDF850065EBC0 /* WMFGradientView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFGradientView.m; path = Wikipedia/Code/WMFGradientView.m; sourceTree = SOURCE_ROOT; }; + B0E8046B1C0CE0B40065EBC0 /* CIContext+WMFImageProcessing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "CIContext+WMFImageProcessing.h"; path = "Wikipedia/Code/CIContext+WMFImageProcessing.h"; sourceTree = SOURCE_ROOT; }; + B0E8046C1C0CE0B40065EBC0 /* CIContext+WMFImageProcessing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "CIContext+WMFImageProcessing.m"; path = "Wikipedia/Code/CIContext+WMFImageProcessing.m"; sourceTree = SOURCE_ROOT; }; + B0E8046D1C0CE0B40065EBC0 /* CIDetector+WMFFaceDetection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "CIDetector+WMFFaceDetection.h"; path = "Wikipedia/Code/CIDetector+WMFFaceDetection.h"; sourceTree = SOURCE_ROOT; }; + B0E8046E1C0CE0B40065EBC0 /* CIDetector+WMFFaceDetection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "CIDetector+WMFFaceDetection.m"; path = "Wikipedia/Code/CIDetector+WMFFaceDetection.m"; sourceTree = SOURCE_ROOT; }; + B0E804711C0CE0B40065EBC0 /* CLLocation+WMFBearing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "CLLocation+WMFBearing.h"; path = "Wikipedia/Code/CLLocation+WMFBearing.h"; sourceTree = SOURCE_ROOT; }; + B0E804721C0CE0B40065EBC0 /* CLLocation+WMFBearing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "CLLocation+WMFBearing.m"; path = "Wikipedia/Code/CLLocation+WMFBearing.m"; sourceTree = SOURCE_ROOT; }; + B0E804731C0CE0B40065EBC0 /* DDLog+WMFLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "DDLog+WMFLogger.h"; path = "Wikipedia/Code/DDLog+WMFLogger.h"; sourceTree = SOURCE_ROOT; }; + B0E804741C0CE0B40065EBC0 /* DDLog+WMFLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "DDLog+WMFLogger.m"; path = "Wikipedia/Code/DDLog+WMFLogger.m"; sourceTree = SOURCE_ROOT; }; + B0E804831C0CE0B40065EBC0 /* NSAttributedString+WMFModify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+WMFModify.h"; path = "Wikipedia/Code/NSAttributedString+WMFModify.h"; sourceTree = SOURCE_ROOT; }; + B0E804841C0CE0B40065EBC0 /* NSAttributedString+WMFModify.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSAttributedString+WMFModify.m"; path = "Wikipedia/Code/NSAttributedString+WMFModify.m"; sourceTree = SOURCE_ROOT; }; + B0E804891C0CE0B40065EBC0 /* NSBundle+WMFInfoUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSBundle+WMFInfoUtils.h"; path = "Wikipedia/Code/NSBundle+WMFInfoUtils.h"; sourceTree = SOURCE_ROOT; }; + B0E8048A1C0CE0B40065EBC0 /* NSBundle+WMFInfoUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSBundle+WMFInfoUtils.m"; path = "Wikipedia/Code/NSBundle+WMFInfoUtils.m"; sourceTree = SOURCE_ROOT; }; + B0E8048B1C0CE0B40065EBC0 /* NSCharacterSet+WMFExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCharacterSet+WMFExtras.h"; path = "Wikipedia/Code/NSCharacterSet+WMFExtras.h"; sourceTree = SOURCE_ROOT; }; + B0E8048C1C0CE0B40065EBC0 /* NSCharacterSet+WMFExtras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSCharacterSet+WMFExtras.m"; path = "Wikipedia/Code/NSCharacterSet+WMFExtras.m"; sourceTree = SOURCE_ROOT; }; + B0E8048F1C0CE0B40065EBC0 /* NSDateFormatter+WMFExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDateFormatter+WMFExtensions.h"; path = "Wikipedia/Code/NSDateFormatter+WMFExtensions.h"; sourceTree = SOURCE_ROOT; }; + B0E804901C0CE0B40065EBC0 /* NSDateFormatter+WMFExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDateFormatter+WMFExtensions.m"; path = "Wikipedia/Code/NSDateFormatter+WMFExtensions.m"; sourceTree = SOURCE_ROOT; }; + B0E804911C0CE0B40065EBC0 /* NSError+WMFExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSError+WMFExtensions.h"; path = "Wikipedia/Code/NSError+WMFExtensions.h"; sourceTree = SOURCE_ROOT; }; + B0E804921C0CE0B40065EBC0 /* NSError+WMFExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSError+WMFExtensions.m"; path = "Wikipedia/Code/NSError+WMFExtensions.m"; sourceTree = SOURCE_ROOT; }; + B0E804951C0CE0B40065EBC0 /* NSIndexSet+BKReduce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSIndexSet+BKReduce.h"; path = "Wikipedia/Code/NSIndexSet+BKReduce.h"; sourceTree = SOURCE_ROOT; }; + B0E804961C0CE0B40065EBC0 /* NSIndexSet+BKReduce.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSIndexSet+BKReduce.m"; path = "Wikipedia/Code/NSIndexSet+BKReduce.m"; sourceTree = SOURCE_ROOT; }; + B0E804971C0CE0B40065EBC0 /* NSLocale+WMFExtras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSLocale+WMFExtras.swift"; path = "Wikipedia/Code/NSLocale+WMFExtras.swift"; sourceTree = SOURCE_ROOT; }; + B0E804A21C0CE0B40065EBC0 /* NSProcessInfo+WMFOperatingSystemVersionChecks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSProcessInfo+WMFOperatingSystemVersionChecks.h"; path = "Wikipedia/Code/NSProcessInfo+WMFOperatingSystemVersionChecks.h"; sourceTree = SOURCE_ROOT; }; + B0E804A31C0CE0B40065EBC0 /* NSProcessInfo+WMFOperatingSystemVersionChecks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSProcessInfo+WMFOperatingSystemVersionChecks.m"; path = "Wikipedia/Code/NSProcessInfo+WMFOperatingSystemVersionChecks.m"; sourceTree = SOURCE_ROOT; }; + B0E804A41C0CE0B40065EBC0 /* NSString+WMFExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+WMFExtras.h"; path = "Wikipedia/Code/NSString+WMFExtras.h"; sourceTree = SOURCE_ROOT; }; + B0E804A51C0CE0B40065EBC0 /* NSString+WMFExtras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+WMFExtras.m"; path = "Wikipedia/Code/NSString+WMFExtras.m"; sourceTree = SOURCE_ROOT; }; + B0E804A61C0CE0B40065EBC0 /* NSString+FormattedAttributedString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+FormattedAttributedString.h"; path = "Wikipedia/Code/NSString+FormattedAttributedString.h"; sourceTree = SOURCE_ROOT; }; + B0E804A71C0CE0B40065EBC0 /* NSString+FormattedAttributedString.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+FormattedAttributedString.m"; path = "Wikipedia/Code/NSString+FormattedAttributedString.m"; sourceTree = SOURCE_ROOT; }; + B0E804A81C0CE0B40065EBC0 /* NSString+WMFDistance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+WMFDistance.h"; path = "Wikipedia/Code/NSString+WMFDistance.h"; sourceTree = SOURCE_ROOT; }; + B0E804A91C0CE0B40065EBC0 /* NSString+WMFDistance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+WMFDistance.m"; path = "Wikipedia/Code/NSString+WMFDistance.m"; sourceTree = SOURCE_ROOT; }; + B0E804AC1C0CE0B40065EBC0 /* NSString+WMFHTMLParsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+WMFHTMLParsing.h"; path = "Wikipedia/Code/NSString+WMFHTMLParsing.h"; sourceTree = SOURCE_ROOT; }; + B0E804AD1C0CE0B40065EBC0 /* NSString+WMFHTMLParsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+WMFHTMLParsing.m"; path = "Wikipedia/Code/NSString+WMFHTMLParsing.m"; sourceTree = SOURCE_ROOT; }; + B0E804AE1C0CE0B40065EBC0 /* NSURL+WMFExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSURL+WMFExtras.h"; path = "Wikipedia/Code/NSURL+WMFExtras.h"; sourceTree = SOURCE_ROOT; }; + B0E804AF1C0CE0B40065EBC0 /* NSURL+WMFExtras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSURL+WMFExtras.m"; path = "Wikipedia/Code/NSURL+WMFExtras.m"; sourceTree = SOURCE_ROOT; }; + B0E804F21C0CE0DC0065EBC0 /* UIColor+WMFStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIColor+WMFStyle.h"; path = "Wikipedia/Code/UIColor+WMFStyle.h"; sourceTree = SOURCE_ROOT; }; + B0E804F31C0CE0DC0065EBC0 /* UIColor+WMFStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIColor+WMFStyle.m"; path = "Wikipedia/Code/UIColor+WMFStyle.m"; sourceTree = SOURCE_ROOT; }; + B0E804FA1C0CE0DC0065EBC0 /* UIImage+WMFImageProcessing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+WMFImageProcessing.h"; path = "Wikipedia/Code/UIImage+WMFImageProcessing.h"; sourceTree = SOURCE_ROOT; }; + B0E804FB1C0CE0DC0065EBC0 /* UIImage+WMFImageProcessing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+WMFImageProcessing.m"; path = "Wikipedia/Code/UIImage+WMFImageProcessing.m"; sourceTree = SOURCE_ROOT; }; + B0E804FC1C0CE0DC0065EBC0 /* UIImage+WMFNormalization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+WMFNormalization.h"; path = "Wikipedia/Code/UIImage+WMFNormalization.h"; sourceTree = SOURCE_ROOT; }; + B0E804FD1C0CE0DC0065EBC0 /* UIImage+WMFNormalization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = "UIImage+WMFNormalization.m"; path = "Wikipedia/Code/UIImage+WMFNormalization.m"; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + B0E805001C0CE0DC0065EBC0 /* UIImage+WMFStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+WMFStyle.h"; path = "Wikipedia/Code/UIImage+WMFStyle.h"; sourceTree = SOURCE_ROOT; }; + B0E805011C0CE0DC0065EBC0 /* UIImage+WMFStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+WMFStyle.m"; path = "Wikipedia/Code/UIImage+WMFStyle.m"; sourceTree = SOURCE_ROOT; }; + B0E805021C0CE0DC0065EBC0 /* UIImageView+WMFContentOffset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImageView+WMFContentOffset.h"; path = "Wikipedia/Code/UIImageView+WMFContentOffset.h"; sourceTree = SOURCE_ROOT; }; + B0E805031C0CE0DC0065EBC0 /* UIImageView+WMFContentOffset.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = "UIImageView+WMFContentOffset.m"; path = "Wikipedia/Code/UIImageView+WMFContentOffset.m"; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + B0E805081C0CE0DC0065EBC0 /* UIScrollView+ScrollSubviewToLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIScrollView+ScrollSubviewToLocation.h"; path = "Wikipedia/Code/UIScrollView+ScrollSubviewToLocation.h"; sourceTree = SOURCE_ROOT; }; + B0E805091C0CE0DC0065EBC0 /* UIScrollView+ScrollSubviewToLocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIScrollView+ScrollSubviewToLocation.m"; path = "Wikipedia/Code/UIScrollView+ScrollSubviewToLocation.m"; sourceTree = SOURCE_ROOT; }; + B0E8050A1C0CE0DC0065EBC0 /* UIScrollView+WMFContentOffsetUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIScrollView+WMFContentOffsetUtils.h"; path = "Wikipedia/Code/UIScrollView+WMFContentOffsetUtils.h"; sourceTree = SOURCE_ROOT; }; + B0E8050B1C0CE0DC0065EBC0 /* UIScrollView+WMFContentOffsetUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIScrollView+WMFContentOffsetUtils.m"; path = "Wikipedia/Code/UIScrollView+WMFContentOffsetUtils.m"; sourceTree = SOURCE_ROOT; }; + B0E805181C0CE0DC0065EBC0 /* UIView+IBExtras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+IBExtras.swift"; path = "Wikipedia/Code/UIView+IBExtras.swift"; sourceTree = SOURCE_ROOT; }; + B0E8051D1C0CE0DC0065EBC0 /* UIView+WMFDefaultNib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+WMFDefaultNib.h"; path = "Wikipedia/Code/UIView+WMFDefaultNib.h"; sourceTree = SOURCE_ROOT; }; + B0E8051E1C0CE0DC0065EBC0 /* UIView+WMFDefaultNib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+WMFDefaultNib.m"; path = "Wikipedia/Code/UIView+WMFDefaultNib.m"; sourceTree = SOURCE_ROOT; }; + B0E8051F1C0CE0DC0065EBC0 /* UIView+WMFFrameUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+WMFFrameUtils.h"; path = "Wikipedia/Code/UIView+WMFFrameUtils.h"; sourceTree = SOURCE_ROOT; }; + B0E805201C0CE0DC0065EBC0 /* UIView+WMFFrameUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+WMFFrameUtils.m"; path = "Wikipedia/Code/UIView+WMFFrameUtils.m"; sourceTree = SOURCE_ROOT; }; + B0E8052D1C0CE0DC0065EBC0 /* WKWebView+ElementLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "WKWebView+ElementLocation.h"; path = "Wikipedia/Code/WKWebView+ElementLocation.h"; sourceTree = SOURCE_ROOT; }; + B0E8052E1C0CE0DC0065EBC0 /* WKWebView+ElementLocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "WKWebView+ElementLocation.m"; path = "Wikipedia/Code/WKWebView+ElementLocation.m"; sourceTree = SOURCE_ROOT; }; + B0E805761C0CE24B0065EBC0 /* WMFDeprecationMacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WMFDeprecationMacros.h; path = Wikipedia/Code/WMFDeprecationMacros.h; sourceTree = SOURCE_ROOT; }; + B0E805771C0CE2C60065EBC0 /* CreateAccountFunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CreateAccountFunnel.h; path = Wikipedia/Code/CreateAccountFunnel.h; sourceTree = SOURCE_ROOT; }; + B0E805781C0CE2C60065EBC0 /* CreateAccountFunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CreateAccountFunnel.m; path = Wikipedia/Code/CreateAccountFunnel.m; sourceTree = SOURCE_ROOT; }; + B0E8057D1C0CE2C60065EBC0 /* EventLoggingFunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EventLoggingFunnel.h; path = Wikipedia/Code/EventLoggingFunnel.h; sourceTree = SOURCE_ROOT; }; + B0E8057E1C0CE2C60065EBC0 /* EventLoggingFunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = EventLoggingFunnel.m; path = Wikipedia/Code/EventLoggingFunnel.m; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + B0E8057F1C0CE2C60065EBC0 /* WMFLoginFunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFLoginFunnel.h; path = Wikipedia/Code/WMFLoginFunnel.h; sourceTree = SOURCE_ROOT; }; + B0E805801C0CE2C60065EBC0 /* WMFLoginFunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFLoginFunnel.m; path = Wikipedia/Code/WMFLoginFunnel.m; sourceTree = SOURCE_ROOT; }; + B0E805811C0CE2C60065EBC0 /* ProtectedEditAttemptFunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProtectedEditAttemptFunnel.h; path = Wikipedia/Code/ProtectedEditAttemptFunnel.h; sourceTree = SOURCE_ROOT; }; + B0E805821C0CE2C60065EBC0 /* ProtectedEditAttemptFunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProtectedEditAttemptFunnel.m; path = Wikipedia/Code/ProtectedEditAttemptFunnel.m; sourceTree = SOURCE_ROOT; }; + B0E805851C0CE2C60065EBC0 /* SavedPagesFunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SavedPagesFunnel.h; path = Wikipedia/Code/SavedPagesFunnel.h; sourceTree = SOURCE_ROOT; }; + B0E805861C0CE2C60065EBC0 /* SavedPagesFunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SavedPagesFunnel.m; path = Wikipedia/Code/SavedPagesFunnel.m; sourceTree = SOURCE_ROOT; }; + B0E805871C0CE2C60065EBC0 /* ToCInteractionFunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ToCInteractionFunnel.h; path = Wikipedia/Code/ToCInteractionFunnel.h; sourceTree = SOURCE_ROOT; }; + B0E805881C0CE2C60065EBC0 /* ToCInteractionFunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ToCInteractionFunnel.m; path = Wikipedia/Code/ToCInteractionFunnel.m; sourceTree = SOURCE_ROOT; }; + B0E805891C0CE2C60065EBC0 /* WMFHamburgerMenuFunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFHamburgerMenuFunnel.h; path = Wikipedia/Code/WMFHamburgerMenuFunnel.h; sourceTree = SOURCE_ROOT; }; + B0E8058A1C0CE2C60065EBC0 /* WMFHamburgerMenuFunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFHamburgerMenuFunnel.m; path = Wikipedia/Code/WMFHamburgerMenuFunnel.m; sourceTree = SOURCE_ROOT; }; + B0E805981C0CE2E40065EBC0 /* WMFSearchFunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFSearchFunnel.h; path = Wikipedia/Code/WMFSearchFunnel.h; sourceTree = SOURCE_ROOT; }; + B0E805991C0CE2E40065EBC0 /* WMFSearchFunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFSearchFunnel.m; path = Wikipedia/Code/WMFSearchFunnel.m; sourceTree = SOURCE_ROOT; }; + B0E8059B1C0CE2F50065EBC0 /* WMFShareFunnel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFShareFunnel.h; path = Wikipedia/Code/WMFShareFunnel.h; sourceTree = SOURCE_ROOT; }; + B0E8059C1C0CE2F50065EBC0 /* WMFShareFunnel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFShareFunnel.m; path = Wikipedia/Code/WMFShareFunnel.m; sourceTree = SOURCE_ROOT; }; + B0E805C61C0CE5250065EBC0 /* ImageDownload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageDownload.swift; path = Wikipedia/Code/ImageDownload.swift; sourceTree = SOURCE_ROOT; }; + B0E805CF1C0CE5420065EBC0 /* WMFFaceDetectionCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFFaceDetectionCache.h; path = Wikipedia/Code/WMFFaceDetectionCache.h; sourceTree = SOURCE_ROOT; }; + B0E805D01C0CE5420065EBC0 /* WMFFaceDetectionCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFFaceDetectionCache.m; path = Wikipedia/Code/WMFFaceDetectionCache.m; sourceTree = SOURCE_ROOT; }; + B0E805D51C0CE59B0065EBC0 /* UIImageView+WMFImageFetching.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImageView+WMFImageFetching.h"; path = "Wikipedia/Code/UIImageView+WMFImageFetching.h"; sourceTree = SOURCE_ROOT; }; + B0E805D61C0CE59B0065EBC0 /* UIImageView+WMFImageFetching.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+WMFImageFetching.m"; path = "Wikipedia/Code/UIImageView+WMFImageFetching.m"; sourceTree = SOURCE_ROOT; }; + B0E806291C0CE7670065EBC0 /* MTLValueTransformer+WMFNumericValueTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MTLValueTransformer+WMFNumericValueTransformer.h"; path = "Wikipedia/Code/MTLValueTransformer+WMFNumericValueTransformer.h"; sourceTree = SOURCE_ROOT; }; + B0E8062A1C0CE7670065EBC0 /* MTLValueTransformer+WMFNumericValueTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MTLValueTransformer+WMFNumericValueTransformer.m"; path = "Wikipedia/Code/MTLValueTransformer+WMFNumericValueTransformer.m"; sourceTree = SOURCE_ROOT; }; + B0E806561C0CE84B0065EBC0 /* WikiTextSectionUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WikiTextSectionUploader.h; path = Wikipedia/Code/WikiTextSectionUploader.h; sourceTree = SOURCE_ROOT; }; + B0E806571C0CE84B0065EBC0 /* WikiTextSectionUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WikiTextSectionUploader.m; path = Wikipedia/Code/WikiTextSectionUploader.m; sourceTree = SOURCE_ROOT; }; + B0E8065F1C0CE9030065EBC0 /* MWKImageInfoFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKImageInfoFetcher.h; path = Wikipedia/Code/MWKImageInfoFetcher.h; sourceTree = SOURCE_ROOT; }; + B0E806601C0CE9030065EBC0 /* MWKImageInfoFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKImageInfoFetcher.m; path = Wikipedia/Code/MWKImageInfoFetcher.m; sourceTree = SOURCE_ROOT; }; + B0E806611C0CE9030065EBC0 /* MWKLanguageLinkFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKLanguageLinkFetcher.h; path = Wikipedia/Code/MWKLanguageLinkFetcher.h; sourceTree = SOURCE_ROOT; }; + B0E806621C0CE9030065EBC0 /* MWKLanguageLinkFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKLanguageLinkFetcher.m; path = Wikipedia/Code/MWKLanguageLinkFetcher.m; sourceTree = SOURCE_ROOT; }; + B0E806781C0CE9C70065EBC0 /* Cancellable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Cancellable.swift; path = Wikipedia/Code/Cancellable.swift; sourceTree = SOURCE_ROOT; }; + B0E806921C0CEA7B0065EBC0 /* AboutViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AboutViewController.h; path = Wikipedia/Code/AboutViewController.h; sourceTree = SOURCE_ROOT; }; + B0E806931C0CEA7B0065EBC0 /* AboutViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AboutViewController.m; path = Wikipedia/Code/AboutViewController.m; sourceTree = SOURCE_ROOT; }; + B0E806941C0CEA7B0065EBC0 /* AboutViewController.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AboutViewController.plist; path = Wikipedia/Code/AboutViewController.plist; sourceTree = SOURCE_ROOT; }; + B0E806AE1C0CEB160065EBC0 /* WMFLanguageCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFLanguageCell.h; path = Wikipedia/Code/WMFLanguageCell.h; sourceTree = SOURCE_ROOT; }; + B0E806B31C0CEB160065EBC0 /* MWKLanguageLinkController_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKLanguageLinkController_Private.h; path = Wikipedia/Code/MWKLanguageLinkController_Private.h; sourceTree = SOURCE_ROOT; }; + B0E806B41C0CEB160065EBC0 /* MWKLanguageLinkController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKLanguageLinkController.h; path = Wikipedia/Code/MWKLanguageLinkController.h; sourceTree = SOURCE_ROOT; }; + B0E806B51C0CEB160065EBC0 /* MWKLanguageLinkController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = MWKLanguageLinkController.m; path = Wikipedia/Code/MWKLanguageLinkController.m; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + B0E806C01C0CEB380065EBC0 /* WMFSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFSettingsViewController.h; path = Wikipedia/Code/WMFSettingsViewController.h; sourceTree = SOURCE_ROOT; }; + B0E806C11C0CEB380065EBC0 /* WMFSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFSettingsViewController.m; path = Wikipedia/Code/WMFSettingsViewController.m; sourceTree = SOURCE_ROOT; usesTabs = 0; }; + B0E8071E1C0CEC8A0065EBC0 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + B0E8072D1C0CED810065EBC0 /* WikipediaAppUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WikipediaAppUtils.h; path = Wikipedia/Code/WikipediaAppUtils.h; sourceTree = SOURCE_ROOT; }; + B0E8072E1C0CED810065EBC0 /* WikipediaAppUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = WikipediaAppUtils.m; path = Wikipedia/Code/WikipediaAppUtils.m; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + B0E8072F1C0CED810065EBC0 /* WMFComparison.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFComparison.h; path = Wikipedia/Code/WMFComparison.h; sourceTree = SOURCE_ROOT; }; + B0E807301C0CED810065EBC0 /* WMFGCDHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFGCDHelpers.h; path = Wikipedia/Code/WMFGCDHelpers.h; sourceTree = SOURCE_ROOT; }; + B0E807311C0CED810065EBC0 /* WMFHashing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFHashing.h; path = Wikipedia/Code/WMFHashing.h; sourceTree = SOURCE_ROOT; }; + B0E807321C0CED810065EBC0 /* WMFImageURLParsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFImageURLParsing.h; path = Wikipedia/Code/WMFImageURLParsing.h; sourceTree = SOURCE_ROOT; }; + B0E807331C0CED810065EBC0 /* WMFImageURLParsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFImageURLParsing.m; path = Wikipedia/Code/WMFImageURLParsing.m; sourceTree = SOURCE_ROOT; }; + B0E807341C0CED810065EBC0 /* WMFLogFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFLogFormatter.h; path = Wikipedia/Code/WMFLogFormatter.h; sourceTree = SOURCE_ROOT; }; + B0E807351C0CED810065EBC0 /* WMFLogFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFLogFormatter.m; path = Wikipedia/Code/WMFLogFormatter.m; sourceTree = SOURCE_ROOT; }; + B0E807361C0CED810065EBC0 /* WMFLogging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFLogging.h; path = Wikipedia/Code/WMFLogging.h; sourceTree = SOURCE_ROOT; }; + B0E807371C0CED810065EBC0 /* WMFMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFMath.h; path = Wikipedia/Code/WMFMath.h; sourceTree = SOURCE_ROOT; }; + B0E807381C0CED810065EBC0 /* WMFMath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFMath.m; path = Wikipedia/Code/WMFMath.m; sourceTree = SOURCE_ROOT; }; + B0E807391C0CED810065EBC0 /* WMFOutParamUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFOutParamUtils.h; path = Wikipedia/Code/WMFOutParamUtils.h; sourceTree = SOURCE_ROOT; }; + B0E8073A1C0CED810065EBC0 /* WMFRangeUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFRangeUtils.h; path = Wikipedia/Code/WMFRangeUtils.h; sourceTree = SOURCE_ROOT; }; + B0E807751C0CEEB00065EBC0 /* UIImageView+WMFImageFetchingInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImageView+WMFImageFetchingInternal.h"; path = "Wikipedia/Code/UIImageView+WMFImageFetchingInternal.h"; sourceTree = SOURCE_ROOT; }; + B0E807761C0CEEB00065EBC0 /* UIImageView+WMFImageFetchingInternal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = "UIImageView+WMFImageFetchingInternal.m"; path = "Wikipedia/Code/UIImageView+WMFImageFetchingInternal.m"; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + B0E807821C0CEF660065EBC0 /* MWKDataObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKDataObject.h; path = Wikipedia/Code/MWKDataObject.h; sourceTree = SOURCE_ROOT; }; + B0E807831C0CEF660065EBC0 /* MWKDataObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKDataObject.m; path = Wikipedia/Code/MWKDataObject.m; sourceTree = SOURCE_ROOT; }; + B0E807841C0CEF660065EBC0 /* MWKDataStoreList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKDataStoreList.h; path = Wikipedia/Code/MWKDataStoreList.h; sourceTree = SOURCE_ROOT; }; + B0E807851C0CEF660065EBC0 /* MWKList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKList.h; path = Wikipedia/Code/MWKList.h; sourceTree = SOURCE_ROOT; }; + B0E807861C0CEF660065EBC0 /* MWKList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKList.m; path = Wikipedia/Code/MWKList.m; sourceTree = SOURCE_ROOT; }; + B0E807871C0CEF660065EBC0 /* MWKList+Subclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MWKList+Subclass.h"; path = "Wikipedia/Code/MWKList+Subclass.h"; sourceTree = SOURCE_ROOT; }; + B0E807881C0CEF660065EBC0 /* MWKSiteDataObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKSiteDataObject.h; path = Wikipedia/Code/MWKSiteDataObject.h; sourceTree = SOURCE_ROOT; }; + B0E807891C0CEF660065EBC0 /* MWKSiteDataObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKSiteDataObject.m; path = Wikipedia/Code/MWKSiteDataObject.m; sourceTree = SOURCE_ROOT; }; + B0E807991C0CEFBD0065EBC0 /* MWKLanguageLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKLanguageLink.h; path = Wikipedia/Code/MWKLanguageLink.h; sourceTree = SOURCE_ROOT; }; + B0E8079A1C0CEFBD0065EBC0 /* MWKLanguageLink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKLanguageLink.m; path = Wikipedia/Code/MWKLanguageLink.m; sourceTree = SOURCE_ROOT; }; + B0E8079B1C0CEFBD0065EBC0 /* MWKLicense.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKLicense.h; path = Wikipedia/Code/MWKLicense.h; sourceTree = SOURCE_ROOT; }; + B0E8079C1C0CEFBD0065EBC0 /* MWKLicense.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKLicense.m; path = Wikipedia/Code/MWKLicense.m; sourceTree = SOURCE_ROOT; }; + B0E807A51C0CEFE30065EBC0 /* MWKRecentSearchEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKRecentSearchEntry.h; path = Wikipedia/Code/MWKRecentSearchEntry.h; sourceTree = SOURCE_ROOT; }; + B0E807A61C0CEFE30065EBC0 /* MWKRecentSearchEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKRecentSearchEntry.m; path = Wikipedia/Code/MWKRecentSearchEntry.m; sourceTree = SOURCE_ROOT; }; + B0E807A71C0CEFE30065EBC0 /* MWKRecentSearchList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKRecentSearchList.h; path = Wikipedia/Code/MWKRecentSearchList.h; sourceTree = SOURCE_ROOT; }; + B0E807A81C0CEFE30065EBC0 /* MWKRecentSearchList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKRecentSearchList.m; path = Wikipedia/Code/MWKRecentSearchList.m; sourceTree = SOURCE_ROOT; }; + B0E807B41C0CF0180065EBC0 /* MWKSavedPageList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKSavedPageList.h; path = Wikipedia/Code/MWKSavedPageList.h; sourceTree = SOURCE_ROOT; }; + B0E807B51C0CF0180065EBC0 /* MWKSavedPageList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = MWKSavedPageList.m; path = Wikipedia/Code/MWKSavedPageList.m; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + B0E807C11C0CF04A0065EBC0 /* MWKDataStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKDataStore.h; path = Wikipedia/Code/MWKDataStore.h; sourceTree = SOURCE_ROOT; }; + B0E807C21C0CF04A0065EBC0 /* MWKDataStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = MWKDataStore.m; path = Wikipedia/Code/MWKDataStore.m; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + B0E807C31C0CF04A0065EBC0 /* MWKImageInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKImageInfo.h; path = Wikipedia/Code/MWKImageInfo.h; sourceTree = SOURCE_ROOT; }; + B0E807C41C0CF04A0065EBC0 /* MWKImageInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKImageInfo.m; path = Wikipedia/Code/MWKImageInfo.m; sourceTree = SOURCE_ROOT; }; + B0E807C71C0CF04A0065EBC0 /* MWKLocationSearchResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKLocationSearchResult.h; path = Wikipedia/Code/MWKLocationSearchResult.h; sourceTree = SOURCE_ROOT; }; + B0E807C81C0CF04A0065EBC0 /* MWKLocationSearchResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKLocationSearchResult.m; path = Wikipedia/Code/MWKLocationSearchResult.m; sourceTree = SOURCE_ROOT; }; + B0E807C91C0CF04A0065EBC0 /* MWKSearchRedirectMapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKSearchRedirectMapping.h; path = Wikipedia/Code/MWKSearchRedirectMapping.h; sourceTree = SOURCE_ROOT; }; + B0E807CA1C0CF04A0065EBC0 /* MWKSearchRedirectMapping.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKSearchRedirectMapping.m; path = Wikipedia/Code/MWKSearchRedirectMapping.m; sourceTree = SOURCE_ROOT; }; + B0E807CB1C0CF04A0065EBC0 /* MWKSearchResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWKSearchResult.h; path = Wikipedia/Code/MWKSearchResult.h; sourceTree = SOURCE_ROOT; }; + B0E807CC1C0CF04A0065EBC0 /* MWKSearchResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKSearchResult.m; path = Wikipedia/Code/MWKSearchResult.m; sourceTree = SOURCE_ROOT; }; + B0E808281C0D07EA0065EBC0 /* Wikipedia-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "Wikipedia-Bridging-Header.h"; path = "Code/Wikipedia-Bridging-Header.h"; sourceTree = ""; }; + B0E8086B1C0D15170065EBC0 /* WMFCodingStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFCodingStyle.h; path = WikipediaUnitTests/Code/WMFCodingStyle.h; sourceTree = SOURCE_ROOT; }; + B0E8086C1C0D15170065EBC0 /* WMFCodingStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFCodingStyle.m; path = WikipediaUnitTests/Code/WMFCodingStyle.m; sourceTree = SOURCE_ROOT; }; + B0E8086E1C0D15330065EBC0 /* WMFTestFixtureUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WMFTestFixtureUtilities.h; path = WikipediaUnitTests/Code/WMFTestFixtureUtilities.h; sourceTree = SOURCE_ROOT; }; + B0E808721C0D154C0065EBC0 /* NSBundle+TestAssets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSBundle+TestAssets.h"; path = "WikipediaUnitTests/Code/NSBundle+TestAssets.h"; sourceTree = SOURCE_ROOT; }; + B0E808731C0D154C0065EBC0 /* NSBundle+TestAssets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSBundle+TestAssets.m"; path = "WikipediaUnitTests/Code/NSBundle+TestAssets.m"; sourceTree = SOURCE_ROOT; }; + B0E808751C0D155A0065EBC0 /* XCTestCase+WMFBundleConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "XCTestCase+WMFBundleConvenience.h"; path = "WikipediaUnitTests/Code/XCTestCase+WMFBundleConvenience.h"; sourceTree = SOURCE_ROOT; }; + B0E808761C0D155A0065EBC0 /* XCTestCase+WMFBundleConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "XCTestCase+WMFBundleConvenience.m"; path = "WikipediaUnitTests/Code/XCTestCase+WMFBundleConvenience.m"; sourceTree = SOURCE_ROOT; }; + B0E8087B1C0D15760065EBC0 /* WMFRandomFileUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFRandomFileUtilities.h; path = WikipediaUnitTests/Code/WMFRandomFileUtilities.h; sourceTree = SOURCE_ROOT; }; + B0E8087C1C0D15760065EBC0 /* WMFRandomFileUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFRandomFileUtilities.m; path = WikipediaUnitTests/Code/WMFRandomFileUtilities.m; sourceTree = SOURCE_ROOT; }; + B0E808801C0D15A20065EBC0 /* MWKDataStore+TemporaryDataStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MWKDataStore+TemporaryDataStore.h"; path = "WikipediaUnitTests/Code/MWKDataStore+TemporaryDataStore.h"; sourceTree = SOURCE_ROOT; }; + B0E808811C0D15A20065EBC0 /* MWKDataStore+TemporaryDataStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MWKDataStore+TemporaryDataStore.m"; path = "WikipediaUnitTests/Code/MWKDataStore+TemporaryDataStore.m"; sourceTree = SOURCE_ROOT; }; + B0E8088D1C0D16140065EBC0 /* WMFAsyncTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFAsyncTestCase.h; path = WikipediaUnitTests/Code/WMFAsyncTestCase.h; sourceTree = SOURCE_ROOT; }; + B0E8088E1C0D16140065EBC0 /* WMFAsyncTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFAsyncTestCase.m; path = WikipediaUnitTests/Code/WMFAsyncTestCase.m; sourceTree = SOURCE_ROOT; }; + B0E808931C0D16330065EBC0 /* NSArray+WMFShuffle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+WMFShuffle.h"; path = "WikipediaUnitTests/Code/NSArray+WMFShuffle.h"; sourceTree = SOURCE_ROOT; }; + B0E808941C0D16330065EBC0 /* NSArray+WMFShuffle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSArray+WMFShuffle.m"; path = "WikipediaUnitTests/Code/NSArray+WMFShuffle.m"; sourceTree = SOURCE_ROOT; }; + B0E808961C0D16430065EBC0 /* XCTestCase+WMFLocaleTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "XCTestCase+WMFLocaleTesting.h"; path = "WikipediaUnitTests/Code/XCTestCase+WMFLocaleTesting.h"; sourceTree = SOURCE_ROOT; }; + B0E808971C0D16430065EBC0 /* XCTestCase+WMFLocaleTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "XCTestCase+WMFLocaleTesting.m"; path = "WikipediaUnitTests/Code/XCTestCase+WMFLocaleTesting.m"; sourceTree = SOURCE_ROOT; }; + B0E8089B1C0D165B0065EBC0 /* XCTestCase+SwiftDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "XCTestCase+SwiftDefaults.swift"; path = "WikipediaUnitTests/Code/XCTestCase+SwiftDefaults.swift"; sourceTree = SOURCE_ROOT; }; + B0E808A01C0D16730065EBC0 /* XCTAssert+CGGeometry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "XCTAssert+CGGeometry.h"; path = "WikipediaUnitTests/Code/XCTAssert+CGGeometry.h"; sourceTree = SOURCE_ROOT; }; + B0E808A71C0D16A00065EBC0 /* UIView+VisualTestSizingUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+VisualTestSizingUtils.h"; path = "WikipediaUnitTests/Code/UIView+VisualTestSizingUtils.h"; sourceTree = SOURCE_ROOT; }; + B0E808A81C0D16A00065EBC0 /* UIView+VisualTestSizingUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+VisualTestSizingUtils.m"; path = "WikipediaUnitTests/Code/UIView+VisualTestSizingUtils.m"; sourceTree = SOURCE_ROOT; }; + B0E808B41C0D17070065EBC0 /* LSStubResponseDSL+WithJSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "LSStubResponseDSL+WithJSON.h"; path = "WikipediaUnitTests/Code/LSStubResponseDSL+WithJSON.h"; sourceTree = SOURCE_ROOT; }; + B0E808B51C0D17070065EBC0 /* LSStubResponseDSL+WithJSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "LSStubResponseDSL+WithJSON.m"; path = "WikipediaUnitTests/Code/LSStubResponseDSL+WithJSON.m"; sourceTree = SOURCE_ROOT; }; + B0E808B71C0D17160065EBC0 /* WMFHTTPHangingProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFHTTPHangingProtocol.h; path = WikipediaUnitTests/Code/WMFHTTPHangingProtocol.h; sourceTree = SOURCE_ROOT; }; + B0E808B81C0D17160065EBC0 /* WMFHTTPHangingProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFHTTPHangingProtocol.m; path = WikipediaUnitTests/Code/WMFHTTPHangingProtocol.m; sourceTree = SOURCE_ROOT; }; + B0E809041C0D18A00065EBC0 /* CircularBitwiseRotationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CircularBitwiseRotationTests.m; path = WikipediaUnitTests/Code/CircularBitwiseRotationTests.m; sourceTree = SOURCE_ROOT; }; + B0E809081C0D18BC0065EBC0 /* NSString+WMFHTMLParsingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+WMFHTMLParsingTests.m"; path = "WikipediaUnitTests/Code/NSString+WMFHTMLParsingTests.m"; sourceTree = SOURCE_ROOT; }; + B0E8090A1C0D18D90065EBC0 /* NSString+FormattedAttributedStringTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+FormattedAttributedStringTests.m"; path = "WikipediaUnitTests/Code/NSString+FormattedAttributedStringTests.m"; sourceTree = SOURCE_ROOT; }; + B0E8090C1C0D18E70065EBC0 /* WMFImageURLParsingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFImageURLParsingTests.m; path = WikipediaUnitTests/Code/WMFImageURLParsingTests.m; sourceTree = SOURCE_ROOT; }; + B0E8090E1C0D18F30065EBC0 /* WMFMathTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFMathTests.m; path = WikipediaUnitTests/Code/WMFMathTests.m; sourceTree = SOURCE_ROOT; }; + B0E809101C0D18FD0065EBC0 /* WMFSubstringUtilsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFSubstringUtilsTests.m; path = WikipediaUnitTests/Code/WMFSubstringUtilsTests.m; sourceTree = SOURCE_ROOT; }; + B0E809121C0D19090065EBC0 /* WMFDateFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFDateFormatterTests.m; path = WikipediaUnitTests/Code/WMFDateFormatterTests.m; sourceTree = SOURCE_ROOT; }; + B0E8092E1C0D1A0B0065EBC0 /* NSURL+WMFExtrasTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSURL+WMFExtrasTests.m"; path = "WikipediaUnitTests/Code/NSURL+WMFExtrasTests.m"; sourceTree = SOURCE_ROOT; }; + B0E809341C0D1A2F0065EBC0 /* WMFGeometryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFGeometryTests.m; path = WikipediaUnitTests/Code/WMFGeometryTests.m; sourceTree = SOURCE_ROOT; }; + B0E809361C0D1A420065EBC0 /* MWKLanguageLinkControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWKLanguageLinkControllerTests.m; path = WikipediaUnitTests/Code/MWKLanguageLinkControllerTests.m; sourceTree = SOURCE_ROOT; }; + B0E8093A1C0D1A590065EBC0 /* WMFSafeAssignTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFSafeAssignTests.m; path = WikipediaUnitTests/Code/WMFSafeAssignTests.m; sourceTree = SOURCE_ROOT; }; + B0E809401C0D1A820065EBC0 /* NSURL+WMFLinkParsingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSURL+WMFLinkParsingTests.m"; path = "WikipediaUnitTests/Code/NSURL+WMFLinkParsingTests.m"; sourceTree = SOURCE_ROOT; }; + B0E809541C0D1B510065EBC0 /* CLLocation+WMFBearingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "CLLocation+WMFBearingTests.m"; path = "WikipediaUnitTests/Code/CLLocation+WMFBearingTests.m"; sourceTree = SOURCE_ROOT; }; + B0E8095D1C0D1B930065EBC0 /* WMFMTLModelSerializationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFMTLModelSerializationTests.m; path = WikipediaUnitTests/Code/WMFMTLModelSerializationTests.m; sourceTree = SOURCE_ROOT; }; + B0E8095F1C0D1BA30065EBC0 /* WMFSearchFetcherTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFSearchFetcherTests.m; path = WikipediaUnitTests/Code/WMFSearchFetcherTests.m; sourceTree = SOURCE_ROOT; }; + B0E8096D1C0D1DD50065EBC0 /* WikipediaUnitTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "WikipediaUnitTests-Bridging-Header.h"; path = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; sourceTree = SOURCE_ROOT; }; + B0E8096E1C0D21530065EBC0 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B0E809701C0D215D0065EBC0 /* WikipediaUnitTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WikipediaUnitTests-Prefix.pch"; sourceTree = ""; }; + B0ED17331E4912EB008B70AD /* WMFTwoFactorPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFTwoFactorPasswordViewController.swift; path = Wikipedia/Code/WMFTwoFactorPasswordViewController.swift; sourceTree = SOURCE_ROOT; }; + B0ED173A1E497AE7008B70AD /* WMFCurrentlyLoggedInUserFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFCurrentlyLoggedInUserFetcher.swift; sourceTree = ""; }; + B0ED173C1E49831B008B70AD /* WMFTwoFactorPasswordViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = WMFTwoFactorPasswordViewController.storyboard; path = Wikipedia/Code/WMFTwoFactorPasswordViewController.storyboard; sourceTree = SOURCE_ROOT; }; + B0ED173E1E4CF3AF008B70AD /* WMFAuthenticationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFAuthenticationManager.swift; sourceTree = ""; }; + B0EF42CE1C43FDD200D125A8 /* UIApplicationShortcutItem+WMFShortcutItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIApplicationShortcutItem+WMFShortcutItem.h"; path = "Wikipedia/Code/UIApplicationShortcutItem+WMFShortcutItem.h"; sourceTree = SOURCE_ROOT; }; + B0EF42CF1C43FDD200D125A8 /* UIApplicationShortcutItem+WMFShortcutItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIApplicationShortcutItem+WMFShortcutItem.m"; path = "Wikipedia/Code/UIApplicationShortcutItem+WMFShortcutItem.m"; sourceTree = SOURCE_ROOT; }; + B0EFCD631EBEC231008F36E5 /* LibrariesUsed.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LibrariesUsed.storyboard; path = Wikipedia/Code/LibrariesUsed.storyboard; sourceTree = SOURCE_ROOT; }; + B0EFCD6C1EBF12E5008F36E5 /* LibrariesUsed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LibrariesUsed.swift; path = Wikipedia/Code/LibrariesUsed.swift; sourceTree = SOURCE_ROOT; }; + B0EFCD711EBF13B2008F36E5 /* LibrariesUsed.plist */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = LibrariesUsed.plist; sourceTree = ""; }; + B0F0874F1C860E910086F710 /* WMFWelcomeAnimationExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomeAnimationExtensions.swift; path = Wikipedia/Code/WMFWelcomeAnimationExtensions.swift; sourceTree = SOURCE_ROOT; }; + B0F4761921F921D300C4E254 /* EditSummaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSummaryViewController.swift; sourceTree = ""; }; + B0F4761A21F921D300C4E254 /* EditSummaryViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EditSummaryViewController.xib; sourceTree = ""; }; + B0F7CB551C8A89E300996DE0 /* WMFWelcomeAnalyticsAnimationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomeAnalyticsAnimationView.swift; path = Wikipedia/Code/WMFWelcomeAnalyticsAnimationView.swift; sourceTree = SOURCE_ROOT; }; + B0F7CB571C8A89EA00996DE0 /* WMFWelcomeIntroductionAnimationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomeIntroductionAnimationView.swift; path = Wikipedia/Code/WMFWelcomeIntroductionAnimationView.swift; sourceTree = SOURCE_ROOT; }; + B0F7CB591C8A89EF00996DE0 /* WMFWelcomeLanguagesAnimationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomeLanguagesAnimationView.swift; path = Wikipedia/Code/WMFWelcomeLanguagesAnimationView.swift; sourceTree = SOURCE_ROOT; }; + B0F84EEC1C8E444400801560 /* WMFWelcomeAnimationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFWelcomeAnimationView.swift; path = Wikipedia/Code/WMFWelcomeAnimationView.swift; sourceTree = SOURCE_ROOT; }; + B0F9299B1F84789C002A0788 /* WMFWelcomeInitialViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMFWelcomeInitialViewController.swift; sourceTree = ""; }; + B0F92C5F1E3A813500B72802 /* WMFAccountLogin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFAccountLogin.swift; sourceTree = ""; }; + B0F92C6E1E3C580900B72802 /* WMFCaptchaResetter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFCaptchaResetter.swift; path = Wikipedia/Code/WMFCaptchaResetter.swift; sourceTree = SOURCE_ROOT; }; + B0F92C7F1E3FFEA100B72802 /* WMFAuthLoginInfoFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFAuthLoginInfoFetcher.swift; sourceTree = ""; }; + B0F92C811E3FFEB900B72802 /* WMFAuthAccountCreationInfoFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFAuthAccountCreationInfoFetcher.swift; sourceTree = ""; }; + B0FFFB2921C9BED0001E787E /* TextFormattingButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFormattingButton.swift; sourceTree = ""; }; + B32535F01EE856FF00372E93 /* EventLogging.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = EventLogging.xcdatamodel; sourceTree = ""; }; + B32535FE1EE87A6200372E93 /* EventRecord+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EventRecord+CoreDataClass.swift"; sourceTree = ""; }; + B32535FF1EE87A6200372E93 /* EventRecord+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EventRecord+CoreDataProperties.swift"; sourceTree = ""; }; + B3369A341EE1F69E0075953E /* EventLoggingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EventLoggingService.swift; path = "WMF Framework/EventLoggingService.swift"; sourceTree = SOURCE_ROOT; }; + B37B38EF1E5F432F00FE11BD /* WMFDatabaseHousekeeper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFDatabaseHousekeeper.swift; sourceTree = ""; }; + B37B6FE81EEAFE11007CBB12 /* EventLoggingServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventLoggingServiceTests.swift; sourceTree = ""; }; + B389CFCA1E6784B600483C06 /* WMFDatabaseHousekeeperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFDatabaseHousekeeperTests.swift; sourceTree = ""; }; + B389CFCD1E6F238300483C06 /* WMFMapsActivity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFMapsActivity.swift; path = Wikipedia/Code/WMFMapsActivity.swift; sourceTree = SOURCE_ROOT; }; + B39427411E71F79700D3146D /* NSDictionaryBlocksKitTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSDictionaryBlocksKitTest.m; sourceTree = ""; }; + B39427421E71F79700D3146D /* NSSetBlocksKitTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSSetBlocksKitTest.m; sourceTree = ""; }; + B3F21D0E1EB0EBB4000ED0BB /* PlaceSearchService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaceSearchService.swift; sourceTree = ""; }; + BA4524161F324C3100439C42 /* FontSizeSliderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FontSizeSliderViewController.swift; sourceTree = ""; }; + BA4524171F324C3100439C42 /* FontSizeSliderViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FontSizeSliderViewController.xib; sourceTree = ""; }; + BA4524221F32500C00439C42 /* TextSizeChangeExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextSizeChangeExampleViewController.swift; sourceTree = ""; }; + BA4524231F32500C00439C42 /* TextSizeChangeExampleViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextSizeChangeExampleViewController.xib; sourceTree = ""; }; + BA6972561F2BA0D900E35F78 /* SettingsTableViewSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SettingsTableViewSection.swift; path = Wikipedia/Code/SettingsTableViewSection.swift; sourceTree = SOURCE_ROOT; }; + BA7683C01F30C56300A487AA /* ImageDimmingExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageDimmingExampleViewController.swift; sourceTree = ""; }; + BA7683C11F30C56300A487AA /* ImageDimmingExampleViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ImageDimmingExampleViewController.xib; sourceTree = ""; }; + BA7683C41F30D86A00A487AA /* ProminentSwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProminentSwitch.swift; sourceTree = ""; }; + BA7FF0B31F6188C70054CF02 /* CollectionViewCellActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewCellActionsView.swift; sourceTree = ""; }; + BA7FF0B51F618F5A0054CF02 /* CollectionViewEditController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewEditController.swift; sourceTree = ""; }; + BA8203E11F15B4CC00925E93 /* ShareActivityController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareActivityController.swift; sourceTree = ""; }; + BAA0D91B1F4F165A00091284 /* PageIssuesTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PageIssuesTableViewController.swift; path = Wikipedia/Code/PageIssuesTableViewController.swift; sourceTree = SOURCE_ROOT; }; + BAFCE8411F1D7FD30077D5E9 /* AppearanceSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppearanceSettingsViewController.swift; sourceTree = ""; }; + BC23E4DB1C223DCB00B5AFDE /* WMFArticleRevisionFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFArticleRevisionFetcher.h; path = Wikipedia/Code/WMFArticleRevisionFetcher.h; sourceTree = SOURCE_ROOT; }; + BC23E4DC1C223DCB00B5AFDE /* WMFArticleRevisionFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFArticleRevisionFetcher.m; path = Wikipedia/Code/WMFArticleRevisionFetcher.m; sourceTree = SOURCE_ROOT; }; + BC23E4E01C223FAE00B5AFDE /* WMFArticleRevision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFArticleRevision.h; path = Wikipedia/Code/WMFArticleRevision.h; sourceTree = SOURCE_ROOT; }; + BC23E4E11C223FAE00B5AFDE /* WMFArticleRevision.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFArticleRevision.m; path = Wikipedia/Code/WMFArticleRevision.m; sourceTree = SOURCE_ROOT; }; + BC23E4E31C22429100B5AFDE /* WMFRevisionQueryResults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFRevisionQueryResults.h; path = Wikipedia/Code/WMFRevisionQueryResults.h; sourceTree = SOURCE_ROOT; }; + BC23E4E41C22429100B5AFDE /* WMFRevisionQueryResults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFRevisionQueryResults.m; path = Wikipedia/Code/WMFRevisionQueryResults.m; sourceTree = SOURCE_ROOT; }; + BC4273521A7C736800068882 /* WikipediaUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WikipediaUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BC45FF461C1B1F9F00BAE501 /* NSUserDefaults+WMFReset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSUserDefaults+WMFReset.h"; path = "WikipediaUnitTests/Code/NSUserDefaults+WMFReset.h"; sourceTree = SOURCE_ROOT; }; + BC45FF471C1B1F9F00BAE501 /* NSUserDefaults+WMFReset.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSUserDefaults+WMFReset.m"; path = "WikipediaUnitTests/Code/NSUserDefaults+WMFReset.m"; sourceTree = SOURCE_ROOT; }; + BC45FF491C1B22C200BAE501 /* NSObject+WMFReflection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+WMFReflection.h"; path = "WikipediaUnitTests/Code/NSObject+WMFReflection.h"; sourceTree = SOURCE_ROOT; }; + BC45FF4A1C1B22C200BAE501 /* NSObject+WMFReflection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+WMFReflection.m"; path = "WikipediaUnitTests/Code/NSObject+WMFReflection.m"; sourceTree = SOURCE_ROOT; }; + BC52D0F61C207D3300F625A9 /* TWNStringsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TWNStringsTests.m; sourceTree = ""; }; + BC62AE601C58FC810064C589 /* NSProcessInfo+WMFTestEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSProcessInfo+WMFTestEnvironment.h"; path = "WikipediaUnitTests/Code/NSProcessInfo+WMFTestEnvironment.h"; sourceTree = SOURCE_ROOT; }; + BC62AE611C58FC810064C589 /* NSProcessInfo+WMFTestEnvironment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSProcessInfo+WMFTestEnvironment.m"; path = "WikipediaUnitTests/Code/NSProcessInfo+WMFTestEnvironment.m"; sourceTree = SOURCE_ROOT; }; + BC62FFBE1C11064200533DA9 /* MWKImageInfoFetcher+PicOfTheDayInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MWKImageInfoFetcher+PicOfTheDayInfo.h"; path = "Wikipedia/Code/MWKImageInfoFetcher+PicOfTheDayInfo.h"; sourceTree = SOURCE_ROOT; }; + BC62FFBF1C11064200533DA9 /* MWKImageInfoFetcher+PicOfTheDayInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MWKImageInfoFetcher+PicOfTheDayInfo.m"; path = "Wikipedia/Code/MWKImageInfoFetcher+PicOfTheDayInfo.m"; sourceTree = SOURCE_ROOT; }; + BC8B4F1D1C77B29A009B06F7 /* LSNocilla+AnyRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "LSNocilla+AnyRequest.h"; path = "WikipediaUnitTests/Code/LSNocilla+AnyRequest.h"; sourceTree = SOURCE_ROOT; }; + BC90DE781C57C5AD007E0E81 /* WMFWelcomeLanguageViewControllerVisualTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFWelcomeLanguageViewControllerVisualTests.m; sourceTree = ""; }; + BCA15AE41C0E213300D0A3EA /* LoggingDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoggingDefaults.swift; path = Wikipedia/Code/LoggingDefaults.swift; sourceTree = SOURCE_ROOT; }; + BCA15B151C0F48EF00D0A3EA /* UIScreen+WMFImageWidth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIScreen+WMFImageWidth.h"; path = "Wikipedia/Code/UIScreen+WMFImageWidth.h"; sourceTree = SOURCE_ROOT; }; + BCA15B161C0F48EF00D0A3EA /* UIScreen+WMFImageWidth.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIScreen+WMFImageWidth.m"; path = "Wikipedia/Code/UIScreen+WMFImageWidth.m"; sourceTree = SOURCE_ROOT; }; + BCAF23141C6CF74C005F2D8D /* NSDictionary+WMFRequiredValueForKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+WMFRequiredValueForKey.h"; path = "Wikipedia/Code/NSDictionary+WMFRequiredValueForKey.h"; sourceTree = SOURCE_ROOT; }; + BCAF23151C6CF74C005F2D8D /* NSDictionary+WMFRequiredValueForKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+WMFRequiredValueForKey.m"; path = "Wikipedia/Code/NSDictionary+WMFRequiredValueForKey.m"; sourceTree = SOURCE_ROOT; }; + BCCB813B1C110702008BC602 /* NSDate+WMFPOTDTitle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDate+WMFPOTDTitle.h"; path = "Wikipedia/Code/NSDate+WMFPOTDTitle.h"; sourceTree = SOURCE_ROOT; }; + BCCB813C1C110702008BC602 /* NSDate+WMFPOTDTitle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDate+WMFPOTDTitle.m"; path = "Wikipedia/Code/NSDate+WMFPOTDTitle.m"; sourceTree = SOURCE_ROOT; }; + BCCFC44A1C84BAE0009D3613 /* CLLocation+WMFComparison.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "CLLocation+WMFComparison.h"; path = "Wikipedia/Code/CLLocation+WMFComparison.h"; sourceTree = SOURCE_ROOT; }; + BCCFC44B1C84BAE0009D3613 /* CLLocation+WMFComparison.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "CLLocation+WMFComparison.m"; path = "Wikipedia/Code/CLLocation+WMFComparison.m"; sourceTree = SOURCE_ROOT; }; + BCD31FFC1C6EB3EE00317D08 /* NSCalendar+WMFCommonCalendars.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCalendar+WMFCommonCalendars.h"; path = "Wikipedia/Code/NSCalendar+WMFCommonCalendars.h"; sourceTree = SOURCE_ROOT; }; + BCD31FFD1C6EB3EE00317D08 /* NSCalendar+WMFCommonCalendars.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSCalendar+WMFCommonCalendars.m"; path = "Wikipedia/Code/NSCalendar+WMFCommonCalendars.m"; sourceTree = SOURCE_ROOT; }; + BCD320081C6EC6BC00317D08 /* NSTimeZone+WMFTestingUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSTimeZone+WMFTestingUtils.h"; path = "WikipediaUnitTests/Code/NSTimeZone+WMFTestingUtils.h"; sourceTree = SOURCE_ROOT; }; + BCD320091C6EC6BC00317D08 /* NSTimeZone+WMFTestingUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSTimeZone+WMFTestingUtils.m"; path = "WikipediaUnitTests/Code/NSTimeZone+WMFTestingUtils.m"; sourceTree = SOURCE_ROOT; }; + BCD557B91C45B1600060A51A /* UIApplication+VisualTestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIApplication+VisualTestUtils.h"; path = "WikipediaUnitTests/Code/UIApplication+VisualTestUtils.h"; sourceTree = SOURCE_ROOT; }; + BCD557BA1C45B1600060A51A /* UIApplication+VisualTestUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIApplication+VisualTestUtils.m"; path = "WikipediaUnitTests/Code/UIApplication+VisualTestUtils.m"; sourceTree = SOURCE_ROOT; }; + BCF012321AD2FA38008D3675 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = ""; }; + D4991435181D51DE00E6073C /* Wikipedia.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Wikipedia.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D4991438181D51DE00E6073C /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + D499143A181D51DE00E6073C /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + D499143C181D51DE00E6073C /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + D4991453181D51DE00E6073C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D801C8521EB8E131001FA294 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8541EB8E131001FA294 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/Localizable.strings; sourceTree = ""; }; + D801C8551EB8E131001FA294 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8561EB8E131001FA294 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = ""; }; + D801C8571EB8E131001FA294 /* as */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = as; path = as.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8581EB8E131001FA294 /* as */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = as; path = as.lproj/Localizable.strings; sourceTree = ""; }; + D801C8591EB8E131001FA294 /* ast */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ast; path = ast.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C85A1EB8E131001FA294 /* ast */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ast; path = ast.lproj/Localizable.strings; sourceTree = ""; }; + D801C85E1EB8E131001FA294 /* ba */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ba; path = ba.lproj/Localizable.strings; sourceTree = ""; }; + D801C8681EB8E131001FA294 /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8691EB8E131001FA294 /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/Localizable.strings; sourceTree = ""; }; + D801C86A1EB8E131001FA294 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C86B1EB8E131001FA294 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/Localizable.strings; sourceTree = ""; }; + D801C86C1EB8E131001FA294 /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C86D1EB8E131001FA294 /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Localizable.strings; sourceTree = ""; }; + D801C86F1EB8E131001FA294 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8701EB8E131001FA294 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = ""; }; + D801C8711EB8E131001FA294 /* ce */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ce; path = ce.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8721EB8E131001FA294 /* ce */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ce; path = ce.lproj/Localizable.strings; sourceTree = ""; }; + D801C8731EB8E131001FA294 /* ckb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ckb; path = ckb.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8741EB8E131001FA294 /* ckb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ckb; path = ckb.lproj/Localizable.strings; sourceTree = ""; }; + D801C8771EB8E131001FA294 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8781EB8E131001FA294 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; + D801C8791EB8E131001FA294 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C87A1EB8E131001FA294 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/Localizable.strings; sourceTree = ""; }; + D801C87B1EB8E131001FA294 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C87C1EB8E131001FA294 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; + D801C87D1EB8E131001FA294 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C87E1EB8E131001FA294 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + D801C87F1EB8E131001FA294 /* diq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = diq; path = diq.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8801EB8E131001FA294 /* diq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = diq; path = diq.lproj/Localizable.strings; sourceTree = ""; }; + D801C8821EB8E131001FA294 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8831EB8E131001FA294 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = ""; }; + D801C8851EB8E131001FA294 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8861EB8E131001FA294 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + D801C8871EB8E131001FA294 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; + D801C8881EB8E131001FA294 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8891EB8E131001FA294 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/Localizable.strings; sourceTree = ""; }; + D801C88A1EB8E131001FA294 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C88B1EB8E131001FA294 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + D801C88C1EB8E131001FA294 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C88D1EB8E131001FA294 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Localizable.strings; sourceTree = ""; }; + D801C88E1EB8E131001FA294 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C88F1EB8E131001FA294 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/Localizable.strings; sourceTree = ""; }; + D801C8901EB8E131001FA294 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8911EB8E131001FA294 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; + D801C8921EB8E131001FA294 /* fo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fo; path = fo.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8931EB8E131001FA294 /* fo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fo; path = fo.lproj/Localizable.strings; sourceTree = ""; }; + D801C8941EB8E131001FA294 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8951EB8E131001FA294 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + D801C8981EB8E131001FA294 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8991EB8E131001FA294 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/Localizable.strings; sourceTree = ""; }; + D801C89C1EB8E131001FA294 /* haw */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = haw; path = haw.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C89D1EB8E131001FA294 /* haw */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = haw; path = haw.lproj/Localizable.strings; sourceTree = ""; }; + D801C89E1EB8E131001FA294 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C89F1EB8E131001FA294 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = ""; }; + D801C8A01EB8E131001FA294 /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi; path = hi.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8A11EB8E131001FA294 /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi; path = hi.lproj/Localizable.strings; sourceTree = ""; }; + D801C8A21EB8E131001FA294 /* hrx */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hrx; path = hrx.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8A41EB8E131001FA294 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8A51EB8E131001FA294 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/Localizable.strings; sourceTree = ""; }; + D801C8A61EB8E131001FA294 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8A71EB8E131001FA294 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; + D801C8A81EB8E131001FA294 /* hy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hy; path = hy.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8A91EB8E131001FA294 /* hy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hy; path = hy.lproj/Localizable.strings; sourceTree = ""; }; + D801C8AA1EB8E131001FA294 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8AB1EB8E131001FA294 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = ""; }; + D801C8AC1EB8E131001FA294 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8AD1EB8E131001FA294 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Localizable.strings; sourceTree = ""; }; + D801C8AE1EB8E131001FA294 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8AF1EB8E131001FA294 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; + D801C8B01EB8E131001FA294 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8B11EB8E131001FA294 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; + D801C8B21EB8E131001FA294 /* jv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = jv; path = jv.lproj/Localizable.strings; sourceTree = ""; }; + D801C8B31EB8E131001FA294 /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8B41EB8E131001FA294 /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/Localizable.strings; sourceTree = ""; }; + D801C8B51EB8E131001FA294 /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = km; path = km.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8B61EB8E131001FA294 /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = km; path = km.lproj/Localizable.strings; sourceTree = ""; }; + D801C8B71EB8E131001FA294 /* kn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kn; path = kn.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8B81EB8E131001FA294 /* kn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kn; path = kn.lproj/Localizable.strings; sourceTree = ""; }; + D801C8B91EB8E131001FA294 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8BA1EB8E131001FA294 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; + D801C8BB1EB8E131001FA294 /* krc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = krc; path = krc.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8BC1EB8E131001FA294 /* krc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = krc; path = krc.lproj/Localizable.strings; sourceTree = ""; }; + D801C8BD1EB8E131001FA294 /* ksh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ksh; path = ksh.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8BE1EB8E131001FA294 /* ksh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ksh; path = ksh.lproj/Localizable.strings; sourceTree = ""; }; + D801C8C11EB8E131001FA294 /* lb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lb; path = lb.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8C21EB8E131001FA294 /* lb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lb; path = lb.lproj/Localizable.strings; sourceTree = ""; }; + D801C8C31EB8E131001FA294 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8C41EB8E131001FA294 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = ""; }; + D801C8C51EB8E131001FA294 /* lv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lv; path = lv.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8C61EB8E131001FA294 /* lv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lv; path = lv.lproj/Localizable.strings; sourceTree = ""; }; + D801C8C71EB8E131001FA294 /* mai */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mai; path = mai.lproj/Localizable.strings; sourceTree = ""; }; + D801C8C91EB8E131001FA294 /* mk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mk; path = mk.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8CA1EB8E131001FA294 /* mk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mk; path = mk.lproj/Localizable.strings; sourceTree = ""; }; + D801C8CB1EB8E131001FA294 /* ml */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ml; path = ml.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8CC1EB8E131001FA294 /* ml */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ml; path = ml.lproj/Localizable.strings; sourceTree = ""; }; + D801C8CD1EB8E131001FA294 /* mr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mr; path = mr.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8CE1EB8E131001FA294 /* mr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mr; path = mr.lproj/Localizable.strings; sourceTree = ""; }; + D801C8CF1EB8E131001FA294 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8D01EB8E131001FA294 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/Localizable.strings; sourceTree = ""; }; + D801C8D21EB8E131001FA294 /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = my; path = my.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8D31EB8E131001FA294 /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = my; path = my.lproj/Localizable.strings; sourceTree = ""; }; + D801C8D51EB8E131001FA294 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8D61EB8E131001FA294 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = ""; }; + D801C8D71EB8E131001FA294 /* ne */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ne; path = ne.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8D81EB8E131001FA294 /* ne */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ne; path = ne.lproj/Localizable.strings; sourceTree = ""; }; + D801C8D91EB8E131001FA294 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8DA1EB8E131001FA294 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; + D801C8DB1EB8E131001FA294 /* oc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = oc; path = oc.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8DC1EB8E131001FA294 /* oc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = oc; path = oc.lproj/Localizable.strings; sourceTree = ""; }; + D801C8DE1EB8E131001FA294 /* om */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = om; path = om.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8DF1EB8E131001FA294 /* om */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = om; path = om.lproj/Localizable.strings; sourceTree = ""; }; + D801C8E01EB8E131001FA294 /* or */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = or; path = or.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8E11EB8E131001FA294 /* or */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = or; path = or.lproj/Localizable.strings; sourceTree = ""; }; + D801C8E21EB8E131001FA294 /* pa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pa; path = pa.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8E31EB8E131001FA294 /* pa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pa; path = pa.lproj/Localizable.strings; sourceTree = ""; }; + D801C8E41EB8E131001FA294 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8E51EB8E131001FA294 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; + D801C8E61EB8E131001FA294 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = pl; path = pl.lproj/Localizable.stringsdict; sourceTree = ""; }; + D801C8E71EB8E131001FA294 /* ps */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ps; path = ps.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8E81EB8E131001FA294 /* ps */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ps; path = ps.lproj/Localizable.strings; sourceTree = ""; }; + D801C8E91EB8E131001FA294 /* pt-br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-br"; path = "pt-br.lproj/InfoPlist.strings"; sourceTree = ""; }; + D801C8EA1EB8E131001FA294 /* pt-br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-br"; path = "pt-br.lproj/Localizable.strings"; sourceTree = ""; }; + D801C8EB1EB8E131001FA294 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8EC1EB8E131001FA294 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = ""; }; + D801C8EF1EB8E131001FA294 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8F01EB8E131001FA294 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = ""; }; + D801C8F11EB8E131001FA294 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8F21EB8E131001FA294 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + D801C8F31EB8E131001FA294 /* sa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sa; path = sa.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8F41EB8E131001FA294 /* sa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sa; path = sa.lproj/Localizable.strings; sourceTree = ""; }; + D801C8F51EB8E131001FA294 /* sah */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sah; path = sah.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8F61EB8E131001FA294 /* sah */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sah; path = sah.lproj/Localizable.strings; sourceTree = ""; }; + D801C8F71EB8E131001FA294 /* sco */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sco; path = sco.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8F81EB8E131001FA294 /* sco */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sco; path = sco.lproj/Localizable.strings; sourceTree = ""; }; + D801C8F91EB8E131001FA294 /* sd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sd; path = sd.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8FA1EB8E131001FA294 /* sd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sd; path = sd.lproj/Localizable.strings; sourceTree = ""; }; + D801C8FC1EB8E131001FA294 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8FD1EB8E131001FA294 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = ""; }; + D801C8FE1EB8E131001FA294 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C8FF1EB8E131001FA294 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Localizable.strings; sourceTree = ""; }; + D801C9001EB8E131001FA294 /* sr-EC */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sr-EC"; path = "sr-EC.lproj/InfoPlist.strings"; sourceTree = ""; }; + D801C9011EB8E131001FA294 /* sr-EC */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sr-EC"; path = "sr-EC.lproj/Localizable.strings"; sourceTree = ""; }; + D801C9031EB8E131001FA294 /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C9041EB8E131001FA294 /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/Localizable.strings; sourceTree = ""; }; + D801C9051EB8E131001FA294 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C9061EB8E131001FA294 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; + D801C9071EB8E131001FA294 /* sw */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sw; path = sw.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C9081EB8E131001FA294 /* sw */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sw; path = sw.lproj/Localizable.strings; sourceTree = ""; }; + D801C9091EB8E131001FA294 /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ta; path = ta.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C90A1EB8E131001FA294 /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ta; path = ta.lproj/Localizable.strings; sourceTree = ""; }; + D801C90B1EB8E131001FA294 /* tcy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tcy; path = tcy.lproj/Localizable.strings; sourceTree = ""; }; + D801C90C1EB8E131001FA294 /* te */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = te; path = te.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C90D1EB8E131001FA294 /* te */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = te; path = te.lproj/Localizable.strings; sourceTree = ""; }; + D801C90E1EB8E131001FA294 /* tg-cyrl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "tg-cyrl"; path = "tg-cyrl.lproj/InfoPlist.strings"; sourceTree = ""; }; + D801C90F1EB8E131001FA294 /* tg-cyrl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "tg-cyrl"; path = "tg-cyrl.lproj/Localizable.strings"; sourceTree = ""; }; + D801C9101EB8E131001FA294 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C9111EB8E131001FA294 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = ""; }; + D801C9121EB8E131001FA294 /* tl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tl; path = tl.lproj/Localizable.strings; sourceTree = ""; }; + D801C9131EB8E131001FA294 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C9141EB8E131001FA294 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; + D801C9151EB8E131001FA294 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C9161EB8E131001FA294 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = ""; }; + D801C9171EB8E131001FA294 /* ur */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ur; path = ur.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C9181EB8E131001FA294 /* ur */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ur; path = ur.lproj/Localizable.strings; sourceTree = ""; }; + D801C9191EB8E131001FA294 /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uz; path = uz.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C91A1EB8E131001FA294 /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uz; path = uz.lproj/Localizable.strings; sourceTree = ""; }; + D801C91B1EB8E131001FA294 /* vec */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vec; path = vec.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C91C1EB8E131001FA294 /* vec */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vec; path = vec.lproj/Localizable.strings; sourceTree = ""; }; + D801C91D1EB8E131001FA294 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C91E1EB8E131001FA294 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = ""; }; + D801C9211EB8E131001FA294 /* yi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = yi; path = yi.lproj/InfoPlist.strings; sourceTree = ""; }; + D801C9221EB8E131001FA294 /* yi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = yi; path = yi.lproj/Localizable.strings; sourceTree = ""; }; + D801C9231EB8E131001FA294 /* zh-hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-hans"; path = "zh-hans.lproj/InfoPlist.strings"; sourceTree = ""; }; + D801C9241EB8E131001FA294 /* zh-hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-hans"; path = "zh-hans.lproj/Localizable.strings"; sourceTree = ""; }; + D801C9251EB8E131001FA294 /* zh-hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-hant"; path = "zh-hant.lproj/InfoPlist.strings"; sourceTree = ""; }; + D801C9261EB8E131001FA294 /* zh-hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-hant"; path = "zh-hant.lproj/Localizable.strings"; sourceTree = ""; }; + D801C93A1EB9404A001FA294 /* WMFLocalization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFLocalization.h; path = Localization/WMFLocalization.h; sourceTree = ""; }; + D801C93B1EB9404A001FA294 /* WMFLocalization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFLocalization.m; path = Localization/WMFLocalization.m; sourceTree = ""; }; + D803F8951DC53B0C00656F20 /* GroupedAccessibilityView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GroupedAccessibilityView.swift; path = Wikipedia/Code/GroupedAccessibilityView.swift; sourceTree = SOURCE_ROOT; }; + D80877951EDE1EFB00CCA97E /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bs; path = bs.lproj/Localizable.stringsdict; sourceTree = ""; }; + D80877961EDE1F3300CCA97E /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/Localizable.stringsdict; sourceTree = ""; }; + D80877971EDE1F4F00CCA97E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nl; path = nl.lproj/Localizable.stringsdict; sourceTree = ""; }; + D808DCEA1E438BE300A3E89C /* PlaceSearchSuggestionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaceSearchSuggestionController.swift; sourceTree = ""; }; + D808DCEC1E438C0C00A3E89C /* PlaceSearch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaceSearch.swift; sourceTree = ""; }; + D808DCEE1E438C5100A3E89C /* MKCoordinateRegion+Dimensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MKCoordinateRegion+Dimensions.swift"; sourceTree = ""; }; + D80A79271F31E63C00EC06AB /* NSCharacterSet+WMFLinkParsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCharacterSet+WMFLinkParsing.h"; path = "WMF Framework/NSCharacterSet+WMFLinkParsing.h"; sourceTree = SOURCE_ROOT; }; + D80A79281F31E63C00EC06AB /* NSCharacterSet+WMFLinkParsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSCharacterSet+WMFLinkParsing.m"; path = "WMF Framework/NSCharacterSet+WMFLinkParsing.m"; sourceTree = SOURCE_ROOT; }; + D80ACD261EA0DD0000DC3F20 /* FLAnimatedImage+SafeForSwift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "FLAnimatedImage+SafeForSwift.h"; path = "Wikipedia/Code/FLAnimatedImage+SafeForSwift.h"; sourceTree = SOURCE_ROOT; }; + D80ACD271EA0DD0000DC3F20 /* FLAnimatedImage+SafeForSwift.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "FLAnimatedImage+SafeForSwift.m"; path = "Wikipedia/Code/FLAnimatedImage+SafeForSwift.m"; sourceTree = SOURCE_ROOT; }; + D80BF0A22347735E00B3B522 /* AppSearchButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSearchButton.swift; sourceTree = ""; }; + D80ED2581EE178A800CE8C50 /* Gradient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Gradient.swift; sourceTree = ""; }; + D80ED25A1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlacesSearchSuggestionTableViewCell.swift; sourceTree = ""; }; + D80ED25B1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PlacesSearchSuggestionTableViewCell.xib; sourceTree = ""; }; + D81082F61D0983AE0070DAC3 /* NSFileManager+WMFExtendedFileAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSFileManager+WMFExtendedFileAttributes.h"; path = "Wikipedia/Code/NSFileManager+WMFExtendedFileAttributes.h"; sourceTree = SOURCE_ROOT; }; + D81082F71D0983AE0070DAC3 /* NSFileManager+WMFExtendedFileAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSFileManager+WMFExtendedFileAttributes.m"; path = "Wikipedia/Code/NSFileManager+WMFExtendedFileAttributes.m"; sourceTree = SOURCE_ROOT; }; + D813FD9F1EC3419400FA4690 /* ArticleRightAlignedImageCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ArticleRightAlignedImageCollectionViewCell.swift; path = Wikipedia/Code/ArticleRightAlignedImageCollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; + D813FDA41EC34B2600FA4690 /* WMFArticle+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WMFArticle+Extensions.swift"; sourceTree = ""; }; + D8181FA42188DC1400FDEC59 /* String+Domains.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "String+Domains.swift"; path = "WMF Framework/String+Domains.swift"; sourceTree = SOURCE_ROOT; }; + D818D3801ED7254D0076110D /* ColumnarCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColumnarCollectionViewController.swift; sourceTree = ""; }; + D818D3851ED750E40076110D /* ArticleCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleCollectionViewController.swift; sourceTree = ""; }; + D818D38A1ED765470076110D /* ArticleLocationCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleLocationCollectionViewController.swift; sourceTree = ""; }; + D818D3AA1ED87E8F0076110D /* ArticleLocationCellUpdating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ArticleLocationCellUpdating.swift; path = Wikipedia/Code/ArticleLocationCellUpdating.swift; sourceTree = SOURCE_ROOT; }; + D818FEBA21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodemirrorSetupUserScript.swift; sourceTree = ""; }; + D81930D81E9F97B200554B19 /* WMFExploreFeedContentController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFExploreFeedContentController.h; sourceTree = ""; }; + D81930D91E9F97B200554B19 /* WMFExploreFeedContentController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFExploreFeedContentController.m; sourceTree = ""; }; + D81A28BD231E8F4C001CC77D /* ExtensionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionViewController.swift; sourceTree = ""; }; + D81E5F871E5F2C8400E1A80C /* UIApplication+SystemSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIApplication+SystemSettings.swift"; path = "Wikipedia/Code/UIApplication+SystemSettings.swift"; sourceTree = SOURCE_ROOT; }; + D81E5F891E5F949B00E1A80C /* WMFAssertions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFAssertions.h; path = Wikipedia/Code/WMFAssertions.h; sourceTree = SOURCE_ROOT; }; + D81EF2D61F1D2F0500D26D3F /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cs; path = cs.lproj/Localizable.stringsdict; sourceTree = ""; }; + D82117FB1EE58C080076C040 /* MapAnnotation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapAnnotation.swift; sourceTree = ""; }; + D826C51421766E570012F940 /* Collection+AsyncMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+AsyncMap.swift"; sourceTree = ""; }; + D826C51621766F1A0012F940 /* BackgroundFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundFetcher.swift; sourceTree = ""; usesTabs = 0; }; + D826C51A217741C50012F940 /* ReachabilityNotifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReachabilityNotifier.swift; sourceTree = ""; }; + D82972821E3950100061550A /* ArticlePlace.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticlePlace.swift; sourceTree = ""; }; + D82972861E3A49980061550A /* ArticlePopoverViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticlePopoverViewController.swift; sourceTree = ""; }; + D82972871E3A49980061550A /* ArticlePopoverViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ArticlePopoverViewController.xib; sourceTree = ""; }; + D82C3A98213451100073EEAC /* DeviceInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceInfo.swift; sourceTree = ""; }; + D82CA32E2020E87D005C2D5C /* ReadingListsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ReadingListsOperation.swift; path = "WMF Framework/ReadingListsOperation.swift"; sourceTree = SOURCE_ROOT; }; + D82CA3322020E8D8005C2D5C /* ReadingListsSyncOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ReadingListsSyncOperation.swift; path = "WMF Framework/ReadingListsSyncOperation.swift"; sourceTree = SOURCE_ROOT; }; + D82E954B1F15397D007BD960 /* ThemeableTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeableTextField.swift; sourceTree = ""; }; + D82E95691F156F77007BD960 /* UIView+SubviewEnumeration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+SubviewEnumeration.swift"; sourceTree = ""; }; + D82E95821F16502E007BD960 /* WMFLanguagesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFLanguagesViewController.h; sourceTree = ""; }; + D82E95831F16502E007BD960 /* WMFLanguagesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFLanguagesViewController.m; sourceTree = ""; }; + D82E95841F16502E007BD960 /* WMFLanguagesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WMFLanguagesViewController.xib; sourceTree = ""; }; + D834DAA823E8538700B7B0E9 /* Wikipedia 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Wikipedia 3.xcdatamodel"; sourceTree = ""; }; + D837B5A51F06AA8C00DCB9CD /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; + D837B5A71F06E5C600DCB9CD /* DateFormatter+WikipediaLanguage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DateFormatter+WikipediaLanguage.swift"; sourceTree = ""; }; + D837B5A91F0D0D1600DCB9CD /* WMFFeedOnThisDayEvent+LocalizedDates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "WMFFeedOnThisDayEvent+LocalizedDates.swift"; path = "WMF Framework/WMFFeedOnThisDayEvent+LocalizedDates.swift"; sourceTree = SOURCE_ROOT; }; + D837B5B11F0D68B800DCB9CD /* URL+LinkParsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "URL+LinkParsing.swift"; path = "Wikipedia/Code/URL+LinkParsing.swift"; sourceTree = SOURCE_ROOT; }; + D837CC36231FE9CC00BA6130 /* ThemeableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeableViewController.swift; sourceTree = ""; }; + D8396D1A22CF7052005625D8 /* WMFArticleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMFArticleTests.swift; sourceTree = ""; }; + D83C5ABA1F2281A90066C892 /* AnnouncementCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AnnouncementCollectionViewCell.swift; path = ../Wikipedia/Code/AnnouncementCollectionViewCell.swift; sourceTree = ""; }; + D8421B51203CC8420040F50B /* DebugReadingListsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugReadingListsViewController.swift; sourceTree = ""; }; + D8421B52203CC8420040F50B /* DebugReadingListsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DebugReadingListsViewController.xib; sourceTree = ""; }; + D844480E1DDA33D900425630 /* Wikipedia.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Wikipedia.xcdatamodel; sourceTree = ""; }; + D84448201DDB60FF00425630 /* WMFArticle+Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WMFArticle+Extensions.h"; sourceTree = ""; }; + D84448211DDB60FF00425630 /* WMFArticle+Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WMFArticle+Extensions.m"; sourceTree = ""; }; + D84448241DDB632100425630 /* WMFArticle+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WMFArticle+CoreDataClass.h"; sourceTree = ""; }; + D84448251DDB632100425630 /* WMFArticle+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WMFArticle+CoreDataClass.m"; sourceTree = ""; }; + D84448261DDB632100425630 /* WMFArticle+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WMFArticle+CoreDataProperties.h"; sourceTree = ""; }; + D84448271DDB632100425630 /* WMFArticle+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WMFArticle+CoreDataProperties.m"; sourceTree = ""; }; + D84448541DDCE49D00425630 /* WMFContentGroup+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WMFContentGroup+CoreDataClass.h"; sourceTree = ""; }; + D84448551DDCE49D00425630 /* WMFContentGroup+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WMFContentGroup+CoreDataClass.m"; sourceTree = ""; }; + D84448561DDCE49D00425630 /* WMFContentGroup+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WMFContentGroup+CoreDataProperties.h"; sourceTree = ""; }; + D84448571DDCE49D00425630 /* WMFContentGroup+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WMFContentGroup+CoreDataProperties.m"; sourceTree = ""; }; + D844485C1DDCE4E500425630 /* WMFContentGroup+Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WMFContentGroup+Extensions.h"; sourceTree = ""; }; + D844485D1DDCE4E500425630 /* WMFContentGroup+Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WMFContentGroup+Extensions.m"; sourceTree = ""; usesTabs = 0; }; + D844D96C1D6CB2600042D692 /* WMF.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WMF.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D844D96E1D6CB2600042D692 /* WMF.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = WMF.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + D844D96F1D6CB2600042D692 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D84649AC1D4514F7009DB4A0 /* WMFTaskGroupTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFTaskGroupTests.m; sourceTree = ""; }; + D84692DE1D5E1E3F000A7058 /* TableOfContentsHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TableOfContentsHeader.swift; path = Wikipedia/Code/TableOfContentsHeader.swift; sourceTree = SOURCE_ROOT; }; + D84692DF1D5E1E3F000A7058 /* TableOfContentsHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = TableOfContentsHeader.xib; path = Wikipedia/Code/TableOfContentsHeader.xib; sourceTree = SOURCE_ROOT; }; + D8479FAB1F222FE80025FD7A /* Wikipedia Stickers.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Wikipedia Stickers.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + D8479FAD1F222FE90025FD7A /* Stickers.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Stickers.xcassets; sourceTree = ""; }; + D8479FAF1F222FE90025FD7A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D8494AD81D6C85C500337433 /* NSURL+WMFLinkParsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSURL+WMFLinkParsing.h"; path = "Wikipedia/Code/NSURL+WMFLinkParsing.h"; sourceTree = SOURCE_ROOT; }; + D8494AD91D6C85C500337433 /* NSURL+WMFLinkParsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSURL+WMFLinkParsing.m"; path = "Wikipedia/Code/NSURL+WMFLinkParsing.m"; sourceTree = SOURCE_ROOT; }; + D8494ADA1D6C85C500337433 /* NSURLComponents+WMFLinkParsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSURLComponents+WMFLinkParsing.h"; path = "Wikipedia/Code/NSURLComponents+WMFLinkParsing.h"; sourceTree = SOURCE_ROOT; }; + D8494ADB1D6C85C500337433 /* NSURLComponents+WMFLinkParsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSURLComponents+WMFLinkParsing.m"; path = "Wikipedia/Code/NSURLComponents+WMFLinkParsing.m"; sourceTree = SOURCE_ROOT; }; + D8494ADC1D6C85C500337433 /* NSUserDefaults+WMFExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSUserDefaults+WMFExtensions.swift"; path = "Wikipedia/Code/NSUserDefaults+WMFExtensions.swift"; sourceTree = SOURCE_ROOT; }; + D8497F5D1EE027D700100CBD /* hrx */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hrx; path = hrx.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8497F631EE09BE600100CBD /* CircledRankView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CircledRankView.swift; path = Wikipedia/Code/CircledRankView.swift; sourceTree = SOURCE_ROOT; }; + D8497F681EE09D0200100CBD /* SizeThatFitsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SizeThatFitsView.swift; path = Wikipedia/Code/SizeThatFitsView.swift; sourceTree = SOURCE_ROOT; }; + D8497F6D1EE09FA100100CBD /* RankedArticleCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RankedArticleCollectionViewCell.swift; path = Wikipedia/Code/RankedArticleCollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; + D84BF62E1DBE616D00E0C85E /* UIViewController+WMFAlerts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+WMFAlerts.swift"; sourceTree = ""; }; + D84C363F1F3245A200895FA1 /* ArticleCollectionViewCell+Themeable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "ArticleCollectionViewCell+Themeable.swift"; path = "WMF Framework/ArticleCollectionViewCell+Themeable.swift"; sourceTree = SOURCE_ROOT; }; + D84C36511F33866C00895FA1 /* WMF Framework.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "WMF Framework.xcassets"; sourceTree = ""; }; + D84DAA091EEEF527008E4B18 /* SWStepSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SWStepSlider.swift; sourceTree = ""; }; + D84E2A9A1FFBCAF600878968 /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ta; path = ta.lproj/Localizable.stringsdict; sourceTree = ""; }; + D84E2A9B1FFBCB9400878968 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = pt; path = pt.lproj/Localizable.stringsdict; sourceTree = ""; }; + D84E2A9C1FFBCBBB00878968 /* lv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lv; path = lv.lproj/Localizable.stringsdict; sourceTree = ""; }; + D84E2A9D1FFBCBE900878968 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Localizable.stringsdict; sourceTree = ""; }; + D84E2A9E1FFBCC0500878968 /* ne */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ne; path = ne.lproj/Localizable.stringsdict; sourceTree = ""; }; + D84F2BF71D2FEE6300963D42 /* WMFRandomDiceButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFRandomDiceButton.h; sourceTree = ""; }; + D84F2BF81D2FEE6300963D42 /* WMFRandomDiceButton.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = WMFRandomDiceButton.html; sourceTree = ""; }; + D84F2BF91D2FEE6300963D42 /* WMFRandomDiceButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFRandomDiceButton.m; sourceTree = ""; }; + D84F2BFA1D2FEE6300963D42 /* WMFRandomDiceButtonRoll.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = WMFRandomDiceButtonRoll.js; sourceTree = ""; }; + D84F2C011D30162700963D42 /* WMFFirstRandomViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFFirstRandomViewController.h; sourceTree = ""; }; + D84F2C021D30162700963D42 /* WMFFirstRandomViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFFirstRandomViewController.m; sourceTree = ""; }; + D84F92401DC161DA00114C2F /* NSDictionary+WMFPageViewsSortedByDate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+WMFPageViewsSortedByDate.h"; path = "Wikipedia/Code/NSDictionary+WMFPageViewsSortedByDate.h"; sourceTree = SOURCE_ROOT; }; + D84F92411DC161DA00114C2F /* NSDictionary+WMFPageViewsSortedByDate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+WMFPageViewsSortedByDate.m"; path = "Wikipedia/Code/NSDictionary+WMFPageViewsSortedByDate.m"; sourceTree = SOURCE_ROOT; }; + D850A5381F8686DE006FD295 /* WMFThemeableNavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMFThemeableNavigationController.h; sourceTree = ""; }; + D850A5391F8686DE006FD295 /* WMFThemeableNavigationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMFThemeableNavigationController.m; sourceTree = ""; }; + D85219371D6DEFBB00084796 /* WMFTodayContinueReadingWidgetViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFTodayContinueReadingWidgetViewController.swift; sourceTree = ""; }; + D8533ED31ECF581600E44F86 /* NewsCollectionViewHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsCollectionViewHeader.swift; sourceTree = ""; }; + D8533ED41ECF581600E44F86 /* NewsCollectionViewHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NewsCollectionViewHeader.xib; sourceTree = ""; }; + D8543230218879D000E895B5 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Configuration.swift; path = "WMF Framework/Configuration.swift"; sourceTree = SOURCE_ROOT; usesTabs = 0; }; + D858A7DE1DA6A04A009C3DEB /* WMFDateCalculationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFDateCalculationTests.m; sourceTree = ""; }; + D858C7B5210B91CD0039E0C9 /* PassthroughView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassthroughView.swift; sourceTree = ""; }; + D85BD2441F8F9D6900D0D478 /* NSManagedObjectContext+WMFKeyValue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSManagedObjectContext+WMFKeyValue.h"; sourceTree = ""; }; + D85BD2451F8F9D6900D0D478 /* NSManagedObjectContext+WMFKeyValue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObjectContext+WMFKeyValue.m"; sourceTree = ""; }; + D85C145A1F388582002186A5 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Localizable.strings; sourceTree = ""; }; + D85C145B1F38859A002186A5 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = kab; path = kab.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29351EC48F12007EF868 /* ast */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ast; path = ast.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29361EC48F5D007EF868 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = af; path = af.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29371EC4909C007EF868 /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bn; path = bn.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29381EC490C4007EF868 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = br; path = br.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29391EC490F1007EF868 /* ckb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ckb; path = ckb.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C293A1EC49148007EF868 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = fr.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C293B1EC4915A007EF868 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C293C1EC4915C007EF868 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fi; path = fi.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29401EC49196007EF868 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29411EC491D8007EF868 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29421EC49238007EF868 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = eu; path = eu.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29431EC49252007EF868 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fa; path = fa.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29441EC4928A007EF868 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = gl; path = gl.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29451EC49299007EF868 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = he; path = he.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29461EC492AC007EF868 /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hi; path = hi.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29471EC492CB007EF868 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = id; path = id.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29481EC492E1007EF868 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = it; path = it.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29491EC49304007EF868 /* jv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = jv; path = jv.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C294A1EC49323007EF868 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ko; path = ko.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C294B1EC4932F007EF868 /* lb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lb; path = lb.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C294C1EC49345007EF868 /* mk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = mk; path = mk.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C294D1EC49356007EF868 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ms; path = ms.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C294E1EC4938A007EF868 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nb; path = nb.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29501EC493B6007EF868 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Localizable.stringsdict"; sourceTree = ""; }; + D85C29511EC493DE007EF868 /* sd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sd; path = sd.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29521EC4940A007EF868 /* sr-EC */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "sr-EC"; path = "sr-EC.lproj/Localizable.stringsdict"; sourceTree = ""; }; + D85C29531EC4945B007EF868 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sv; path = sv.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29541EC4946D007EF868 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/Localizable.stringsdict; sourceTree = ""; }; + D85C29551EC4947B007EF868 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.stringsdict"; sourceTree = ""; }; + D85C29561EC49485007EF868 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.stringsdict"; sourceTree = ""; }; + D85F56A1219C45C900AF3E13 /* URLComponents+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "URLComponents+Extensions.swift"; path = "WMF Framework/URLComponents+Extensions.swift"; sourceTree = SOURCE_ROOT; }; + D8619B9E1FBB10240045C8BC /* ReadingList+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReadingList+CoreDataClass.swift"; sourceTree = ""; }; + D8619B9F1FBB10240045C8BC /* ReadingList+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReadingList+CoreDataProperties.swift"; sourceTree = ""; }; + D8619BA01FBB10240045C8BC /* ReadingListEntry+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReadingListEntry+CoreDataClass.swift"; sourceTree = ""; }; + D8619BA11FBB10240045C8BC /* ReadingListEntry+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReadingListEntry+CoreDataProperties.swift"; sourceTree = ""; }; + D8635AE7216E2BFC001A7C00 /* HTTPCookieStorage+Migration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HTTPCookieStorage+Migration.swift"; sourceTree = ""; }; + D864D68B1DA3EA3800B86934 /* NumberFormatterExtrasTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberFormatterExtrasTests.swift; sourceTree = ""; }; + D864D68D1DA3EDF900B86934 /* NSNumberFormatter+WMFExtras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSNumberFormatter+WMFExtras.swift"; path = "Wikipedia/Code/NSNumberFormatter+WMFExtras.swift"; sourceTree = SOURCE_ROOT; }; + D8650B7920350FEE0044DFFA /* NSString+SHA256.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+SHA256.h"; sourceTree = ""; }; + D8650B7A20350FEE0044DFFA /* NSString+SHA256.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+SHA256.m"; sourceTree = ""; }; + D87021601EBA63EE000D02D6 /* localization */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = localization; sourceTree = BUILT_PRODUCTS_DIR; }; + D87233FF1E1FF0A500751E83 /* PlacesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlacesViewController.swift; sourceTree = ""; }; + D87234031E1FF18100751E83 /* Places.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Places.storyboard; sourceTree = ""; }; + D8726D421EBA052900A107D0 /* Localization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Localization.swift; path = Localization/Localization.swift; sourceTree = ""; }; + D8733C8A1ECA10930011E379 /* LabelGroupAccessibilityElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelGroupAccessibilityElement.swift; sourceTree = ""; }; + D8733C8C1ECA14DD0011E379 /* ArticleCollectionViewCell+WMFFeedContentDisplaying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "ArticleCollectionViewCell+WMFFeedContentDisplaying.swift"; path = "Wikipedia/Code/ArticleCollectionViewCell+WMFFeedContentDisplaying.swift"; sourceTree = SOURCE_ROOT; }; + D8733C911ECA16580011E379 /* UIView+SemanticContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+SemanticContent.swift"; path = "WMF Framework/UIView+SemanticContent.swift"; sourceTree = SOURCE_ROOT; }; + D8733C931ECA16940011E379 /* HasText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HasText.swift; sourceTree = ""; }; + D8733C951ECA48490011E379 /* CollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CollectionViewCell.swift; path = Wikipedia/Code/CollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; + D87647471F1F9C2500D02CA4 /* CommonStrings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonStrings.swift; sourceTree = ""; usesTabs = 0; }; + D876769E21E7B73C00491039 /* ToolbarSeparatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarSeparatorView.swift; sourceTree = ""; }; + D876C2851E5CDE6500FCA00A /* AlignedImageButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AlignedImageButton.swift; path = ../Wikipedia/Code/AlignedImageButton.swift; sourceTree = ""; }; + D87721F21EC0CDB60005E634 /* ArticleCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ArticleCollectionViewCell.swift; path = Wikipedia/Code/ArticleCollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; + D87721FC1EC0DCA30005E634 /* SaveButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SaveButton.swift; path = ../Wikipedia/Code/SaveButton.swift; sourceTree = ""; }; + D87914DC1DFA04E10012C5DA /* NSUserDefaults+WMFApplicationDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSUserDefaults+WMFApplicationDefaults.swift"; sourceTree = ""; }; + D87F1D3C1EC0ACC400575CF8 /* AsyncOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncOperation.swift; sourceTree = ""; }; + D8800CB01E2FF5B70035D2DB /* QuadKeyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuadKeyTests.swift; sourceTree = ""; }; + D880652E218C732800BF7B91 /* WorkerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkerController.swift; sourceTree = ""; }; + D881B1121E32874500D33F62 /* WMFArticle+QuadKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "WMFArticle+QuadKey.swift"; path = "WMF Framework/WMFArticle+QuadKey.swift"; sourceTree = SOURCE_ROOT; }; + D8831D381EC33F1D008CA89A /* ArticleFullWidthImageCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ArticleFullWidthImageCollectionViewCell.swift; path = Wikipedia/Code/ArticleFullWidthImageCollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; + D88C70171EE595E90022A26A /* MapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; + D88E0E1C1EBB5A97005B8E9E /* Bundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; + D88FCADE1E4B74D300505A9F /* WikidataFetcher+Places.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WikidataFetcher+Places.swift"; sourceTree = ""; }; + D88FCAE01E4B776600505A9F /* MapUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapUtilities.swift; sourceTree = ""; }; + D890C85C1D772ED3007132C9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; + D890C85E1D772EE7007132C9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + D8940CED1DB56C8A00E17F9E /* NewsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsViewController.swift; sourceTree = ""; }; + D895D0841D9C1EB8005418C1 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; + D895D0861D9C1EB8005418C1 /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; }; + D896C7941D6F2E4E007101DF /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.h"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + D896C7951D6F2E4E007101DF /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m"; sourceTree = ""; }; + D89845211ECB3F6C00849DA4 /* CGRect+Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGRect+Layout.swift"; sourceTree = ""; }; + D89845281ECC8A1700849DA4 /* NewsCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NewsCollectionViewCell.swift; path = Wikipedia/Code/NewsCollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; + D8987E001E325C7900E75DA6 /* WMFKeyValue+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WMFKeyValue+CoreDataClass.h"; sourceTree = ""; }; + D8987E011E325C7A00E75DA6 /* WMFKeyValue+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WMFKeyValue+CoreDataClass.m"; sourceTree = ""; }; + D8987E021E325C7A00E75DA6 /* WMFKeyValue+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WMFKeyValue+CoreDataProperties.h"; sourceTree = ""; }; + D8987E031E325C7A00E75DA6 /* WMFKeyValue+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WMFKeyValue+CoreDataProperties.m"; sourceTree = ""; }; + D89927DC1ED310540008F54C /* yi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = yi; path = yi.lproj/Localizable.stringsdict; sourceTree = ""; }; + D89927DD1ED310B60008F54C /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = su; path = su.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8A42C2B1E815A9C00D8E281 /* User Testing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "User Testing.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + D8A42C2D1E815B0700D8E281 /* User Testing-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "User Testing-Info.plist"; sourceTree = ""; }; + D8A42C2F1E815C1300D8E281 /* Wikipedia User Testing.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Wikipedia User Testing.entitlements"; sourceTree = ""; }; + D8A47C8423D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoIntrinsicContentSizeImageView.swift; sourceTree = ""; }; + D8A47C8923D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleTableOfContentsDisplayController.swift; sourceTree = ""; }; + D8A47C8E23D7338C002AA823 /* ArticleViewController+TableOfContents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+TableOfContents.swift"; sourceTree = ""; }; + D8A6BAEC1E4C9BF400A981C8 /* UserLocationAnnotationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserLocationAnnotationView.swift; sourceTree = ""; }; + D8A6BAEE1E4C9C0700A981C8 /* ArticlePlaceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticlePlaceView.swift; sourceTree = ""; }; + D8A76D811D6F2B2E00E5A798 /* WMFTaskGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFTaskGroup.h; path = Wikipedia/Code/WMFTaskGroup.h; sourceTree = SOURCE_ROOT; }; + D8A76D821D6F2B2E00E5A798 /* WMFTaskGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFTaskGroup.m; path = Wikipedia/Code/WMFTaskGroup.m; sourceTree = SOURCE_ROOT; }; + D8AAF6B71FE93DE9005760E6 /* UIScrollView+Limits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScrollView+Limits.swift"; sourceTree = ""; }; + D8B166841FD97A0500097D8B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + D8B1668A1FD97FE000097D8B /* WMFViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMFViewController.h; sourceTree = ""; }; + D8B1668B1FD97FE000097D8B /* WMFViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WMFViewController.m; sourceTree = ""; }; + D8B3D7651EC34F5B00930C21 /* SaveButtonsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveButtonsController.swift; sourceTree = ""; }; + D8B589A521CD05070027083A /* languages */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = languages; sourceTree = BUILT_PRODUCTS_DIR; }; + D8BD63BE1EA7E28700BBC082 /* SummaryExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SummaryExtensions.swift; sourceTree = ""; }; + D8BDA8C01E71C0760031F4BF /* WMFBlocksKitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFBlocksKitTests.m; sourceTree = ""; }; + D8C41DDA23FC09EE00353DCE /* NSManagedObjectContext+History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+History.swift"; sourceTree = ""; }; + D8C4D3D01FD5D9250089CEC2 /* TUSafariActivity.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TUSafariActivity.bundle; sourceTree = ""; }; + D8C4D3D11FD5D9250089CEC2 /* TUSafariActivity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUSafariActivity.h; sourceTree = ""; }; + D8C4D3D21FD5D9250089CEC2 /* TUSafariActivity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TUSafariActivity.m; sourceTree = ""; }; + D8C8C21F20113E6200B3317B /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sq; path = sq.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8C8C22020113EA000B3317B /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = is; path = is.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8C8C22120113F0500B3317B /* ps */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ps; path = ps.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8CB32AC1E79D8A0008A0966 /* RoundedCornerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoundedCornerView.swift; sourceTree = ""; }; + D8CC94D9217897FB007293E7 /* NSManagedObject+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+Extensions.swift"; sourceTree = ""; }; + D8CD975F1E83F68400ECCA9D /* SafariServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SafariServices.framework; path = System/Library/Frameworks/SafariServices.framework; sourceTree = SDKROOT; }; + D8CD97641E83FAB400ECCA9D /* Cache.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Cache.xcdatamodel; sourceTree = ""; }; + D8CE26AF1E698E2400DAE2E0 /* Experimental.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Experimental.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D8CE26B01E698E2500DAE2E0 /* Experimental-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Experimental-Info.plist"; path = "Wikipedia/Experimental-Info.plist"; sourceTree = SOURCE_ROOT; }; + D8CE9AFD1FDEB14E00AE7D49 /* NavigationBarHider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NavigationBarHider.swift; path = ../Wikipedia/Code/NavigationBarHider.swift; sourceTree = ""; }; + D8D0CC361DF6F8C30031EDD9 /* UIFont+WMFDynamicType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIFont+WMFDynamicType.swift"; sourceTree = ""; }; + D8D270391D75ED5000D093A8 /* WMFArticlePreviewViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WMFArticlePreviewViewController.swift; path = Wikipedia/Code/WMFArticlePreviewViewController.swift; sourceTree = SOURCE_ROOT; }; + D8D2703A1D75ED5000D093A8 /* WMFArticlePreviewViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = WMFArticlePreviewViewController.xib; path = Wikipedia/Code/WMFArticlePreviewViewController.xib; sourceTree = SOURCE_ROOT; }; + D8D365141E953C7100593A38 /* ImageControllerCompletionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageControllerCompletionManager.swift; path = Wikipedia/Code/ImageControllerCompletionManager.swift; sourceTree = SOURCE_ROOT; }; + D8D5507F1DF0D2BD00B90177 /* NSArray+WMFMatching.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+WMFMatching.h"; path = "WikipediaUnitTests/Code/NSArray+WMFMatching.h"; sourceTree = SOURCE_ROOT; }; + D8D550801DF0D2BD00B90177 /* NSArray+WMFMatching.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSArray+WMFMatching.m"; path = "WikipediaUnitTests/Code/NSArray+WMFMatching.m"; sourceTree = SOURCE_ROOT; }; + D8D551411DF1A33D00B90177 /* EXTScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTScope.h; sourceTree = ""; }; + D8D551421DF1A33D00B90177 /* EXTScope.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EXTScope.m; sourceTree = ""; }; + D8D551431DF1A33D00B90177 /* metamacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metamacros.h; sourceTree = ""; }; + D8D553611DF1B63200B90177 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + D8D92B491DF22E1700B95311 /* NotificationBackgroundError.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = NotificationBackgroundError.png; sourceTree = ""; }; + D8D92B4A1DF22E1700B95311 /* NotificationBackgroundError@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NotificationBackgroundError@2x.png"; sourceTree = ""; }; + D8D92B4B1DF22E1700B95311 /* NotificationBackgroundErrorIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = NotificationBackgroundErrorIcon.png; sourceTree = ""; }; + D8D92B4C1DF22E1700B95311 /* NotificationBackgroundErrorIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NotificationBackgroundErrorIcon@2x.png"; sourceTree = ""; }; + D8D92B4D1DF22E1700B95311 /* NotificationBackgroundMessage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = NotificationBackgroundMessage.png; sourceTree = ""; }; + D8D92B4E1DF22E1700B95311 /* NotificationBackgroundMessage@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NotificationBackgroundMessage@2x.png"; sourceTree = ""; }; + D8D92B4F1DF22E1700B95311 /* NotificationBackgroundSuccess.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = NotificationBackgroundSuccess.png; sourceTree = ""; }; + D8D92B501DF22E1700B95311 /* NotificationBackgroundSuccess@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NotificationBackgroundSuccess@2x.png"; sourceTree = ""; }; + D8D92B511DF22E1700B95311 /* NotificationBackgroundSuccessIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = NotificationBackgroundSuccessIcon.png; sourceTree = ""; }; + D8D92B521DF22E1700B95311 /* NotificationBackgroundSuccessIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NotificationBackgroundSuccessIcon@2x.png"; sourceTree = ""; }; + D8D92B531DF22E1700B95311 /* NotificationBackgroundWarning.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = NotificationBackgroundWarning.png; sourceTree = ""; }; + D8D92B541DF22E1700B95311 /* NotificationBackgroundWarning@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NotificationBackgroundWarning@2x.png"; sourceTree = ""; }; + D8D92B551DF22E1700B95311 /* NotificationBackgroundWarningIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = NotificationBackgroundWarningIcon.png; sourceTree = ""; }; + D8D92B561DF22E1700B95311 /* NotificationBackgroundWarningIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NotificationBackgroundWarningIcon@2x.png"; sourceTree = ""; }; + D8D92B571DF22E1700B95311 /* NotificationButtonBackground.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = NotificationButtonBackground.png; sourceTree = ""; }; + D8D92B581DF22E1700B95311 /* NotificationButtonBackground@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NotificationButtonBackground@2x.png"; sourceTree = ""; }; + D8D92B591DF22E1700B95311 /* TSMessagesDefaultDesign.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = TSMessagesDefaultDesign.json; sourceTree = ""; }; + D8DC16FC1D6F709300D6D9FB /* CoreImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreImage.framework; path = System/Library/Frameworks/CoreImage.framework; sourceTree = SDKROOT; }; + D8E27B9D1F82AFE600F9D2B3 /* RMessage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RMessage.m; sourceTree = ""; }; + D8E27B9E1F82AFE600F9D2B3 /* RMessageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RMessageView.m; sourceTree = ""; }; + D8E27B9F1F82AFE600F9D2B3 /* RMessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RMessage.h; sourceTree = ""; }; + D8E27BA01F82AFE600F9D2B3 /* RMessageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RMessageView.h; sourceTree = ""; }; + D8E27BAC1F82B4FF00F9D2B3 /* RMessageDefaultDesign.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = RMessageDefaultDesign.json; sourceTree = ""; }; + D8E27BB21F82B5CB00F9D2B3 /* RMessageView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RMessageView.xib; sourceTree = ""; }; + D8E6FF6624054FA100686272 /* ArticleViewController+LinkPreviewing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+LinkPreviewing.swift"; sourceTree = ""; usesTabs = 0; }; + D8E6FF6B24056AC300686272 /* ArticleViewController+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+ContextMenu.swift"; sourceTree = ""; }; + D8E6FF7524058AC600686272 /* WMFWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFWebView.h; sourceTree = ""; }; + D8E6FF7624058AC600686272 /* WMFWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFWebView.m; sourceTree = ""; }; + D8E6FF7B2405AAC400686272 /* ArticleViewController+Analytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArticleViewController+Analytics.swift"; sourceTree = ""; }; + D8E78FA31FB4C8250094B968 /* ReadingListsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ReadingListsController.swift; path = Wikipedia/Code/ReadingListsController.swift; sourceTree = SOURCE_ROOT; }; + D8E78FA51FB4C8740094B968 /* Session.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = ""; }; + D8E892242176124F00587F61 /* PeriodicWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeriodicWorker.swift; sourceTree = ""; }; + D8EBD1B71FBB13EE00AA7DA9 /* ReadingList+JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReadingList+JSON.swift"; sourceTree = ""; }; + D8EBD1BB1FBB177D00AA7DA9 /* ReadingListEntry+JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReadingListEntry+JSON.swift"; sourceTree = ""; }; + D8EC3FB41E9BDA35006712EB /* Staging.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Staging.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D8EC3FB61E9BDAAB006712EB /* Staging-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Staging-Info.plist"; sourceTree = ""; }; + D8EC64021D007B1F00C286EE /* WMFLinkParsingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFLinkParsingTests.m; sourceTree = ""; }; + D8EEA0F81D6E21A400D88143 /* NSFileManager+WMFGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSFileManager+WMFGroup.h"; path = "Wikipedia/Code/NSFileManager+WMFGroup.h"; sourceTree = SOURCE_ROOT; }; + D8EEA0F91D6E21A400D88143 /* NSFileManager+WMFGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSFileManager+WMFGroup.m"; path = "Wikipedia/Code/NSFileManager+WMFGroup.m"; sourceTree = SOURCE_ROOT; }; + D8F1BF241D9C2AFB00036E71 /* WMFNotificationsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFNotificationsController.h; sourceTree = ""; }; + D8F1BF251D9C2AFB00036E71 /* WMFNotificationsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = WMFNotificationsController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + D8F36EFC1EEAEAF20087D4DD /* WMFQuoteMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFQuoteMacros.h; sourceTree = ""; }; + D8F36F021EEEBA130087D4DD /* Licenses.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Licenses.swift; sourceTree = ""; }; + D8FA39B71D7F556400D89889 /* WMFSparklineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMFSparklineView.swift; sourceTree = ""; }; + D8FB46A11E26BC6600F2620F /* QuadKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = QuadKey.swift; path = ../Wikipedia/Code/QuadKey.swift; sourceTree = ""; }; + D8FEECCB1DE3729400B883F0 /* WMFChange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFChange.h; sourceTree = ""; }; + D8FEECCC1DE3729400B883F0 /* WMFChange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFChange.m; sourceTree = ""; }; + D8FFF63F202C7A9400A028E0 /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = km; path = km.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6552031CAB800A028E0 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = el; path = el.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6562031CACC00A028E0 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ar; path = ar.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6582031CACE00A028E0 /* bik */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bik; path = bik.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6592031CAD100A028E0 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ca; path = ca.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF65A2031CAD300A028E0 /* ce */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ce; path = ce.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF65C2031CAD700A028E0 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = eo; path = eo.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF65E2031CADB00A028E0 /* haw */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = haw; path = haw.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF65F2031CADD00A028E0 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hsb; path = hsb.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6602031CADF00A028E0 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hu; path = hu.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6612031CAE000A028E0 /* hy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hy; path = hy.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6622031CAE200A028E0 /* krc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = krc; path = krc.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6632031CAE500A028E0 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lt; path = lt.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6652031CAE800A028E0 /* ml */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ml; path = ml.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6662031CAE900A028E0 /* mr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = mr; path = mr.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6672031CAEB00A028E0 /* oc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = oc; path = oc.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6682031CAED00A028E0 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ro; path = ro.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6692031CAEE00A028E0 /* sco */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sco; path = sco.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF66B2031CAF600A028E0 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sk; path = sk.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF66C2031CAF900A028E0 /* te */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = te; path = te.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF66D2031CAFA00A028E0 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF66E2031CAFD00A028E0 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = da.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF66F2031CAFF00A028E0 /* zza */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = zza; path = zza.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6702031CB0000A028E0 /* fo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fo; path = fo.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6722031CB0400A028E0 /* kn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = kn; path = kn.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6732031CB0500A028E0 /* or */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = or; path = or.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6742031CB0600A028E0 /* tg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tg; path = tg.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6752031CB0900A028E0 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = th; path = th.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6762031CB0C00A028E0 /* av */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = av; path = av.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6782031CB0F00A028E0 /* ksh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ksh; path = ksh.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6792031CB1100A028E0 /* as */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = as; path = as.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF67A2031CB1200A028E0 /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ka; path = ka.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF67B2031CB1400A028E0 /* om */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = om; path = om.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF67C2031CB1500A028E0 /* pa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = pa; path = pa.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF67D2031CB1B00A028E0 /* sah */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sah; path = sah.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF67E2031CB1D00A028E0 /* sw */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sw; path = sw.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF67F2031CB1E00A028E0 /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uz; path = uz.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6802031CB2000A028E0 /* ur */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ur; path = ur.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6812031CB2100A028E0 /* azb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = azb; path = azb.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6822031CB2300A028E0 /* ba */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ba; path = ba.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6832031CB2500A028E0 /* be-tarask */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "be-tarask"; path = "be-tarask.lproj/Localizable.stringsdict"; sourceTree = ""; }; + D8FFF6842031CB2600A028E0 /* bgn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bgn; path = bgn.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6852031CB2700A028E0 /* cnh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cnh; path = cnh.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6892031CB2F00A028E0 /* mai */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = mai; path = mai.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF68D2031CB3500A028E0 /* sa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sa; path = sa.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF68E2031CB3700A028E0 /* fil */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fil; path = fil.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF68F2031CB3800A028E0 /* vec */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vec; path = vec.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6912031CB3B00A028E0 /* xmf */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = xmf; path = xmf.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6922031CB3D00A028E0 /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = my; path = my.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6932031CB3E00A028E0 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cy; path = cy.lproj/Localizable.stringsdict; sourceTree = ""; }; + D8FFF6942031CB4000A028E0 /* tcy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tcy; path = tcy.lproj/Localizable.stringsdict; sourceTree = ""; }; + EB8237522970AE6A00FD629E /* WMFItemSourceExcludingActivityTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMFItemSourceExcludingActivityTypes.swift; sourceTree = ""; }; + FF0261872627B2D800CBD55F /* kcg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kcg; path = kcg.lproj/InfoPlist.strings; sourceTree = ""; }; + FF19C1AB260548FD000AC20B /* kcg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kcg; path = kcg.lproj/Localizable.strings; sourceTree = ""; }; + FF19C1AC26054909000AC20B /* kcg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = kcg; path = kcg.lproj/Localizable.stringsdict; sourceTree = ""; }; + FF2090EE2500247100849774 /* ThreeLineHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLineHeaderView.swift; sourceTree = ""; }; + FF2B210F254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorCollectionViewFooter.swift; sourceTree = ""; }; + FF338A0A258D572900292602 /* nqo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nqo; path = nqo.lproj/Localizable.strings; sourceTree = ""; }; + FF338A0B258D572900292602 /* nqo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nqo; path = nqo.lproj/Localizable.stringsdict; sourceTree = ""; }; + FF5555632771388F00925099 /* CollectionViewContextMenuShowing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewContextMenuShowing.swift; sourceTree = ""; }; + FF59DF4C2555E0CB0048E66C /* InternalLinkPreviewing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalLinkPreviewing.swift; sourceTree = ""; }; + FF6A77E8258D68E7006FA238 /* nqo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nqo; path = nqo.lproj/InfoPlist.strings; sourceTree = ""; }; + FF921856252E8F4F00C39A8F /* ThanksGiving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThanksGiving.swift; sourceTree = ""; }; + FF9416D724E203030070FEE7 /* OnThisDayWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnThisDayWidget.swift; sourceTree = ""; usesTabs = 0; }; + FF9416DD24E2098C0070FEE7 /* OnThisDayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnThisDayView.swift; sourceTree = ""; }; + FFA0641825A943EB00B9460B /* BasicLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicLogger.swift; sourceTree = ""; }; + FFBA8C1827D824D8009E9B65 /* URL+ExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+ExtensionTests.swift"; sourceTree = ""; }; + FFD7B84524AEAB3F005C2471 /* ArticleScrolling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleScrolling.swift; sourceTree = ""; }; + FFD7B85524B3B384005C2471 /* ReferenceBackLinksViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReferenceBackLinksViewControllerDelegate.swift; sourceTree = ""; }; + FFD7B85824B3CA7A005C2471 /* ReferenceShowing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReferenceShowing.swift; sourceTree = ""; }; + FFE891452445150B0058B642 /* AppTabBarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTabBarDelegate.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 00021DDE24D48EFD00476F97 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 00021DE524D48EFD00476F97 /* SwiftUI.framework in Frameworks */, + 00021DE324D48EFD00476F97 /* WidgetKit.framework in Frameworks */, + 00AB75BD24D4E8FB0041056A /* WMF.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0E8380601D64989F0076EDE4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D8FA190A1E1BDB79009675C3 /* WMF.framework in Frameworks */, + 0E8380651D64989F0076EDE4 /* NotificationCenter.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 676C864126D40AEA00A704C1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 676C868126D4545300A704C1 /* WMF.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B0606EAB20AA6FF0006EC6B9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B018501620BC846600A508F1 /* WMF.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BC42734F1A7C736800068882 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B09BE6A11FB3DA46007F52E3 /* WebKit.framework in Frameworks */, + D8FA19111E1BDFC8009675C3 /* WMF.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D4991432181D51DE00E6073C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D844D9731D6CB2600042D692 /* WMF.framework in Frameworks */, + 83AF34F724D3341E000046D6 /* BackgroundTasks.framework in Frameworks */, + D8D553621DF1B63200B90177 /* QuartzCore.framework in Frameworks */, + D4E6D9121A5C65F9004916C1 /* CoreData.framework in Frameworks */, + D499143B181D51DE00E6073C /* CoreGraphics.framework in Frameworks */, + 46EB334829E1D204001D5EAF /* Shared in Frameworks */, + D499143D181D51DE00E6073C /* UIKit.framework in Frameworks */, + D4991439181D51DE00E6073C /* Foundation.framework in Frameworks */, + 041EFC371996A1F800B2CB28 /* MapKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D844D9681D6CB2600042D692 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 83FFFFBA29AEC094005506A0 /* Components in Frameworks */, + 83B019D624F6C31B0014B5EF /* WidgetKit.framework in Frameworks */, + D8CD975E1E83F65700ECCA9D /* UserNotifications.framework in Frameworks */, + 67A770C8251BFE0400F94EF9 /* CocoaLumberjackSwift in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D870215D1EBA63EE000D02D6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8A42B9B1E815A9C00D8E281 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D8A42BA81E815A9C00D8E281 /* WMF.framework in Frameworks */, + 83AF34FA24D3343B000046D6 /* BackgroundTasks.framework in Frameworks */, + D8A42B9D1E815A9C00D8E281 /* QuartzCore.framework in Frameworks */, + D8A42BA71E815A9C00D8E281 /* CoreData.framework in Frameworks */, + D8A42BAA1E815A9C00D8E281 /* CoreGraphics.framework in Frameworks */, + D8A42BAD1E815A9C00D8E281 /* UIKit.framework in Frameworks */, + D8A42BAF1E815A9C00D8E281 /* Foundation.framework in Frameworks */, + D8A42BB01E815A9C00D8E281 /* MapKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8B589A221CD05070027083A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8CE261E1E698E2400DAE2E0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D8CE262C1E698E2400DAE2E0 /* WMF.framework in Frameworks */, + 83AF34F924D33432000046D6 /* BackgroundTasks.framework in Frameworks */, + D8CE26201E698E2400DAE2E0 /* QuartzCore.framework in Frameworks */, + D8CE262B1E698E2400DAE2E0 /* CoreData.framework in Frameworks */, + D8CE262E1E698E2400DAE2E0 /* CoreGraphics.framework in Frameworks */, + D8CE26321E698E2400DAE2E0 /* UIKit.framework in Frameworks */, + D8CE26351E698E2400DAE2E0 /* Foundation.framework in Frameworks */, + D8CE26361E698E2400DAE2E0 /* MapKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8EC3F1D1E9BDA35006712EB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D8EC3F2E1E9BDA35006712EB /* WMF.framework in Frameworks */, + 83AF34F824D33428000046D6 /* BackgroundTasks.framework in Frameworks */, + D8EC3F241E9BDA35006712EB /* QuartzCore.framework in Frameworks */, + D8EC3F2D1E9BDA35006712EB /* CoreData.framework in Frameworks */, + D8EC3F301E9BDA35006712EB /* CoreGraphics.framework in Frameworks */, + D8EC3F341E9BDA35006712EB /* UIKit.framework in Frameworks */, + D8EC3F361E9BDA35006712EB /* Foundation.framework in Frameworks */, + D8EC3F371E9BDA35006712EB /* MapKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 00021DE624D48EFD00476F97 /* Widgets */ = { + isa = PBXGroup; + children = ( + 00D5593424DB152300C78F08 /* WidgetsExtension.entitlements */, + 00021DFF24D48F3000476F97 /* Extension */, + 00021DFE24D48F2200476F97 /* Widgets */, + 0033D79F24F8197A00CAB5B3 /* Views */, + 006D273324D8BAE500947551 /* Utilities */, + 00021DEB24D48EFE00476F97 /* Info.plist */, + ); + path = Widgets; + sourceTree = ""; + }; + 00021DFE24D48F2200476F97 /* Widgets */ = { + isa = PBXGroup; + children = ( + 00669504265DA3D300E23AE4 /* FeaturedArticleWidget.swift */, + 00669506265DAB7800E23AE4 /* FeaturedArticleWidget+LocalizedStrings.swift */, + FF9416D724E203030070FEE7 /* OnThisDayWidget.swift */, + FF9416DD24E2098C0070FEE7 /* OnThisDayView.swift */, + 00021E0324D4A42A00476F97 /* PictureOfTheDayWidget.swift */, + 002AB86F250BEFBE00ADAC87 /* PictureOfTheDayWidget+LocalizedStrings.swift */, + 0033D79824F818EC00CAB5B3 /* TopReadWidget.swift */, + 0033D79724F818EB00CAB5B3 /* TopReadWidget+LocalizedStrings.swift */, + ); + path = Widgets; + sourceTree = ""; + }; + 00021DFF24D48F3000476F97 /* Extension */ = { + isa = PBXGroup; + children = ( + 00550D2426B1E5660055C496 /* Sample Content */, + 00021DE724D48EFD00476F97 /* Widgets.swift */, + 00021DE924D48EFE00476F97 /* Assets.xcassets */, + ); + path = Extension; + sourceTree = ""; + }; + 0033D79F24F8197A00CAB5B3 /* Views */ = { + isa = PBXGroup; + children = ( + 0033D7A024F8199300CAB5B3 /* Sparkline.swift */, + ); + path = Views; + sourceTree = ""; + }; + 0042803E25E6E395004945B3 /* FLAnimatedImage */ = { + isa = PBXGroup; + children = ( + 0042803F25E6E395004945B3 /* FLAnimatedImage */, + 0042804425E6E395004945B3 /* .gitignore */, + ); + path = FLAnimatedImage; + sourceTree = ""; + }; + 0042803F25E6E395004945B3 /* FLAnimatedImage */ = { + isa = PBXGroup; + children = ( + 0042804025E6E395004945B3 /* FLAnimatedImage.m */, + 0042804125E6E395004945B3 /* FLAnimatedImageView.m */, + 0042804225E6E395004945B3 /* FLAnimatedImage.h */, + 0042804325E6E395004945B3 /* FLAnimatedImageView.h */, + ); + path = FLAnimatedImage; + sourceTree = ""; + }; + 0042804525E6E395004945B3 /* Mantle */ = { + isa = PBXGroup; + children = ( + 0042804625E6E395004945B3 /* NSValueTransformer+MTLPredefinedTransformerAdditions.m */, + 0042804725E6E395004945B3 /* NSError+MTLModelException.m */, + 0042804825E6E395004945B3 /* NSDictionary+MTLJSONKeyPath.h */, + 0042804925E6E395004945B3 /* extobjc */, + 0042805125E6E395004945B3 /* MTLJSONAdapter.m */, + 0042805225E6E395004945B3 /* MTLModel+NSCoding.m */, + 0042805325E6E395004945B3 /* include */, + 0042806025E6E395004945B3 /* NSArray+MTLManipulationAdditions.m */, + 0042806125E6E395004945B3 /* MTLModel.m */, + 0042806225E6E395004945B3 /* NSDictionary+MTLMappingAdditions.m */, + 0042806325E6E395004945B3 /* MTLReflection.h */, + 0042806425E6E395004945B3 /* NSError+MTLModelException.h */, + 0042806525E6E395004945B3 /* MTLValueTransformer.m */, + 0042806625E6E395004945B3 /* NSObject+MTLComparisonAdditions.m */, + 0042806725E6E395004945B3 /* NSDictionary+MTLJSONKeyPath.m */, + 0042806825E6E395004945B3 /* NSValueTransformer+MTLInversionAdditions.m */, + 0042806925E6E395004945B3 /* MTLTransformerErrorHandling.m */, + 0042806A25E6E395004945B3 /* MTLReflection.m */, + 0042806B25E6E395004945B3 /* NSDictionary+MTLManipulationAdditions.m */, + ); + path = Mantle; + sourceTree = ""; + }; + 0042804925E6E395004945B3 /* extobjc */ = { + isa = PBXGroup; + children = ( + 0042804A25E6E395004945B3 /* include */, + 0042804F25E6E395004945B3 /* MTLEXTScope.m */, + 0042805025E6E395004945B3 /* MTLEXTRuntimeExtensions.m */, + ); + path = extobjc; + sourceTree = ""; + }; + 0042804A25E6E395004945B3 /* include */ = { + isa = PBXGroup; + children = ( + 0042804B25E6E395004945B3 /* MTLEXTRuntimeExtensions.h */, + 0042804C25E6E395004945B3 /* MTLEXTScope.h */, + 0042804D25E6E395004945B3 /* MTLMetamacros.h */, + 0042804E25E6E395004945B3 /* MTLEXTKeyPathCoding.h */, + ); + path = include; + sourceTree = ""; + }; + 0042805325E6E395004945B3 /* include */ = { + isa = PBXGroup; + children = ( + 0042805425E6E395004945B3 /* NSObject+MTLComparisonAdditions.h */, + 0042805525E6E395004945B3 /* MTLValueTransformer.h */, + 0042805625E6E395004945B3 /* MTLTransformerErrorHandling.h */, + 0042805725E6E395004945B3 /* NSValueTransformer+MTLInversionAdditions.h */, + 0042805825E6E395004945B3 /* NSDictionary+MTLManipulationAdditions.h */, + 0042805925E6E395004945B3 /* Mantle.h */, + 0042805A25E6E395004945B3 /* NSValueTransformer+MTLPredefinedTransformerAdditions.h */, + 0042805B25E6E395004945B3 /* MTLJSONAdapter.h */, + 0042805C25E6E395004945B3 /* MTLModel+NSCoding.h */, + 0042805D25E6E395004945B3 /* MTLModel.h */, + 0042805E25E6E395004945B3 /* NSDictionary+MTLMappingAdditions.h */, + 0042805F25E6E395004945B3 /* NSArray+MTLManipulationAdditions.h */, + ); + path = include; + sourceTree = ""; + }; + 004280F725E6E841004945B3 /* NYTPhotoViewer */ = { + isa = PBXGroup; + children = ( + 004280F825E6E841004945B3 /* NYTPhotoViewer.bundle */, + 004280F925E6E841004945B3 /* NYTPhotoDismissalInteractionController.h */, + 004280FA25E6E841004945B3 /* NYTPhotosOverlayView.m */, + 004280FB25E6E841004945B3 /* NYTPhotoTransitionAnimator.m */, + 004280FC25E6E841004945B3 /* Resource Loading */, + 004280FF25E6E841004945B3 /* NYTPhotoTransitionController.m */, + 0042810025E6E841004945B3 /* NYTPhotoViewController.m */, + 0042810125E6E841004945B3 /* NYTScalingImageView.h */, + 0042810225E6E841004945B3 /* NYTPhotoCaptionView.m */, + 0042810325E6E841004945B3 /* NYTPhotoViewer.h */, + 0042810425E6E841004945B3 /* NYTPhotosDataSource.m */, + 0042810525E6E841004945B3 /* NYTPhotosViewController.h */, + 0042810625E6E841004945B3 /* NYTPhotosOverlayView.h */, + 0042810725E6E841004945B3 /* NYTPhotoDismissalInteractionController.m */, + 0042810825E6E841004945B3 /* NYTPhotoViewController.h */, + 0042810925E6E841004945B3 /* NYTPhotoTransitionController.h */, + 0042810A25E6E841004945B3 /* NYTPhotoTransitionAnimator.h */, + 0042810B25E6E841004945B3 /* NYTPhotoCaptionView.h */, + 0042810C25E6E841004945B3 /* NYTScalingImageView.m */, + 0042810D25E6E841004945B3 /* NYTPhotoViewerCore.h */, + 0042810E25E6E841004945B3 /* Protocols */, + 0042811325E6E841004945B3 /* NYTPhotosViewController.m */, + 0042811425E6E841004945B3 /* NYTPhotosDataSource.h */, + ); + name = NYTPhotoViewer; + path = "Wikipedia/Third Party Code/NYTPhotoViewer"; + sourceTree = SOURCE_ROOT; + }; + 004280FC25E6E841004945B3 /* Resource Loading */ = { + isa = PBXGroup; + children = ( + 004280FD25E6E841004945B3 /* NSBundle+NYTPhotoViewer.m */, + 004280FE25E6E841004945B3 /* NSBundle+NYTPhotoViewer.h */, + ); + path = "Resource Loading"; + sourceTree = ""; + }; + 0042810E25E6E841004945B3 /* Protocols */ = { + isa = PBXGroup; + children = ( + 0042810F25E6E841004945B3 /* NYTPhotoCaptionViewLayoutWidthHinting.h */, + 0042811025E6E841004945B3 /* NYTPhotoContainer.h */, + 0042811125E6E841004945B3 /* NYTPhotosViewControllerDataSource.h */, + 0042811225E6E841004945B3 /* NYTPhoto.h */, + ); + path = Protocols; + sourceTree = ""; + }; + 0042816E25E6EF58004945B3 /* Third Party */ = { + isa = PBXGroup; + children = ( + 0042816F25E6EFC4004945B3 /* Nocilla */, + ); + path = "Third Party"; + sourceTree = ""; + }; + 0042816F25E6EFC4004945B3 /* Nocilla */ = { + isa = PBXGroup; + children = ( + 0042817025E6EFC4004945B3 /* Stubs */, + 0042817525E6EFC4004945B3 /* LSNocilla.m */, + 0042817625E6EFC4004945B3 /* Nocilla-Prefix.pch */, + 0042817725E6EFC4004945B3 /* Diff */, + 0042817A25E6EFC4004945B3 /* Nocilla.h */, + 0042817B25E6EFC4004945B3 /* Hooks */, + 0042819125E6EFC4004945B3 /* Model */, + 0042819525E6EFC4004945B3 /* LSNocilla.h */, + 0042819625E6EFC4004945B3 /* Matchers */, + 004281A625E6EFC4004945B3 /* Categories */, + 004281AB25E6EFC4004945B3 /* DSL */, + ); + path = Nocilla; + sourceTree = ""; + }; + 0042817025E6EFC4004945B3 /* Stubs */ = { + isa = PBXGroup; + children = ( + 0042817125E6EFC4004945B3 /* LSStubRequest.m */, + 0042817225E6EFC4004945B3 /* LSStubResponse.m */, + 0042817325E6EFC4004945B3 /* LSStubResponse.h */, + 0042817425E6EFC4004945B3 /* LSStubRequest.h */, + ); + path = Stubs; + sourceTree = ""; + }; + 0042817725E6EFC4004945B3 /* Diff */ = { + isa = PBXGroup; + children = ( + 0042817825E6EFC4004945B3 /* LSHTTPRequestDiff.m */, + 0042817925E6EFC4004945B3 /* LSHTTPRequestDiff.h */, + ); + path = Diff; + sourceTree = ""; + }; + 0042817B25E6EFC4004945B3 /* Hooks */ = { + isa = PBXGroup; + children = ( + 0042817C25E6EFC4004945B3 /* LSHTTPClientHook.m */, + 0042817D25E6EFC4004945B3 /* LSHTTPClientHook.h */, + 0042817E25E6EFC4004945B3 /* NSURLRequest */, + 0042818725E6EFC4004945B3 /* ASIHTTPRequest */, + 0042818E25E6EFC4004945B3 /* NSURLSession */, + ); + path = Hooks; + sourceTree = ""; + }; + 0042817E25E6EFC4004945B3 /* NSURLRequest */ = { + isa = PBXGroup; + children = ( + 0042817F25E6EFC4004945B3 /* NSURLRequest+LSHTTPRequest.m */, + 0042818025E6EFC4004945B3 /* NSURLRequest+DSL.h */, + 0042818125E6EFC4004945B3 /* LSNSURLHook.h */, + 0042818225E6EFC4004945B3 /* LSHTTPStubURLProtocol.h */, + 0042818325E6EFC4004945B3 /* NSURLRequest+LSHTTPRequest.h */, + 0042818425E6EFC4004945B3 /* LSNSURLHook.m */, + 0042818525E6EFC4004945B3 /* NSURLRequest+DSL.m */, + 0042818625E6EFC4004945B3 /* LSHTTPStubURLProtocol.m */, + ); + path = NSURLRequest; + sourceTree = ""; + }; + 0042818725E6EFC4004945B3 /* ASIHTTPRequest */ = { + isa = PBXGroup; + children = ( + 0042818825E6EFC4004945B3 /* LSASIHTTPRequestHook.h */, + 0042818925E6EFC4004945B3 /* ASIHTTPRequestStub.m */, + 0042818A25E6EFC4004945B3 /* LSASIHTTPRequestAdapter.h */, + 0042818B25E6EFC4004945B3 /* LSASIHTTPRequestHook.m */, + 0042818C25E6EFC4004945B3 /* LSASIHTTPRequestAdapter.m */, + 0042818D25E6EFC4004945B3 /* ASIHTTPRequestStub.h */, + ); + path = ASIHTTPRequest; + sourceTree = ""; + }; + 0042818E25E6EFC4004945B3 /* NSURLSession */ = { + isa = PBXGroup; + children = ( + 0042818F25E6EFC4004945B3 /* LSNSURLSessionHook.h */, + 0042819025E6EFC4004945B3 /* LSNSURLSessionHook.m */, + ); + path = NSURLSession; + sourceTree = ""; + }; + 0042819125E6EFC4004945B3 /* Model */ = { + isa = PBXGroup; + children = ( + 0042819225E6EFC4004945B3 /* LSHTTPBody.h */, + 0042819325E6EFC4004945B3 /* LSHTTPRequest.h */, + 0042819425E6EFC4004945B3 /* LSHTTPResponse.h */, + ); + path = Model; + sourceTree = ""; + }; + 0042819625E6EFC4004945B3 /* Matchers */ = { + isa = PBXGroup; + children = ( + 0042819725E6EFC4004945B3 /* LSMatcheable.h */, + 0042819825E6EFC4004945B3 /* LSMatcher.h */, + 0042819925E6EFC4004945B3 /* LSStringMatcher.h */, + 0042819A25E6EFC4004945B3 /* NSRegularExpression+Matcheable.m */, + 0042819B25E6EFC4004945B3 /* LSRegexMatcher.h */, + 0042819C25E6EFC4004945B3 /* NSString+Matcheable.m */, + 0042819D25E6EFC4004945B3 /* NSData+Matcheable.m */, + 0042819E25E6EFC4004945B3 /* LSDataMatcher.m */, + 0042819F25E6EFC4004945B3 /* LSMatcher.m */, + 004281A025E6EFC4004945B3 /* LSRegexMatcher.m */, + 004281A125E6EFC4004945B3 /* NSRegularExpression+Matcheable.h */, + 004281A225E6EFC4004945B3 /* LSStringMatcher.m */, + 004281A325E6EFC4004945B3 /* NSString+Matcheable.h */, + 004281A425E6EFC4004945B3 /* LSDataMatcher.h */, + 004281A525E6EFC4004945B3 /* NSData+Matcheable.h */, + ); + path = Matchers; + sourceTree = ""; + }; + 004281A625E6EFC4004945B3 /* Categories */ = { + isa = PBXGroup; + children = ( + 004281A725E6EFC4004945B3 /* NSData+Nocilla.h */, + 004281A825E6EFC4004945B3 /* NSString+Nocilla.m */, + 004281A925E6EFC4004945B3 /* NSData+Nocilla.m */, + 004281AA25E6EFC4004945B3 /* NSString+Nocilla.h */, + ); + path = Categories; + sourceTree = ""; + }; + 004281AB25E6EFC4004945B3 /* DSL */ = { + isa = PBXGroup; + children = ( + 004281AC25E6EFC4004945B3 /* LSHTTPRequestDSLRepresentation.m */, + 004281AD25E6EFC4004945B3 /* LSStubRequestDSL.h */, + 004281AE25E6EFC4004945B3 /* LSStubResponseDSL.m */, + 004281AF25E6EFC4004945B3 /* LSStubRequestDSL.m */, + 004281B025E6EFC4004945B3 /* LSHTTPRequestDSLRepresentation.h */, + 004281B125E6EFC4004945B3 /* LSStubResponseDSL.h */, + ); + path = DSL; + sourceTree = ""; + }; + 00550D2426B1E5660055C496 /* Sample Content */ = { + isa = PBXGroup; + children = ( + 00550D2526B1E7DB0055C496 /* Featured Article Widget Preview Content.json */, + ); + path = "Sample Content"; + sourceTree = ""; + }; + 006694FA265D9ECD00E23AE4 /* Widget */ = { + isa = PBXGroup; + children = ( + 00669503265DA10B00E23AE4 /* Models */, + 0062597224DE0A2500C95037 /* WidgetController.swift */, + 006694FF265DA01000E23AE4 /* WidgetContentFetcher.swift */, + ); + path = Widget; + sourceTree = ""; + }; + 00669503265DA10B00E23AE4 /* Models */ = { + isa = PBXGroup; + children = ( + 006694FD265D9F3A00E23AE4 /* WidgetCache.swift */, + 006694FB265D9F2900E23AE4 /* WidgetSettings.swift */, + 0066BE2F265EC4A900512BE8 /* WidgetFeaturedContent.swift */, + ); + path = Models; + sourceTree = ""; + }; + 006D273324D8BAE500947551 /* Utilities */ = { + isa = PBXGroup; + children = ( + 006D273424D8BAFB00947551 /* View+Extensions.swift */, + 006D273624D8D8D100947551 /* Date+Extensions.swift */, + 0033D79C24F8193900CAB5B3 /* CGPoint+Extensions.swift */, + 0033D79B24F8193900CAB5B3 /* UIColor+Extensions.swift */, + 00B16E8D293AACC200EF847F /* UIImage+Extensions.swift */, + ); + path = Utilities; + sourceTree = ""; + }; + 0072990428AC44C100DCD2E6 /* Cell UI Elements */ = { + isa = PBXGroup; + children = ( + 0072990528AC44F100DCD2E6 /* TalkPageCellTopicView.swift */, + 0072991428AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift */, + 0072991E28AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift */, + 0072991928AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift */, + ); + name = "Cell UI Elements"; + sourceTree = ""; + }; + 007CCF0526D5A10700D5EA7C /* Notifications Center */ = { + isa = PBXGroup; + children = ( + 00CF2E9E27DABC94006EFDDC /* Onboarding */, + 007CCF0026D5A10100D5EA7C /* NotificationsCenterViewController.swift */, + 007CCF0626D5A17200D5EA7C /* NotificationsCenterView.swift */, + 007CCF0B26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift */, + 007CCF1026D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift */, + 00FCB2BD26D8398700F5A47A /* NotificationsCenterCell.swift */, + 67C6F79627E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift */, + 67C6F7A527E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift */, + 67C6F79B27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift */, + 67C6F7A027E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift */, + 00FCB2C226D839A500F5A47A /* NotificationsCenterCellViewModel.swift */, + 6761AEE02704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift */, + 6761AEE52704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift */, + 678D29AD2729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift */, + 678D29B2272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift */, + 00E75B5C27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift */, + 00E75B6127EB87DC00A45B78 /* NotificationsCenterDetailView.swift */, + 67C6F7AA27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift */, + 00E75B6B27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift */, + 00E75B7027EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift */, + 00E75B6627EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift */, + 67C6F79127E8C03900B9C864 /* NotificationsCenterAction.swift */, + 67DAEDD827E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift */, + 67E50B2A27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift */, + 6741244F27E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift */, + 00E2EA8826E28A9700B1A741 /* NotificationsCenterCellStyle.swift */, + 00E2EA8D26E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift */, + 6761AEF62707E34F00E47BAD /* NotificationsCenterModelController.swift */, + 676F39272745FB1F00F4D33D /* NotificationsCenterFiltersViewModel.swift */, + 67112E3C275E603B007A9850 /* NotificationsCenterInboxViewModel.swift */, + 679471DA275F245000621071 /* NotificationsCenterInboxView.swift */, + 67E5DA5B2761B0AB00CE827D /* NotificationsCenterFilterView.swift */, + 675175DB276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift */, + 67C6F77D27E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift */, + 67C6F78227E8BC2E00B9C864 /* NotificationsCenterIconType.swift */, + 0036C8B2282C2AAA00EADB35 /* Notification+NotificationsCenter.swift */, + ); + name = "Notifications Center"; + sourceTree = ""; + }; + 00CF2E9E27DABC94006EFDDC /* Onboarding */ = { + isa = PBXGroup; + children = ( + 0030592527DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift */, + 00CF2E9F27DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift */, + ); + name = Onboarding; + sourceTree = ""; + }; + 00D1F58D28885B8200127169 /* Talk Pages */ = { + isa = PBXGroup; + children = ( + 6780CF212967680200D45927 /* Archives */, + 83C0688C292EEC0200DF1403 /* Formatting Toolbar */, + 83C0688B292EEBE300DF1403 /* Coffee Roll */, + 83510B0528F4CF0200B6235B /* View Model */, + 83510B0428F4CED800B6235B /* View */, + 83ACF8EA28E5D7E3000F3B6F /* Model */, + 00E5B39D28EB8E0600D2C51A /* Onboarding */, + 00FCCBC32900829800C9ECD2 /* Find in Page */, + 0072990428AC44C100DCD2E6 /* Cell UI Elements */, + 836BF56D2869F9C200B98321 /* TalkPageViewController.swift */, + 67C9FBFE28C77F350065A530 /* TalkPageTopicComposeViewController.swift */, + 00D46DA92889B9250015DE9B /* TalkPageCell.swift */, + 67134A1628A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift */, + 6730FD0D28998EFD000E5F40 /* TalkPageReplyComposeContentView.swift */, + 67C1757528AD4D6000C5ABA4 /* TalkPageDataController.swift */, + ); + name = "Talk Pages"; + sourceTree = ""; + }; + 00E5B39D28EB8E0600D2C51A /* Onboarding */ = { + isa = PBXGroup; + children = ( + 00E5B39E28EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift */, + 00E5B3A328EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift */, + ); + name = Onboarding; + sourceTree = ""; + }; + 00FCCBC32900829800C9ECD2 /* Find in Page */ = { + isa = PBXGroup; + children = ( + 003CD3E828EF7C77000158E4 /* TalkPageFindInPageSearchController.swift */, + 00FCCBC4290082C200C9ECD2 /* TalkPageFindInPageState.swift */, + 00FCCBC92900848300C9ECD2 /* TalkPageViewController+FindInPage.swift */, + ); + name = "Find in Page"; + sourceTree = ""; + }; + 041A3B5718E11ED90079FF1C /* Languages */ = { + isa = PBXGroup; + children = ( + 0EF2249D1CC5538200FDF78E /* View Controllers */, + BC45D5A71C33090A007C72F3 /* Views */, + ); + name = Languages; + path = Wikipedia/Code; + sourceTree = SOURCE_ROOT; + }; + 042A5B1419253D2A0095E172 /* View Controller */ = { + isa = PBXGroup; + children = ( + B0E806C01C0CEB380065EBC0 /* WMFSettingsViewController.h */, + B0E806C11C0CEB380065EBC0 /* WMFSettingsViewController.m */, + B0E804091C0CDE480065EBC0 /* WMFSettingsViewController.storyboard */, + B02B82721C696ECA00B19309 /* WMFSettingsTableViewCell.h */, + B02B82731C696ECA00B19309 /* WMFSettingsTableViewCell.m */, + BA6972561F2BA0D900E35F78 /* SettingsTableViewSection.swift */, + B02B82741C696ECA00B19309 /* WMFSettingsTableViewCell.xib */, + D8421B51203CC8420040F50B /* DebugReadingListsViewController.swift */, + D8421B52203CC8420040F50B /* DebugReadingListsViewController.xib */, + ); + name = "View Controller"; + sourceTree = ""; + }; + 04478625185145090050563B /* WebView */ = { + isa = PBXGroup; + children = ( + BC45D5751C32EE01007C72F3 /* Utilities */, + B069FA2D1CEACB8400083D59 /* WeakScriptMessageDelegate.swift */, + B011FA471D470F4700AD7C5E /* FindInPage */, + ); + name = WebView; + sourceTree = ""; + }; + 0449E63218A9844000D51524 /* Login */ = { + isa = PBXGroup; + children = ( + B0E804001C0CDE480065EBC0 /* WMFLoginViewController.storyboard */, + B0C6BE3F1E4068C60033BD6E /* WMFLoginViewController.swift */, + B0ED17301E49129E008B70AD /* Two Factor */, + B0C6BE4D1E45255D0033BD6E /* Change Password */, + ); + name = Login; + sourceTree = ""; + }; + 044BD6B218849AD000FFE4BE /* SectionEditor */ = { + isa = PBXGroup; + children = ( + D818FEBA21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift */, + 7A7AC84521B6B89B003B849B /* SectionEditorViewController.swift */, + 7A73B48121E54B4200249E09 /* SectionEditorNavigationItemController.swift */, + 7AC809C421DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift */, + 7AF56C3421DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift */, + B04AE84B21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift */, + 7A196F5921BF199500D9E4B5 /* SectionEditorWebView.swift */, + 7A32078721E40193009E1677 /* SectionEditorMenuItemsController.swift */, + 83F26B29220B62EC002D87A4 /* SectionEditorButton.swift */, + 003AD72D2979C512005BDB90 /* EditNoticesViewModel.swift */, + 009B8357298091BC00AABEA3 /* EditNoticesViewController.swift */, + 009B835C298091CD00AABEA3 /* EditNoticesView.swift */, + ); + name = SectionEditor; + sourceTree = ""; + }; + 045D871D19FAD2D00035C1F9 /* About The App */ = { + isa = PBXGroup; + children = ( + B0E806921C0CEA7B0065EBC0 /* AboutViewController.h */, + B0E806931C0CEA7B0065EBC0 /* AboutViewController.m */, + B0E806941C0CEA7B0065EBC0 /* AboutViewController.plist */, + B0EFCD761EBFBC6B008F36E5 /* Libraries Used */, + ); + name = "About The App"; + sourceTree = ""; + }; + 0463639518A844380049EE4F /* Keychain */ = { + isa = PBXGroup; + children = ( + B066F0D11E4F00B100A199F8 /* WMFKeychainCredentials.swift */, + 7AEF527220ADF07100DDF791 /* KeychainCredentialsManager.swift */, + ); + name = Keychain; + path = ../Wikipedia/Code; + sourceTree = ""; + }; + 0487041519F824D700B7D307 /* Networking */ = { + isa = PBXGroup; + children = ( + 0487045619F8262600B7D307 /* Fetchers */, + BCB669621A83DB8100C7B1FE /* Serializers */, + ); + name = Networking; + sourceTree = ""; + }; + 0487045619F8262600B7D307 /* Fetchers */ = { + isa = PBXGroup; + children = ( + BCCB813F1C110721008BC602 /* Picture of the Day */, + 83B01F8023DB1235001185F4 /* SectionFetcher.swift */, + B0E806561C0CE84B0065EBC0 /* WikiTextSectionUploader.h */, + B0E806571C0CE84B0065EBC0 /* WikiTextSectionUploader.m */, + B0E806611C0CE9030065EBC0 /* MWKLanguageLinkFetcher.h */, + B0E806621C0CE9030065EBC0 /* MWKLanguageLinkFetcher.m */, + B09B03F11CE0FB6300009083 /* PageHistoryFetcher.swift */, + 67E8B0A7226A654D00537BC9 /* OldTalkPageFetcher.swift */, + 838790B22858009000067B1D /* TalkPageFetcher.swift */, + 00B0B3CF2978745400DD7893 /* EditNoticesFetcher.swift */, + ); + name = Fetchers; + path = Wikipedia/Code; + sourceTree = SOURCE_ROOT; + }; + 04A97E8418B81D440046B166 /* AccountCreation */ = { + isa = PBXGroup; + children = ( + B0E803FC1C0CDE480065EBC0 /* WMFAccountCreationViewController.storyboard */, + B0C6BE411E413B3F0033BD6E /* WMFAccountCreationViewController.swift */, + ); + name = AccountCreation; + sourceTree = ""; + }; + 04B7B9BA18B5569600A63551 /* Captcha */ = { + isa = PBXGroup; + children = ( + B0E803FD1C0CDE480065EBC0 /* WMFCaptchaViewController.storyboard */, + B0C6BE491E42D19D0033BD6E /* WMFCaptchaViewController.swift */, + B0F92C6E1E3C580900B72802 /* WMFCaptchaResetter.swift */, + 7A3AD05620ADAFEF00C92E04 /* WMFCaptcha.swift */, + ); + name = Captcha; + sourceTree = ""; + }; + 04C43AB7183442FC006C643B /* Categories */ = { + isa = PBXGroup; + children = ( + 6798332722C3F2700073CE6F /* UITextView */, + B04C44491E5695C900C6DFB0 /* Array */, + BC45D5851C32F813007C72F3 /* Strings */, + BC45D5831C32F79A007C72F3 /* UIScrollView */, + BC45D5821C32F745007C72F3 /* UIView */, + BC45D5811C32F6BC007C72F3 /* UIViewController */, + 00D280F6247EFFFE006BEE23 /* Date+Extensions.swift */, + 0EE489021D4ADCA00088505C /* UIViewController+WMFScrollToTop.swift */, + D81E5F871E5F2C8400E1A80C /* UIApplication+SystemSettings.swift */, + 6707C037237F0A6E0017E7B6 /* UIFont+Extensions.swift */, + 67C9D58E28D3689F00629165 /* WMFLocalizedDateFormatStrings+Extensions.swift */, + ); + name = Categories; + sourceTree = ""; + }; + 04CCA0BD19830837000E982A /* References */ = { + isa = PBXGroup; + children = ( + 0E281A321DC263DE00FA1AB1 /* WMFLegacyReference.swift */, + B014909F1DB96A55007F5391 /* Tablet */, + B014909E1DB96A4C007F5391 /* Phone */, + ); + name = References; + sourceTree = ""; + }; + 04DD89AE18BFE63A00DD5DAD /* Preview */ = { + isa = PBXGroup; + children = ( + B01E3AFE21F98BFF0015B715 /* EditPreviewViewController.swift */, + 7A827657226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift */, + 7A827658226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib */, + B01E3AF821F986750015B715 /* PreviewWebViewContainer.swift */, + ); + name = Preview; + sourceTree = ""; + }; + 04F27B6D18FE0F2E00EDD838 /* PageHistory */ = { + isa = PBXGroup; + children = ( + 7AB209F822FC67B4006FECB4 /* PageHistoryViewController.swift */, + 7A39327F236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift */, + 7A393280236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib */, + 7ADF853523516CF500500ADC /* PageHistoryHintController.swift */, + 7A0FF2CB230343BA00E755D4 /* PageHistoryCollectionViewCell.swift */, + 7A0FF2D1230349AA00E755D4 /* Counts */, + ); + name = PageHistory; + sourceTree = ""; + }; + 0E03E27E1B82EF7600C1FBD7 /* Nearby */ = { + isa = PBXGroup; + children = ( + 0E03E2961B844D7700C1FBD7 /* Location Manager */, + BC45D5AF1C330B61007C72F3 /* Model */, + B0E802FC1C0CD5000065EBC0 /* WMFLocationSearchFetcher.h */, + B0E802FD1C0CD5000065EBC0 /* WMFLocationSearchFetcher.m */, + ); + name = Nearby; + sourceTree = ""; + }; + 0E03E2861B83948B00C1FBD7 /* Views */ = { + isa = PBXGroup; + children = ( + 0EE151901BF5402D0039828A /* Cells */, + ); + name = Views; + sourceTree = ""; + }; + 0E03E2961B844D7700C1FBD7 /* Location Manager */ = { + isa = PBXGroup; + children = ( + A4C558BE2403D7E200AFBFDC /* LocationManager.swift */, + A4C558BC2403D74100AFBFDC /* LocationManagerProtocol.swift */, + 67540CA824D221E3008B2894 /* LocationManagerFactory.swift */, + B0E804711C0CE0B40065EBC0 /* CLLocation+WMFBearing.h */, + B0E804721C0CE0B40065EBC0 /* CLLocation+WMFBearing.m */, + B0E804A81C0CE0B40065EBC0 /* NSString+WMFDistance.h */, + B0E804A91C0CE0B40065EBC0 /* NSString+WMFDistance.m */, + BCCFC44A1C84BAE0009D3613 /* CLLocation+WMFComparison.h */, + BCCFC44B1C84BAE0009D3613 /* CLLocation+WMFComparison.m */, + ); + name = "Location Manager"; + sourceTree = ""; + }; + 0E09EAC71C442A130058F2D8 /* Container VC */ = { + isa = PBXGroup; + children = ( + 83B4CDBE20E3DCD6007D5A6E /* SearchViewController.swift */, + ); + name = "Container VC"; + sourceTree = ""; + }; + 0E19B9AC1DA7DBE300239F3A /* Feed Models */ = { + isa = PBXGroup; + children = ( + 8387CE8D24C99C2600439D93 /* WMFMTLModel.h */, + 8387CE8E24C99C2600439D93 /* WMFMTLModel.m */, + 0E19B99D1DA7CAC200239F3A /* WMFFeedDayResponse.h */, + 0E19B99E1DA7CAC200239F3A /* WMFFeedDayResponse.m */, + 0E19B9A31DA7CE4400239F3A /* WMFFeedTopReadResponse.h */, + 0E19B9A41DA7CE4400239F3A /* WMFFeedTopReadResponse.m */, + 0E19B9A01DA7CB8200239F3A /* WMFFeedArticlePreview.h */, + 0E19B9A11DA7CB8200239F3A /* WMFFeedArticlePreview.m */, + B0B423781F0211A000D3DC4C /* WMFFeedArticlePreview+DescriptionOrSnippet.swift */, + 0E19B9A61DA7D52A00239F3A /* WMFFeedImage.h */, + 0E19B9A71DA7D52A00239F3A /* WMFFeedImage.m */, + 0E19B9A91DA7D77600239F3A /* WMFFeedNewsStory.h */, + 0E19B9AA1DA7D77600239F3A /* WMFFeedNewsStory.m */, + 67C9D59028D36BDD00629165 /* WMFFeedNewsStory+LocalizedStrings.swift */, + B0B423451EF1FEE000D3DC4C /* WMFFeedOnThisDayEvent.h */, + B0B423461EF1FEE000D3DC4C /* WMFFeedOnThisDayEvent.m */, + D837B5A91F0D0D1600DCB9CD /* WMFFeedOnThisDayEvent+LocalizedDates.swift */, + ); + name = "Feed Models"; + path = ../Wikipedia/Code; + sourceTree = ""; + }; + 0E26628E1D09ED1C006D4A46 /* AuthManager Info */ = { + isa = PBXGroup; + children = ( + B0F92C7F1E3FFEA100B72802 /* WMFAuthLoginInfoFetcher.swift */, + B0F92C811E3FFEB900B72802 /* WMFAuthAccountCreationInfoFetcher.swift */, + ); + name = "AuthManager Info"; + sourceTree = ""; + }; + 0E26628F1D09F583006D4A46 /* Authentication */ = { + isa = PBXGroup; + children = ( + B027447C1E6253E200E7B248 /* WMFScrollViewController.swift */, + B027FD271E678F5C005644A9 /* WMFAuthButton.swift */, + B010E1A71E723E3600CFE1CD /* WMFAuthLinkLabel.swift */, + B0267CE71E31698F006B6D8D /* ForgotPassword */, + 0449E63218A9844000D51524 /* Login */, + 04A97E8418B81D440046B166 /* AccountCreation */, + ); + name = Authentication; + sourceTree = ""; + }; + 0E2691041B86BBD1009B8605 /* Related */ = { + isa = PBXGroup; + children = ( + 7AD5D452223874F600C01164 /* RelatedSearchFetcher.swift */, + ); + name = Related; + sourceTree = ""; + }; + 0E26B0541C0E28E60004D687 /* Welcome */ = { + isa = PBXGroup; + children = ( + 0E26B0741C0F4F720004D687 /* WMFWelcome.storyboard */, + B0F9299B1F84789C002A0788 /* WMFWelcomeInitialViewController.swift */, + B0E294CC1DB2CF4300861D04 /* UIView+Animations.swift */, + B09B30D01DB817660012281F /* UIViewController+WMFWelcomeStoryboard.swift */, + 0E26B0881C0FD7E00004D687 /* Containers */, + B0F7CB521C8A87B300996DE0 /* Animation */, + B043C9831F871AA8005400D3 /* Pages */, + ); + name = Welcome; + sourceTree = ""; + }; + 0E26B0881C0FD7E00004D687 /* Containers */ = { + isa = PBXGroup; + children = ( + B083371D1DADB251002860D2 /* WMFWelcomePageViewController.swift */, + B00DDEDC1DB591C400615FA2 /* WMFWelcomeContainerViewController.swift */, + B083375C1DB16A09002860D2 /* WMFWelcomePanelViewController.swift */, + ); + name = Containers; + sourceTree = ""; + }; + 0E26B0891C0FD7F70004D687 /* Intro */ = { + isa = PBXGroup; + children = ( + B0E294D21DB2DC8900861D04 /* WMFWelcomeIntroductionViewController.swift */, + B0F7CB571C8A89EA00996DE0 /* WMFWelcomeIntroductionAnimationView.swift */, + ); + name = Intro; + sourceTree = ""; + }; + 0E26B08A1C0FD8040004D687 /* Language */ = { + isa = PBXGroup; + children = ( + B083375E1DB17DA5002860D2 /* WMFWelcomeLanguageTableViewController.swift */, + B0F7CB591C8A89EF00996DE0 /* WMFWelcomeLanguagesAnimationView.swift */, + B0C7A07E1F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift */, + ); + name = Language; + sourceTree = ""; + }; + 0E26B08B1C0FD80B0004D687 /* Analytics */ = { + isa = PBXGroup; + children = ( + B08337601DB17DC5002860D2 /* WMFWelcomeAnalyticsViewController.swift */, + B0F7CB551C8A89E300996DE0 /* WMFWelcomeAnalyticsAnimationView.swift */, + B0C7A0841F710EB0008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift */, + ); + name = Analytics; + sourceTree = ""; + }; + 0E281A0E1DC1391900FA1AB1 /* Housekeeping */ = { + isa = PBXGroup; + children = ( + 83ACF8E428E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift */, + B37B38EF1E5F432F00FE11BD /* WMFDatabaseHousekeeper.swift */, + ); + name = Housekeeping; + sourceTree = ""; + }; + 0E28C45F1D74A673000C5919 /* Legacy Models */ = { + isa = PBXGroup; + children = ( + B0E807851C0CEF660065EBC0 /* MWKList.h */, + B0E807861C0CEF660065EBC0 /* MWKList.m */, + B0E807841C0CEF660065EBC0 /* MWKDataStoreList.h */, + B0E807871C0CEF660065EBC0 /* MWKList+Subclass.h */, + ); + name = "Legacy Models"; + sourceTree = ""; + }; + 0E28C4601D74A68D000C5919 /* Language */ = { + isa = PBXGroup; + children = ( + B0E806B31C0CEB160065EBC0 /* MWKLanguageLinkController_Private.h */, + B0E806B41C0CEB160065EBC0 /* MWKLanguageLinkController.h */, + B0E806B51C0CEB160065EBC0 /* MWKLanguageLinkController.m */, + 0EBCA7441C162EE9004F1FD9 /* MWKLanguageFilter.h */, + 0EBCA7451C162EE9004F1FD9 /* MWKLanguageFilter.m */, + B0E807991C0CEFBD0065EBC0 /* MWKLanguageLink.h */, + B0E8079A1C0CEFBD0065EBC0 /* MWKLanguageLink.m */, + ); + name = Language; + sourceTree = ""; + }; + 0E28C4611D74AA55000C5919 /* Article Content */ = { + isa = PBXGroup; + children = ( + ); + name = "Article Content"; + sourceTree = ""; + }; + 0E28C4631D74AA81000C5919 /* Image Caching */ = { + isa = PBXGroup; + children = ( + D80ACD261EA0DD0000DC3F20 /* FLAnimatedImage+SafeForSwift.h */, + D80ACD271EA0DD0000DC3F20 /* FLAnimatedImage+SafeForSwift.m */, + D8D365141E953C7100593A38 /* ImageControllerCompletionManager.swift */, + B0E805C61C0CE5250065EBC0 /* ImageDownload.swift */, + B0E806781C0CE9C70065EBC0 /* Cancellable.swift */, + ); + name = "Image Caching"; + sourceTree = ""; + }; + 0E28C4641D74AAB0000C5919 /* User DataStore */ = { + isa = PBXGroup; + children = ( + 7A06020D20EAAF5A00FBB71D /* ExploreFeedPreferencesUpdateCoordinator.swift */, + B0E807C11C0CF04A0065EBC0 /* MWKDataStore.h */, + B0E807C21C0CF04A0065EBC0 /* MWKDataStore.m */, + 535F16D525CE11A300875AAD /* MWKDataStore+LanguageVariantMigration.swift */, + B0E807B41C0CF0180065EBC0 /* MWKSavedPageList.h */, + B0E807B51C0CF0180065EBC0 /* MWKSavedPageList.m */, + D8C41DDA23FC09EE00353DCE /* NSManagedObjectContext+History.swift */, + D8BD63BE1EA7E28700BBC082 /* SummaryExtensions.swift */, + 83A933442514C491006EB48A /* WMFCrossProcessCoreDataSynchronizer.h */, + 83A933452514C491006EB48A /* WMFCrossProcessCoreDataSynchronizer.m */, + D81930D81E9F97B200554B19 /* WMFExploreFeedContentController.h */, + D81930D91E9F97B200554B19 /* WMFExploreFeedContentController.m */, + 83DF1D1324F53878007E08D8 /* WMFPreferredLanguageInfoProvider.h */, + ); + name = "User DataStore"; + sourceTree = ""; + }; + 0E28C4651D74AB54000C5919 /* Base Model Classes */ = { + isa = PBXGroup; + children = ( + B0E807821C0CEF660065EBC0 /* MWKDataObject.h */, + B0E807831C0CEF660065EBC0 /* MWKDataObject.m */, + B0E807881C0CEF660065EBC0 /* MWKSiteDataObject.h */, + B0E807891C0CEF660065EBC0 /* MWKSiteDataObject.m */, + ); + name = "Base Model Classes"; + sourceTree = ""; + }; + 0E2B06F91B2D128D00EA2F53 /* Search */ = { + isa = PBXGroup; + children = ( + 0EAED8561BE95090006B01E6 /* View Controller */, + D80BF0A22347735E00B3B522 /* AppSearchButton.swift */, + ); + name = Search; + path = Wikipedia/Code; + sourceTree = SOURCE_ROOT; + }; + 0E4D1CFD1BBDC72F009BEB64 /* Table of Contents */ = { + isa = PBXGroup; + children = ( + 83C0656A23D23220001821BC /* TableOfContentsItem.swift */, + BC7FA4BF1BD6A687006CA1A3 /* View Controller */, + 0E9DFEB11BDEBAEA0032606E /* Animation */, + BC7FA4C01BD6A693006CA1A3 /* Views */, + ); + name = "Table of Contents"; + sourceTree = ""; + }; + 0E52FD651DA40EA200587426 /* Nearby */ = { + isa = PBXGroup; + children = ( + D818D3AA1ED87E8F0076110D /* ArticleLocationCellUpdating.swift */, + 8382F8C020D8431000AE5250 /* ArticleLocationCollectionViewCell.swift */, + 8350FC4B20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift */, + ); + name = Nearby; + sourceTree = ""; + }; + 0E52FD681DA40FA300587426 /* Random */ = { + isa = PBXGroup; + children = ( + 83C6435222394F0300FC16BF /* RandomArticleFetcher.swift */, + ); + name = Random; + path = ../Wikipedia/Code; + sourceTree = ""; + }; + 0E6A6F501D9E9A1300189C80 /* Content Sources */ = { + isa = PBXGroup; + children = ( + 0E3C5D371D664BFC00C95BA1 /* WMFContentSource.h */, + 0E3C5D381D664CBF00C95BA1 /* WMFRelatedPagesContentSource.h */, + 0E3C5D391D664CBF00C95BA1 /* WMFRelatedPagesContentSource.m */, + 0E9880601DA2C7CF0058D7F2 /* WMFNearbyContentSource.h */, + 0E9880611DA2C7CF0058D7F2 /* WMFNearbyContentSource.m */, + 0E9880631DA303070058D7F2 /* WMFContinueReadingContentSource.h */, + 0E9880641DA303070058D7F2 /* WMFContinueReadingContentSource.m */, + 0E19B9B01DA80C4900239F3A /* WMFFeedContentSource.h */, + 0E19B9B11DA80C4900239F3A /* WMFFeedContentSource.m */, + 0E19B9B41DAC574E00239F3A /* WMFRandomContentSource.h */, + 0E19B9B51DAC574E00239F3A /* WMFRandomContentSource.m */, + 0E8768341DDE002C00B8CACD /* WMFAnnouncementsContentSource.h */, + 0E8768351DDE002C00B8CACD /* WMFAnnouncementsContentSource.m */, + B0B4234E1EF32D2700D3DC4C /* WMFOnThisDayContentSource.h */, + B0B4234F1EF32D2700D3DC4C /* WMFOnThisDayContentSource.m */, + ); + name = "Content Sources"; + path = ../Wikipedia/Code; + sourceTree = ""; + }; + 0E728D131DAEE06F0074EB4B /* Search */ = { + isa = PBXGroup; + children = ( + B0E807CB1C0CF04A0065EBC0 /* MWKSearchResult.h */, + B0E807CC1C0CF04A0065EBC0 /* MWKSearchResult.m */, + ); + name = Search; + sourceTree = ""; + }; + 0E728D141DAEE0790074EB4B /* Event Logging */ = { + isa = PBXGroup; + children = ( + B0E8057D1C0CE2C60065EBC0 /* EventLoggingFunnel.h */, + B0E8057E1C0CE2C60065EBC0 /* EventLoggingFunnel.m */, + ); + name = "Event Logging"; + sourceTree = ""; + }; + 0E728D151DAEE0C60074EB4B /* Base Networking */ = { + isa = PBXGroup; + children = ( + 0E87683C1DDE00DB00B8CACD /* Announcements */, + 8359BAC621E4C9C1009B5E6C /* Fetcher.swift */, + 8338AF8B21F7B33E000C4055 /* WMFLegacyFetcher.h */, + 8338AF8C21F7B33E000C4055 /* WMFLegacyFetcher.m */, + ); + name = "Base Networking"; + sourceTree = ""; + }; + 0E728D161DAEE0F20074EB4B /* HTML Parsing */ = { + isa = PBXGroup; + children = ( + 83CCB287209CA4E600D31565 /* NSRegularExpression+HTML.h */, + 83CCB288209CA4E600D31565 /* NSRegularExpression+HTML.m */, + B0E804AC1C0CE0B40065EBC0 /* NSString+WMFHTMLParsing.h */, + B0E804AD1C0CE0B40065EBC0 /* NSString+WMFHTMLParsing.m */, + 7A5AB82522940CE200B91C9C /* WMFHTMLElement.m */, + 7A5AB82B22940D8500B91C9C /* WMFHTMLElement.h */, + 833D4FFA20A9E20800B44E7C /* String+HTML.swift */, + B0E807321C0CED810065EBC0 /* WMFImageURLParsing.h */, + B0E807331C0CED810065EBC0 /* WMFImageURLParsing.m */, + ); + name = "HTML Parsing"; + sourceTree = ""; + }; + 0E728D171DAEE1200074EB4B /* Recent Searches */ = { + isa = PBXGroup; + children = ( + B0E807A51C0CEFE30065EBC0 /* MWKRecentSearchEntry.h */, + B0E807A61C0CEFE30065EBC0 /* MWKRecentSearchEntry.m */, + B0E807A71C0CEFE30065EBC0 /* MWKRecentSearchList.h */, + B0E807A81C0CEFE30065EBC0 /* MWKRecentSearchList.m */, + ); + name = "Recent Searches"; + sourceTree = ""; + }; + 0E728D181DAEE2210074EB4B /* Sharing */ = { + isa = PBXGroup; + children = ( + ); + name = Sharing; + sourceTree = ""; + }; + 0E728D191DAEE2390074EB4B /* Images */ = { + isa = PBXGroup; + children = ( + BCA15B151C0F48EF00D0A3EA /* UIScreen+WMFImageWidth.h */, + BCA15B161C0F48EF00D0A3EA /* UIScreen+WMFImageWidth.m */, + B0E807C31C0CF04A0065EBC0 /* MWKImageInfo.h */, + B0E807C41C0CF04A0065EBC0 /* MWKImageInfo.m */, + B0E8079B1C0CEFBD0065EBC0 /* MWKLicense.h */, + B0E8079C1C0CEFBD0065EBC0 /* MWKLicense.m */, + ); + name = Images; + sourceTree = ""; + }; + 0E728D261DAEE2FC0074EB4B /* Feed ContentFetcher */ = { + isa = PBXGroup; + children = ( + 0E19B9AD1DA7DC9D00239F3A /* WMFFeedContentFetcher.h */, + 0E19B9AE1DA7DC9D00239F3A /* WMFFeedContentFetcher.m */, + ); + name = "Feed ContentFetcher"; + sourceTree = ""; + }; + 0E728D271DAEE3200074EB4B /* Feed */ = { + isa = PBXGroup; + children = ( + 0E728D261DAEE2FC0074EB4B /* Feed ContentFetcher */, + 0E19B9AC1DA7DBE300239F3A /* Feed Models */, + ); + name = Feed; + sourceTree = ""; + }; + 0E74DC821BEBBF4200A8A005 /* Article Footer */ = { + isa = PBXGroup; + children = ( + BC45D5B71C330D27007C72F3 /* About the article */, + ); + name = "Article Footer"; + sourceTree = ""; + }; + 0E7AAEEA1C21F4160046B5B6 /* WIkimedia Event Logging */ = { + isa = PBXGroup; + children = ( + 0E8DC0931C74F0D700622CBD /* WMFDailyStatsLoggingFunnel.h */, + 0E8DC0941C74F0D700622CBD /* WMFDailyStatsLoggingFunnel.m */, + B0E805771C0CE2C60065EBC0 /* CreateAccountFunnel.h */, + B0E805781C0CE2C60065EBC0 /* CreateAccountFunnel.m */, + 7A420DB322A029780005689B /* EditFunnel.swift */, + B0E8057F1C0CE2C60065EBC0 /* WMFLoginFunnel.h */, + B0E805801C0CE2C60065EBC0 /* WMFLoginFunnel.m */, + B0E805811C0CE2C60065EBC0 /* ProtectedEditAttemptFunnel.h */, + B0E805821C0CE2C60065EBC0 /* ProtectedEditAttemptFunnel.m */, + B0E805871C0CE2C60065EBC0 /* ToCInteractionFunnel.h */, + B0E805881C0CE2C60065EBC0 /* ToCInteractionFunnel.m */, + B0E805891C0CE2C60065EBC0 /* WMFHamburgerMenuFunnel.h */, + B0E8058A1C0CE2C60065EBC0 /* WMFHamburgerMenuFunnel.m */, + B0E805981C0CE2E40065EBC0 /* WMFSearchFunnel.h */, + B0E805991C0CE2E40065EBC0 /* WMFSearchFunnel.m */, + B0E8059B1C0CE2F50065EBC0 /* WMFShareFunnel.h */, + B0E8059C1C0CE2F50065EBC0 /* WMFShareFunnel.m */, + 7AF1B19520A1E14F000C8DFE /* ReadingListsFunnel.swift */, + 67E466F9241BED770014149B /* EditHistoryCompareFunnel.swift */, + 678E7E8026432F060005439C /* NavigationEventsFunnel.swift */, + 7A25B1CB20A483F1008C6F29 /* LoginFunnel.swift */, + 7A00E0BF20A9E54400F033C8 /* SettingsFunnel.swift */, + 7A07A46D20AA482C00F7B2BB /* SessionsFunnel.swift */, + 7A4ABA8520AA8966007AA405 /* UserHistoryFunnel.swift */, + B0501BBC2110ED8800020BFA /* FeedFunnel.swift */, + 83B1218327FC8750006B8CCC /* RemoteNotificationsFunnel.swift */, + 676E813229380D8A00F15258 /* TalkPagesFunnel.swift */, + ); + name = "WIkimedia Event Logging"; + sourceTree = ""; + }; + 0E8380661D64989F0076EDE4 /* ContinueReadingWidget */ = { + isa = PBXGroup; + children = ( + D85219371D6DEFBB00084796 /* WMFTodayContinueReadingWidgetViewController.swift */, + 0E8380781D649DE10076EDE4 /* ContinueReadingWidget.entitlements */, + 0E83806A1D64989F0076EDE4 /* MainInterface.storyboard */, + 0E83806D1D64989F0076EDE4 /* Info.plist */, + D890C85B1D772ED3007132C9 /* InfoPlist.strings */, + ); + path = ContinueReadingWidget; + sourceTree = ""; + }; + 0E87683C1DDE00DB00B8CACD /* Announcements */ = { + isa = PBXGroup; + children = ( + 0E8768381DDE00D600B8CACD /* WMFAnnouncementsFetcher.h */, + 0E8768391DDE00D600B8CACD /* WMFAnnouncementsFetcher.m */, + 0E87683D1DDE012300B8CACD /* WMFAnnouncement.h */, + 0E87683E1DDE012300B8CACD /* WMFAnnouncement.m */, + 67146031243B885E008CE885 /* SurveyAnnouncementsController.swift */, + 67146033243B8B4F008CE885 /* AnnouncementType.swift */, + ); + name = Announcements; + sourceTree = ""; + }; + 0E8DC0961C7632B500622CBD /* Settings Item */ = { + isa = PBXGroup; + children = ( + B02B827A1C698FAB00B19309 /* WMFSettingsMenuItem.h */, + B0B0EC211C6999A9006F0D9C /* WMFSettingsMenuItem.m */, + ); + name = "Settings Item"; + sourceTree = ""; + }; + 0E98805F1DA2A0C00058D7F2 /* Feed */ = { + isa = PBXGroup; + children = ( + 83EE476920D019A100A21F34 /* ExploreViewController.swift */, + 7A49A20021231510005C574C /* CollectionViewFooter.swift */, + 8351CE7720D4424100E32FC1 /* CollectionViewHeader.swift */, + 83CA612920D1675800EF0C4A /* ExploreCardViewController.swift */, + D8B3D7651EC34F5B00930C21 /* SaveButtonsController.swift */, + 0E03E2861B83948B00C1FBD7 /* Views */, + B0B4235F1EF9D65F00D3DC4C /* On This Day */, + ); + name = Feed; + sourceTree = ""; + }; + 0E9B9E341CBF3262001E4C3C /* Overlay View */ = { + isa = PBXGroup; + children = ( + 0E9B9E311CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.h */, + 0E9B9E301CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.m */, + B0408C542127F2C100AC76CE /* WMFImageGalleryGradientViews.swift */, + B0016CC221362DB000FA1096 /* SetupGradientView.swift */, + B0ACB13221265B930078C136 /* WMFImageGalleryDescriptionTextView.swift */, + 0E9B9E2F1CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.xib */, + ); + name = "Overlay View"; + sourceTree = ""; + }; + 0E9DFEAB1BDEB82E0032606E /* Networking */ = { + isa = PBXGroup; + children = ( + BC23E4DE1C223EAB00B5AFDE /* Revisions */, + ); + name = Networking; + sourceTree = ""; + }; + 0E9DFEB11BDEBAEA0032606E /* Animation */ = { + isa = PBXGroup; + children = ( + B0E8036E1C0CD99A0065EBC0 /* TableOfContentsPresentationController.swift */, + B0E803701C0CD9A80065EBC0 /* TableOfContentsAnimator.swift */, + ); + name = Animation; + sourceTree = ""; + }; + 0EAED8551BE9507C006B01E6 /* Networking */ = { + isa = PBXGroup; + children = ( + B0E803411C0CD7980065EBC0 /* WMFSearchFetcher_Testing.h */, + B0E803421C0CD7980065EBC0 /* WMFSearchFetcher.h */, + B0E803431C0CD7980065EBC0 /* WMFSearchFetcher.m */, + ); + name = Networking; + sourceTree = ""; + }; + 0EAED8561BE95090006B01E6 /* View Controller */ = { + isa = PBXGroup; + children = ( + BC45D5A21C330480007C72F3 /* Model */, + 0EAED8551BE9507C006B01E6 /* Networking */, + 0E09EAC71C442A130058F2D8 /* Container VC */, + B04DA3C71D9F044100F45DB7 /* Search Languages Bar */, + ); + name = "View Controller"; + path = Wikipedia/Code; + sourceTree = SOURCE_ROOT; + }; + 0EBB23191D1C450900CA9742 /* AuthenticationManager */ = { + isa = PBXGroup; + children = ( + B0ED173E1E4CF3AF008B70AD /* WMFAuthenticationManager.swift */, + B0ED173A1E497AE7008B70AD /* WMFCurrentlyLoggedInUserFetcher.swift */, + 0E26628E1D09ED1C006D4A46 /* AuthManager Info */, + 0EBB231A1D1C453C00CA9742 /* Login */, + 0EBB231B1D1C454500CA9742 /* Account Creation */, + 04B7B9BA18B5569600A63551 /* Captcha */, + ); + name = AuthenticationManager; + path = ../Wikipedia/Code; + sourceTree = ""; + }; + 0EBB231A1D1C453C00CA9742 /* Login */ = { + isa = PBXGroup; + children = ( + B0F92C5F1E3A813500B72802 /* WMFAccountLogin.swift */, + ); + name = Login; + sourceTree = ""; + }; + 0EBB231B1D1C454500CA9742 /* Account Creation */ = { + isa = PBXGroup; + children = ( + B0C6BE471E428C940033BD6E /* WMFAccountCreator.swift */, + ); + name = "Account Creation"; + sourceTree = ""; + }; + 0EC0447C1C7974590033D773 /* Share Sources */ = { + isa = PBXGroup; + children = ( + 0EC0447A1C796FEF0033D773 /* WMFImageTextActivitySource.swift */, + 0EC0447E1C797DC20033D773 /* WMFImageURLActivitySource.swift */, + 0EC044771C7917860033D773 /* WMFArticleTextActivitySource.h */, + 0EC044781C7917860033D773 /* WMFArticleTextActivitySource.m */, + EB8237522970AE6A00FD629E /* WMFItemSourceExcludingActivityTypes.swift */, + ); + name = "Share Sources"; + sourceTree = ""; + }; + 0EE151901BF5402D0039828A /* Cells */ = { + isa = PBXGroup; + children = ( + BCCB81461C110857008BC602 /* POTD */, + 0E52FD651DA40EA200587426 /* Nearby */, + ); + name = Cells; + sourceTree = ""; + }; + 0EE151921BF540420039828A /* Compass View */ = { + isa = PBXGroup; + children = ( + B0E8031A1C0CD6820065EBC0 /* WMFCompassView.h */, + B0E8031B1C0CD6820065EBC0 /* WMFCompassView.m */, + ); + name = "Compass View"; + sourceTree = ""; + }; + 0EF2249B1CC5536B00FDF78E /* Cell */ = { + isa = PBXGroup; + children = ( + B0E806AE1C0CEB160065EBC0 /* WMFLanguageCell.h */, + 0EF224991CC5536200FDF78E /* WMFLanguageCell.m */, + 0E4D071C1CC5526200AE968B /* WMFLanguageCell.xib */, + ); + name = Cell; + sourceTree = ""; + }; + 0EF2249C1CC5537100FDF78E /* Header */ = { + isa = PBXGroup; + children = ( + B0B4CF081CC0500E0051DF7A /* WMFArticleLanguagesSectionHeader.h */, + B0B4CF091CC0500E0051DF7A /* WMFArticleLanguagesSectionHeader.m */, + B0B4CF0B1CC0501B0051DF7A /* WMFArticleLanguagesSectionHeader.xib */, + ); + name = Header; + sourceTree = ""; + }; + 0EF2249D1CC5538200FDF78E /* View Controllers */ = { + isa = PBXGroup; + children = ( + D82E95821F16502E007BD960 /* WMFLanguagesViewController.h */, + D82E95831F16502E007BD960 /* WMFLanguagesViewController.m */, + D82E95841F16502E007BD960 /* WMFLanguagesViewController.xib */, + 67F35A6523E875B900C3D6C7 /* WMFLanguagesViewControllerDelegate.h */, + ); + name = "View Controllers"; + sourceTree = ""; + }; + 0EFB48071B3BAFE900381F99 /* OldArticle */ = { + isa = PBXGroup; + children = ( + BC45D5AB1C330A8E007C72F3 /* Components */, + 0E9DFEAB1BDEB82E0032606E /* Networking */, + ); + name = OldArticle; + path = Wikipedia/Code; + sourceTree = SOURCE_ROOT; + }; + 0EFB48091B3BB01300381F99 /* Style */ = { + isa = PBXGroup; + children = ( + BC45D5501C31EB4E007C72F3 /* Utilities */, + ); + name = Style; + sourceTree = ""; + }; + 229C20D91CB08FA500BC17AD /* PageHistory */ = { + isa = PBXGroup; + children = ( + B09B03E91CE0FB2600009083 /* WMFPageHistoryRevision.h */, + B09B03EA1CE0FB2600009083 /* WMFPageHistoryRevision.m */, + B09B03EC1CE0FB4200009083 /* PageHistorySection.swift */, + ); + name = PageHistory; + sourceTree = ""; + }; + 6714D6C9245A2B6A00CE5A4A /* Manual Tests */ = { + isa = PBXGroup; + children = ( + 6714D6CA245A2B9700CE5A4A /* ArticleCacheReadingManualTests.swift */, + 67E3992924786E2100441831 /* ReadingListManualPerformanceTests.swift */, + 679FA103242E651C0095F3C6 /* ArticleManualPerformanceTests.swift */, + 67E3992B24786E6D00441831 /* TalkPageManualPerformanceTests.swift */, + ); + path = "Manual Tests"; + sourceTree = ""; + }; + 671AC2542226FB26005B37F8 /* Reading Themes Controls */ = { + isa = PBXGroup; + children = ( + 671AC2552226FB9B005B37F8 /* ReadingThemesControlsProtocols.swift */, + B09B03F31CE0FB7700009083 /* ReadingThemesControlsViewController.swift */, + B09B03F41CE0FB7700009083 /* ReadingThemesControlsViewController.xib */, + ); + name = "Reading Themes Controls"; + sourceTree = ""; + }; + 671DF9B425F2AE380011799E /* Controllers */ = { + isa = PBXGroup; + children = ( + 671DF9BE25F2AE4E0011799E /* ArticleDescriptionControlling.swift */, + 671DF9BF25F2AE4E0011799E /* ShortDescriptionController.swift */, + 671DF9C025F2AE4E0011799E /* WikidataDescriptionController.swift */, + ); + path = Controllers; + sourceTree = ""; + }; + 671DF9D625F2B57B0011799E /* Article Description Tests */ = { + isa = PBXGroup; + children = ( + 671DF9D725F2B59A0011799E /* ShortDescriptionControllerTests.swift */, + ); + path = "Article Description Tests"; + sourceTree = ""; + }; + 676C864526D40AEB00A704C1 /* NotificationServiceExtension */ = { + isa = PBXGroup; + children = ( + 676C868626D4545D00A704C1 /* NotificationServiceExtension.entitlements */, + 676C864626D40AEB00A704C1 /* NotificationService.swift */, + 676C864826D40AEB00A704C1 /* Info.plist */, + ); + path = NotificationServiceExtension; + sourceTree = ""; + }; + 6780CF212967680200D45927 /* Archives */ = { + isa = PBXGroup; + children = ( + 6780CF222967683800D45927 /* TalkPageArchivesViewController.swift */, + 6780CF272967690200D45927 /* TalkPageArchivesView.swift */, + 67FBE334297056EB00A2E4AD /* TalkPageArchivesFetcher.swift */, + 67FBE33929705FC200A2E4AD /* TalkPageArchivesItem.swift */, + ); + name = Archives; + sourceTree = ""; + }; + 6780CF3129676DB500D45927 /* Shifting Top Views */ = { + isa = PBXGroup; + children = ( + 6780CF2C29676AB000D45927 /* ShiftingTopViewsContaining.swift */, + 6780CF3229676DE300D45927 /* ShiftingTopViewsStack.swift */, + 679A23F82968D865008D7686 /* ShiftingTopViewsData.swift */, + 679A23FD2968DAB9008D7686 /* ShiftingTopView.swift */, + 679A24022968DBFC008D7686 /* ShiftingNavigationBarView.swift */, + 679A24072968E0D0008D7686 /* ShiftingScrollView.swift */, + 67D9D1FA29711CA700BFCD4F /* Loadable.swift */, + ); + name = "Shifting Top Views"; + sourceTree = ""; + }; + 6782DB8F2343B691003FA21B /* Diff */ = { + isa = PBXGroup; + children = ( + 6780D5B3237A1F480087A5D1 /* DiffResponse.json */, + 67CEF26E2351113000D5CA6C /* DiffController.swift */, + 6782DBDF2344F154003FA21B /* List */, + 6782DBDE2344F147003FA21B /* Header */, + 6782DB902343B6F9003FA21B /* DiffContainerViewController.swift */, + 6782DC102346920B003FA21B /* DiffContainerViewModel.swift */, + 67CEF262235110F700D5CA6C /* DiffNetworkModels.swift */, + 672428962362113400490629 /* DiffFetcher.swift */, + 67A5E656236775C3007749FB /* GlobalUserInfoFetcher.swift */, + B09705B3236B29D7006FDB5C /* DiffThanker.swift */, + 674E8AB82382DEFF0053D206 /* DiffTransformer.swift */, + 6780D5B9237AF8A10087A5D1 /* DiffToolbarView.swift */, + 6780D5BF237AF8AE0087A5D1 /* DiffToolbarView.xib */, + 6707C031237DBCEA0017E7B6 /* DiffRevisionTransition.swift */, + ); + name = Diff; + sourceTree = ""; + }; + 6782DBDE2344F147003FA21B /* Header */ = { + isa = PBXGroup; + children = ( + 6782DBD82344EC86003FA21B /* DiffHeaderViewModels.swift */, + 6782DBE02345053C003FA21B /* DiffHeaderExtendedView.xib */, + 6782DBE12345054C003FA21B /* DiffHeaderExtendedView.swift */, + 6782DBE22345370F003FA21B /* DiffHeaderTitleView.xib */, + 6782DB9C2343B7DB003FA21B /* DiffHeaderTitleView.swift */, + 6782DBA22343B7EE003FA21B /* DiffHeaderSummaryView.swift */, + 6782DBE32345377B003FA21B /* DiffHeaderSummaryView.xib */, + 6782DBA82343B7FC003FA21B /* DiffHeaderEditorView.swift */, + 6782DBE923453787003FA21B /* DiffHeaderEditorView.xib */, + 6782DBAE2343B812003FA21B /* DiffHeaderCompareView.swift */, + 6782DBEF23453799003FA21B /* DiffHeaderCompareView.xib */, + 6782DC0423453D6B003FA21B /* DiffHeaderCompareItemView.xib */, + 6782DC0A23453D7D003FA21B /* DiffHeaderCompareItemView.swift */, + ); + name = Header; + sourceTree = ""; + }; + 6782DBDF2344F154003FA21B /* List */ = { + isa = PBXGroup; + children = ( + 678D79E2235E58CF006161FF /* DiffListViewModels */, + 6782DBC02343FDCA003FA21B /* DiffListChangeCell.swift */, + 6782DC162347EE59003FA21B /* DiffListChangeCell.xib */, + 6782DBC62343FDE4003FA21B /* DiffListContextCell.swift */, + 67CEF25E234FCA8100D5CA6C /* DiffListContextCell.xib */, + 6782DBCC2343FDF2003FA21B /* DiffListUneditedCell.swift */, + 67CEF2602350C29D00D5CA6C /* DiffListUneditedCell.xib */, + 6782DBBA2343B861003FA21B /* DiffListViewController.swift */, + ); + name = List; + sourceTree = ""; + }; + 678C7C2823BE6766001AC4D5 /* Cache */ = { + isa = PBXGroup; + children = ( + 83CDC7D325122A1700A2F8A1 /* PermanentCacheController.swift */, + 67A6F13423BFB4E700736539 /* ImageCache */, + 67F9AE5423AD7FF1003D4F5E /* MobileHTML Article Cache */, + 678C7C3923BE944F001AC4D5 /* BaseCache */, + ); + name = Cache; + sourceTree = ""; + }; + 678C7C3923BE944F001AC4D5 /* BaseCache */ = { + isa = PBXGroup; + children = ( + 6773B1FD240F02E40022A70E /* PermanentlyPersistableURLCache.swift */, + 678C7C2923BE67F0001AC4D5 /* CacheController.swift */, + 67DAEDA023CD1BC9003AA208 /* CacheGatekeeper.swift */, + 678C7C2D23BE705C001AC4D5 /* CacheDBWriting.swift */, + 678C7C2F23BE7319001AC4D5 /* CacheDBWriterHelper.swift */, + 678C7C3323BE75F9001AC4D5 /* CacheFileWriterHelper.swift */, + 678C7C3523BE7779001AC4D5 /* FileManager+CacheExtensions.swift */, + 67F1375D23C986CD00512B61 /* CacheTaskTracking.swift */, + 6779D45023F60903002840CA /* CacheFileWriter.swift */, + 6779D45223F6EC2D002840CA /* CacheFetching.swift */, + D8CD97631E83FAB400ECCA9D /* Cache.xcdatamodeld */, + 67D6C01E2405B3D2005709B1 /* CacheGroup+CoreDataClass.swift */, + 67D6C01F2405B3D2005709B1 /* CacheGroup+CoreDataProperties.swift */, + 67D6C01A2405A4FB005709B1 /* CacheItem+CoreDataClass.swift */, + 67D6C01B2405A4FB005709B1 /* CacheItem+CoreDataProperties.swift */, + 67D6C009240581ED005709B1 /* CacheItemMigrationPolicy.swift */, + 67D6C00B24058714005709B1 /* CacheItemMappingModel.xcmappingmodel */, + ); + name = BaseCache; + sourceTree = ""; + }; + 678D79E2235E58CF006161FF /* DiffListViewModels */ = { + isa = PBXGroup; + children = ( + 6782DBD22343FE03003FA21B /* DiffListGroupViewModel.swift */, + 678D79E3235E592F006161FF /* DiffListChangeItemViewModel.swift */, + 678D79EF235E5979006161FF /* DiffListChangeViewModel.swift */, + 678D79F5235E599B006161FF /* DiffListContextViewModel.swift */, + 678D79FB235E59B2006161FF /* DiffListUneditedViewModel.swift */, + ); + name = DiffListViewModels; + sourceTree = ""; + }; + 6798035A24F94CAE00D765AA /* Article as a Living Document */ = { + isa = PBXGroup; + children = ( + 6771298E24FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift */, + 6771299324FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift */, + 6771299F24FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift */, + 67B64D562507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift */, + 67B64D5B2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift */, + 674711822507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift */, + 6747117D250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift */, + 67E2E48E250452E60070F12D /* ArticleAsLivingDocHeaderView.swift */, + 6771C9532509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib */, + 6798036024F94CEE00D765AA /* ArticleAsLivingDocController.swift */, + 67985A852524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift */, + FF2B210F254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift */, + ); + path = "Article as a Living Document"; + sourceTree = ""; + }; + 6798036F24F99A5300D765AA /* Significant Events Endpoint */ = { + isa = PBXGroup; + children = ( + 6798035B24F94CE300D765AA /* SignificantEventsFetcher.swift */, + 6798036A24F94D6700D765AA /* SignificantEventsModels.swift */, + 6798036524F94D0300D765AA /* ArticleAsLivingDocViewModels.swift */, + ); + path = "Significant Events Endpoint"; + sourceTree = ""; + }; + 6798332722C3F2700073CE6F /* UITextView */ = { + isa = PBXGroup; + children = ( + 6798332822C3F28A0073CE6F /* UITextView+Extensions.swift */, + ); + name = UITextView; + sourceTree = ""; + }; + 679FA102242E64FC0095F3C6 /* Article Tests */ = { + isa = PBXGroup; + children = ( + 679F0AA82456FADE00EF4A6A /* ArticleCacheReadingTests.swift */, + 679F0AAC24574AD400EF4A6A /* ArticleViewControllerTests.swift */, + ); + name = "Article Tests"; + sourceTree = ""; + }; + 67A6F13423BFB4E700736539 /* ImageCache */ = { + isa = PBXGroup; + children = ( + 67A6F13723BFB75300736539 /* ImageCacheDBWriter.swift */, + 67A6F13F23BFF62200736539 /* ImageCacheController.swift */, + 67A6F13923BFEA0400736539 /* ImageFetcher.swift */, + ); + name = ImageCache; + sourceTree = ""; + }; + 67D6C0152405A3E2005709B1 /* Recovered References */ = { + isa = PBXGroup; + children = ( + ); + name = "Recovered References"; + sourceTree = ""; + }; + 67D9D1F42970D8B000BFCD4F /* Button Styles */ = { + isa = PBXGroup; + children = ( + 67D9D1F52970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift */, + ); + name = "Button Styles"; + sourceTree = ""; + }; + 67DB110822613EF700F789B0 /* SchemeHandler */ = { + isa = PBXGroup; + children = ( + 67059DB42260D034009811AA /* SchemeHandler.swift */, + ); + path = SchemeHandler; + sourceTree = ""; + }; + 67DC5BDB23A00DF500B03A84 /* Article */ = { + isa = PBXGroup; + children = ( + 830AD2B824D1D615003EEFE6 /* WebPageUserScript.swift */, + 67DC5BEE23A1427C00B03A84 /* ActionHandlerScript.swift */, + 00A7946A245CA4E60063BA18 /* ArticleSurveyTimerController.swift */, + 67DC5BE223A017CA00B03A84 /* ArticleViewController.swift */, + D8A47C8E23D7338C002AA823 /* ArticleViewController+TableOfContents.swift */, + 83B01F7123DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift */, + 83B01F7623DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift */, + 83B01F7B23DB0BA2001185F4 /* ArticleViewController+Editing.swift */, + 83B01F8F23DB41BE001185F4 /* ArticleViewController+Sharing.swift */, + 83B01F9423DB41D7001185F4 /* ArticleViewController+FindInPage.swift */, + 83B01F9923DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift */, + 832A7A5A23EA138C00D0A750 /* ArticleViewController+References.swift */, + 8330532823EF0B4200123141 /* ArticleViewController+Media.swift */, + D8E6FF6624054FA100686272 /* ArticleViewController+LinkPreviewing.swift */, + D8E6FF6B24056AC300686272 /* ArticleViewController+ContextMenu.swift */, + D8E6FF7B2405AAC400686272 /* ArticleViewController+Analytics.swift */, + 67146035243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift */, + 8361474A24223689003E49D3 /* ArticleViewController+Announcements.swift */, + D8A47C8423D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift */, + 830C0DD923D9C218006471C4 /* Properties.js */, + 67DC5BE823A03FE700B03A84 /* ArticleToolbarController.swift */, + D8A47C8923D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift */, + 83F1096323D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift */, + 83F1095E23D09F80003F3E9E /* ArticleViewController+WIconPopover.swift */, + 67ADEE9523A2CFFB0000CAF7 /* ArticleWebMessagingController.swift */, + 83DAA9AF23FEB611002D5716 /* ReferenceBackLinksViewController.swift */, + FFD7B85524B3B384005C2471 /* ReferenceBackLinksViewControllerDelegate.swift */, + D8E6FF7524058AC600686272 /* WMFWebView.h */, + D8E6FF7624058AC600686272 /* WMFWebView.m */, + B0DE92301D6E3A2000EC76A7 /* UIBarButtonItem Popover Message */, + ); + name = Article; + sourceTree = ""; + }; + 67E8B0772268F8E300537BC9 /* View Controllers */ = { + isa = PBXGroup; + children = ( + 67E8B0752268DE4B00537BC9 /* TalkPageContainerViewController.swift */, + 67F73E702267B8020079DEEF /* TalkPageTopicListViewController.swift */, + 67F73E742267B9070079DEEF /* TalkPageReplyListViewController.swift */, + 676070A32280987C00A81F09 /* TalkPageTopicNewViewController.xib */, + 67F73E782267B9500079DEEF /* TalkPageTopicNewViewController.swift */, + 7AF0265422985CB9000E0A06 /* BeKindInputAccessoryView.swift */, + 7AF0265522985CB9000E0A06 /* BeKindInputAccessoryView.xib */, + 6706A21822927D63004774E2 /* TalkPageHintViewController.swift */, + 670F765E22B0C10600D87545 /* FakeProgressLoading.swift */, + ); + name = "View Controllers"; + sourceTree = ""; + }; + 67E8B0782268F8F100537BC9 /* Views */ = { + isa = PBXGroup; + children = ( + 67E8B0732268DA8B00537BC9 /* OldTalkPageTopicCell.swift */, + 6734116F22773122005B31DA /* OldTalkPageReplyCell.swift */, + 67D3C452228CB54E001D5741 /* OldTalkPageReplyComposeView.swift */, + 675A7CFD227A3F7C0034D9D9 /* OldTalkPageHeaderView.swift */, + 676070A1227CE60800A81F09 /* TalkPageReplyFooterView.swift */, + 6706A21622925FD2004774E2 /* InfoBannerView.swift */, + 672B127722A450F000CC85A5 /* OldTalkPageHeaderView.xib */, + ); + name = Views; + sourceTree = ""; + }; + 67E8B0AA226A6DB000537BC9 /* TalkPageTests */ = { + isa = PBXGroup; + children = ( + 67E8B0AB226A6DCA00537BC9 /* TalkPageNetworkDataTests.swift */, + 67E8B0AD226A74C200537BC9 /* OldTalkPageFetcherTests.swift */, + 6734114F22700A95005B31DA /* TalkPageControllerTests.swift */, + 6734115122700C47005B31DA /* TalkPageTestHelpers.swift */, + 6734116922739FD6005B31DA /* TalkPageLocalHandlerTests.swift */, + ); + name = TalkPageTests; + sourceTree = ""; + }; + 67ED8EAF24F99F1900DD5D39 /* Significant Events Tests */ = { + isa = PBXGroup; + children = ( + 67ED8EB024F99FF400DD5D39 /* SignificantEventsFetcherTests.swift */, + 673612F124FD7210002A1989 /* ArticleAsLivingDocViewModelTests.swift */, + ); + path = "Significant Events Tests"; + sourceTree = ""; + }; + 67F73384273C1FA200D7D713 /* Notifications Tests */ = { + isa = PBXGroup; + children = ( + 67F73385273C1FBA00D7D713 /* NotificationServiceHelperTests.swift */, + 67C6F74D27E2919A00B9C864 /* RemoteNotificationsModelController+TestExtensions.swift */, + 67C6F74F27E293C700B9C864 /* NotificationsCenterViewModelTests.swift */, + 67C6F76727E2E76E00B9C864 /* NotificationsCenterCellViewModelUserTalkMessageTests.swift */, + 67C6F76C27E2E77F00B9C864 /* NotificationsCenterCellViewModelMentionTests.swift */, + 67C6F77027E2E78400B9C864 /* NotificationsCenterCellViewModelEditRevertedTests.swift */, + 67C6F76D27E2E78000B9C864 /* NotificationsCenterCellViewModelEditMilestoneTests.swift */, + 67C6F76E27E2E78100B9C864 /* NotificationsCenterCellViewModelThanksTests.swift */, + 67C6F77227E2E78600B9C864 /* NotificationsCenterCellViewModelWelcomeTests.swift */, + 67C6F76927E2E77D00B9C864 /* NotificationsCenterCellViewModelWikidataConnectionTests.swift */, + 67C6F77127E2E78500B9C864 /* NotificationsCenterCellViewModelLoginIssuesTests.swift */, + 67C6F76F27E2E78300B9C864 /* NotificationsCenterCellViewModelUserRightsChangeTests.swift */, + 67C6F76A27E2E77E00B9C864 /* NotificationsCenterCellViewModelPageLinkTests.swift */, + 67C6F76B27E2E77E00B9C864 /* NotificationsCenterCellViewModelGenericTests.swift */, + 67DAEDE127E8FB60005CF9B6 /* NotificationsCenterDetailViewModelUserTalkMessageTests.swift */, + 67DAEDE527E8FB62005CF9B6 /* NotificationsCenterDetailViewModelMentionTests.swift */, + 67DAEDE327E8FB61005CF9B6 /* NotificationsCenterDetailViewModelEditRevertedTests.swift */, + 67DAEDE627E8FB62005CF9B6 /* NotificationsCenterDetailViewModelEditMilestoneTests.swift */, + 67DAEDE427E8FB61005CF9B6 /* NotificationsCenterDetailViewModelThanksTests.swift */, + 67DAEDDD27E8FB5F005CF9B6 /* NotificationsCenterDetailViewModelWelcomeTests.swift */, + 67DAEDE227E8FB60005CF9B6 /* NotificationsCenterDetailViewModelWikidataConnectionTests.swift */, + 67DAEDDE27E8FB5F005CF9B6 /* NotificationsCenterDetailViewModelLoginIssuesTests.swift */, + 67DAEDE027E8FB60005CF9B6 /* NotificationsCenterDetailViewModelUserRightsChangeTests.swift */, + 67DAEDE727E8FB62005CF9B6 /* NotificationsCenterDetailViewModelPageLinkTests.swift */, + 67DAEDDF27E8FB5F005CF9B6 /* NotificationsCenterDetailViewModelGenericTests.swift */, + ); + path = "Notifications Tests"; + sourceTree = ""; + }; + 67F73E6A2267B7650079DEEF /* Account */ = { + isa = PBXGroup; + children = ( + 67F73E6C2267B79E0079DEEF /* AccountViewController.swift */, + 832BD3BB28996B68002623CA /* VanishAccountContentView.swift */, + 67C78F7528B7406E00AC207A /* VanishAccountFooterView.swift */, + 83ED2E23289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift */, + 83EDC4C028B424B5007D0192 /* VanishAccountPopUpAlertView.swift */, + 006ABEE72901E8F600722DF8 /* VanishAccountWarningView.swift */, + 006ABEEC2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift */, + ); + name = Account; + sourceTree = ""; + }; + 67F73E6B2267B77C0079DEEF /* Old Talk Pages */ = { + isa = PBXGroup; + children = ( + 67E8B0A4226A64CB00537BC9 /* OldTalkPagesController.swift */, + 6734116322739CA2005B31DA /* TalkPageLocalHandler.swift */, + 833D6B47229EE872003CB650 /* TalkPageTopic+Extensions.swift */, + 67E8B0782268F8F100537BC9 /* Views */, + 67E8B0772268F8E300537BC9 /* View Controllers */, + 6789FA2D22E7790900E43842 /* TalkPage+Extensions.swift */, + ); + name = "Old Talk Pages"; + sourceTree = ""; + }; + 67F9AE5423AD7FF1003D4F5E /* MobileHTML Article Cache */ = { + isa = PBXGroup; + children = ( + B0E8065F1C0CE9030065EBC0 /* MWKImageInfoFetcher.h */, + B0E806601C0CE9030065EBC0 /* MWKImageInfoFetcher.m */, + 676A8A8223A4013D0084B967 /* ArticleFetcher.swift */, + 83D3FC12223A8BCD0048384B /* ArticleSummary.swift */, + 67A6F13D23BFEF4200736539 /* ArticleCacheController.swift */, + 678F511823A4B92000CE5357 /* ArticleCacheDBWriter.swift */, + 6773B2012411D8600022A70E /* ArticleCacheDBWriter+SyncResources.swift */, + 6773B2032411DCF50022A70E /* ArticleCacheResourceDBWriting.swift */, + ); + name = "MobileHTML Article Cache"; + sourceTree = ""; + }; + 70416BF62565D6C000D5BC33 /* Event Platform */ = { + isa = PBXGroup; + children = ( + 982800D524D302BF004B1850 /* EventPlatformClient.swift */, + 702096B8256C3D5700E27041 /* SamplingController.swift */, + 70B7981F257577B800C10BCA /* StorageManager.swift */, + 70B7982A25758E6D00C10BCA /* EPEventRecord+CoreDataClass.swift */, + 70B7983525758EB800C10BCA /* EPEventRecord+CoreDataProperties.swift */, + 70B798122575714100C10BCA /* EventPlatformEvents.xcdatamodeld */, + 7004A5B9268CEE680029C46B /* MetricsClientBridge.swift */, + ); + path = "Event Platform"; + sourceTree = ""; + }; + 7A03130021542F120095C953 /* Operations */ = { + isa = PBXGroup; + children = ( + 6761AEF42707BE4200E47BAD /* RemoteNotificationsRefreshOperation.swift */, + 67E5DA6A276416A600CE827D /* RemoteNotificationsRefreshCrossWikiOperation.swift */, + 7A0312FA215402FD0095C953 /* RemoteNotificationsImportOperation.swift */, + 67DA31872720957A0035D40F /* RemoteNotificationsPagingOperation.swift */, + 7A0312FE215422960095C953 /* RemoteNotificationsMarkReadOrUnreadOperation.swift */, + 6739A181273061220063E0E0 /* RemoteNotificationsMarkAllAsReadOperation.swift */, + 7A5357AA215552E7007998DC /* RemoteNotificationsOperation.swift */, + 672034E427A2600C007DC24F /* RemoteNotificationsProjectOperation.swift */, + 672034E227A2531F007DC24F /* RemoteNotificationsReauthenticateOperation.swift */, + ); + path = Operations; + sourceTree = ""; + }; + 7A03130121542F250095C953 /* Model */ = { + isa = PBXGroup; + children = ( + 6761AED82704BA3800E47BAD /* RemoteNotification+CoreDataClass.swift */, + 678D29AB2729EAD20036C5D9 /* RemoteNotification+CoreDataProperties.swift */, + 7A9133A822B162E7002AEBCF /* RemoteNotifications.xcdatamodeld */, + 670AF1CD26CA188B005F76D0 /* RemoteNotificationLinks.swift */, + 007B5FC426FA40F000180FF8 /* RemoteNotificationType.swift */, + 7A0312F62153C4990095C953 /* RemoteNotificationsModelController.swift */, + 6761AEDE2704CF0000E47BAD /* WikimediaProject+RemoteNotifications.swift */, + 6761AEEC2706247800E47BAD /* PushNotificationsSettings.swift */, + 6761AEEE2706249300E47BAD /* PushNotificationsCache.swift */, + 00D4B1B3282996A2008C705C /* EchoModelVersion.swift */, + 00A988072829D92B006D800B /* PushNotificationContentIdentifier.swift */, + 834F47F32833D91F00F86C80 /* RemoteNotificationFilterType.swift */, + ); + path = Model; + sourceTree = ""; + }; + 7A0D4D41225EABFC00774A5A /* Insert */ = { + isa = PBXGroup; + children = ( + 7A1C498E227254EC00230ED2 /* InsertMediaSearchViewController.swift */, + 7AF8CEEB22653406000B7676 /* InsertMediaSelectedImageView.swift */, + 7A1C4994227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift */, + 7A8422452268BBE70074648E /* InsertMediaImageInfoView.swift */, + 7A8422462268BBE70074648E /* InsertMediaImageInfoView.xib */, + 7AF8CEDD226527DC000B7676 /* Settings */, + 7A5A0541225FBDD300BBEAC1 /* Search */, + ); + name = Insert; + sourceTree = ""; + }; + 7A0DE4F120CEEBE40032AB57 /* Explore Feed */ = { + isa = PBXGroup; + children = ( + 7AA7011220DB352000F3F0BC /* Common */, + 7A0DE4FE20CEEC760032AB57 /* ExploreFeedSettingsViewController.swift */, + 7A28126120D3F84A009B42B5 /* FeedCardSettingsViewController.swift */, + ); + name = "Explore Feed"; + sourceTree = ""; + }; + 7A0FF2D1230349AA00E755D4 /* Counts */ = { + isa = PBXGroup; + children = ( + 7AB20A0A22FC8432006FECB4 /* PageHistoryCountsViewController.swift */, + 7AC19E312301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift */, + 7AC19E432301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift */, + 7AC19E442301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib */, + 7AB20A0B22FC8432006FECB4 /* PageHistoryCountsViewController.xib */, + 00D9276A29511E95004ECBEA /* PageHistoryCountsView.swift */, + ); + name = Counts; + sourceTree = ""; + }; + 7A1469BB220BBD5D000A20F1 /* Hint */ = { + isa = PBXGroup; + children = ( + 7A1469C2220BC19F000A20F1 /* Reading List */, + 7A1469C3220BC1A9000A20F1 /* Edit */, + 7A610CB6220A30C900C266AE /* HintViewController.swift */, + 7AFA21BA20110D7900E957E7 /* HintViewController.xib */, + 7A610CBC220A582A00C266AE /* HintController.swift */, + ); + name = Hint; + sourceTree = ""; + }; + 7A1469C2220BC19F000A20F1 /* Reading List */ = { + isa = PBXGroup; + children = ( + 7A203F091FDEDCDD00A229EC /* ReadingListHintController.swift */, + 7AFA21B920110D7900E957E7 /* ReadingListHintViewController.swift */, + ); + name = "Reading List"; + sourceTree = ""; + }; + 7A1469C3220BC1A9000A20F1 /* Edit */ = { + isa = PBXGroup; + children = ( + 7A1469BC220BBE44000A20F1 /* EditHintViewController.swift */, + 7A1469C4220BC223000A20F1 /* EditHintController.swift */, + ); + name = Edit; + sourceTree = ""; + }; + 7A255D3D215162AF0081A068 /* Remote Notifications */ = { + isa = PBXGroup; + children = ( + 7A52C01A2150389D00A3A4A1 /* RemoteNotificationsController.swift */, + 7A0312F82153DEB30095C953 /* RemoteNotificationsAPIController.swift */, + 7A03130221542F5C0095C953 /* RemoteNotificationsOperationsController.swift */, + 7A03130121542F250095C953 /* Model */, + 7A03130021542F120095C953 /* Operations */, + 67F73387273C26A000D7D713 /* NotificationServiceHelper.swift */, + 6713519C277285B7006C07D9 /* RemoteNotificationsRefreshDeadlineController.swift */, + ); + path = "Remote Notifications"; + sourceTree = ""; + }; + 7A27E85021B1971D001B2D21 /* Text Formatting */ = { + isa = PBXGroup; + children = ( + 7A6F55FE21AF508B0076D184 /* TextFormatting.storyboard */, + B0FFFB2921C9BED0001E787E /* TextFormattingButton.swift */, + 7A82899621B34BAC005D7EC1 /* Table View Cells */, + 7A27E85121B19767001B2D21 /* TextStyleFormattingTableViewController.swift */, + 7AE99B2D21CC53AB0092BE7F /* TextFontFormattingTableViewController.swift */, + 7AE99B2721CC4F420092BE7F /* TextSizeFormattingTableViewController.swift */, + 7A6F560421AF527A0076D184 /* TextFormattingInputViewController.swift */, + 7AFC79F721B0367700BB0C50 /* TextFormattingTableViewController.swift */, + ); + name = "Text Formatting"; + sourceTree = ""; + }; + 7A5A0541225FBDD300BBEAC1 /* Search */ = { + isa = PBXGroup; + children = ( + 7A9F2775225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift */, + 7A8422512268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift */, + 7A8422522268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib */, + 7A5A0542225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift */, + ); + name = Search; + sourceTree = ""; + }; + 7A7857E3219F196C00ED1579 /* Tools */ = { + isa = PBXGroup; + children = ( + 7AE98F14228339C4005944E4 /* Links */, + 7AC6B6AC225CE5DD00C285CB /* Media */, + 7A27E85021B1971D001B2D21 /* Text Formatting */, + 7AEC9863219F5D9C00BEF62B /* Toolbar */, + ); + name = Tools; + sourceTree = ""; + }; + 7A82899621B34BAC005D7EC1 /* Table View Cells */ = { + isa = PBXGroup; + children = ( + 7AA96D5B21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift */, + 7AF56C2E21DDEC1C00563A9C /* TextFormattingProviding.swift */, + 7A4D227B21B1CD8600D889BD /* TextFormattingTableViewCell.swift */, + 7A82898A21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift */, + 7A82896621B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift */, + 7ADF499121B45E68009EA338 /* Custom Table View Cell Views */, + ); + name = "Table View Cells"; + sourceTree = ""; + }; + 7A976F81207BAC2100F7EFE6 /* Authentication */ = { + isa = PBXGroup; + children = ( + 0EBB23191D1C450900CA9742 /* AuthenticationManager */, + ); + name = Authentication; + sourceTree = ""; + }; + 7A9C215921022E91000505EF /* Search */ = { + isa = PBXGroup; + children = ( + 7AF8B7402102297A009772CC /* SearchSettingsViewController.swift */, + ); + name = Search; + sourceTree = ""; + }; + 7AA7011220DB352000F3F0BC /* Common */ = { + isa = PBXGroup; + children = ( + B0432343210680A800A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift */, + 7A19C64720DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift */, + ); + name = Common; + sourceTree = ""; + }; + 7AB809E122679F9A00BFAB7C /* Advanced Settings */ = { + isa = PBXGroup; + children = ( + 7AB809DB22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift */, + 7A715660226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift */, + 7A71567822699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift */, + 7A715666226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift */, + 7A71566C22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift */, + 7A71566D22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib */, + 7A71565A226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift */, + ); + path = "Advanced Settings"; + sourceTree = ""; + }; + 7AB961D6202CB88E005DB2BA /* Protocols */ = { + isa = PBXGroup; + children = ( + 7ABAD6BE20349B91006A364C /* Collection.swift */, + FFD7B84524AEAB3F005C2471 /* ArticleScrolling.swift */, + FFD7B85824B3CA7A005C2471 /* ReferenceShowing.swift */, + FF921856252E8F4F00C39A8F /* ThanksGiving.swift */, + FF59DF4C2555E0CB0048E66C /* InternalLinkPreviewing.swift */, + FF5555632771388F00925099 /* CollectionViewContextMenuShowing.swift */, + 00E75B7527EB946D00A45B78 /* ReusableCell.swift */, + ); + name = Protocols; + sourceTree = ""; + }; + 7AC6B6AC225CE5DD00C285CB /* Media */ = { + isa = PBXGroup; + children = ( + 7AB7DEC7227203A600DD61A2 /* InsertMediaViewController.swift */, + 7A0D4D41225EABFC00774A5A /* Insert */, + ); + name = Media; + sourceTree = ""; + }; + 7ADF499121B45E68009EA338 /* Custom Table View Cell Views */ = { + isa = PBXGroup; + children = ( + 7ADF497921B45CEE009EA338 /* TextFormattingPlainToolbarView.swift */, + 7A0CD23F21DFA34000066F68 /* TextFormattingToolbarView.swift */, + 7ADF497A21B45CEE009EA338 /* TextFormattingPlainToolbarView.xib */, + 7AE1FE2F21B4A9790068BE9F /* TextFormattingButtonView.swift */, + 7AE1FE3021B4A9790068BE9F /* TextFormattingButtonView.xib */, + 7ADF498521B45E42009EA338 /* TextFormattingGroupedToolbarView.swift */, + 7ADF498621B45E42009EA338 /* TextFormattingGroupedToolbarView.xib */, + ); + name = "Custom Table View Cell Views"; + sourceTree = ""; + }; + 7AE98F14228339C4005944E4 /* Links */ = { + isa = PBXGroup; + children = ( + 7A27EDA12279F5270010CB24 /* InsertLinkViewController.swift */, + 7A6CA28C2289AF2200C7FD47 /* EditLinkViewController.swift */, + 7A6CA28D2289AF2200C7FD47 /* EditLinkViewController.xib */, + 83FDE798293564AC006D55FE /* Link.swift */, + ); + name = Links; + sourceTree = ""; + }; + 7AEC9863219F5D9C00BEF62B /* Toolbar */ = { + isa = PBXGroup; + children = ( + 7AEC9857219F529000BEF62B /* DefaultEditToolbarView.swift */, + 7A48EA0D21B5C9B20083F3DC /* EditToolbarView.swift */, + D876769E21E7B73C00491039 /* ToolbarSeparatorView.swift */, + 7AEC9858219F529000BEF62B /* DefaultEditToolbarView.xib */, + 7A25367521B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift */, + 7A25367621B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib */, + ); + name = Toolbar; + sourceTree = ""; + }; + 7AF49F85204EEDD800578861 /* Storage and syncing */ = { + isa = PBXGroup; + children = ( + 7AF49F7F204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift */, + 7A2FE5552051757E00F92F8F /* EraseSavedArticlesView.xib */, + 7A2FE55B20517BAE00F92F8F /* EraseSavedArticlesView.swift */, + ); + name = "Storage and syncing"; + sourceTree = ""; + }; + 7AF6F74C2239383100949393 /* Welcome */ = { + isa = PBXGroup; + children = ( + 7AF6F76522395BEB00949393 /* EditingWelcomeViewController.swift */, + 7ABE16FE2239B346006BA309 /* WelcomeViewController.swift */, + 7A70797B223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift */, + 7A70797C223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib */, + 7ABE170A2239B5A0006BA309 /* WelcomePageViewController.swift */, + 7ABE17222239BB54006BA309 /* WelcomePanelViewController.swift */, + 7ABE17232239BB54006BA309 /* WelcomePanelViewController.xib */, + 7ABE17162239B8EE006BA309 /* WelcomeContainerViewController.swift */, + 7ABE17342239DCF5006BA309 /* WelcomeAnimationViewController.swift */, + 7ABE173A2239DEF0006BA309 /* WelcomeAnimationView.swift */, + 7ABE17172239B8EE006BA309 /* WelcomeContainerViewController.xib */, + ); + name = Welcome; + sourceTree = ""; + }; + 7AF8CEDD226527DC000B7676 /* Settings */ = { + isa = PBXGroup; + children = ( + 7A9F060B2266425700856321 /* InsertMediaSettingsViewController.swift */, + 7AB809E122679F9A00BFAB7C /* Advanced Settings */, + 7A9F06172266432200856321 /* InsertMediaSettingsTextTableViewCell.swift */, + 7AB809CF22675B2300BFAB7C /* ThemeableTextView.swift */, + 7A9F06182266432200856321 /* InsertMediaSettingsTextTableViewCell.xib */, + 7A9524C922665E6400C55CDC /* InsertMediaSettingsImageView.swift */, + 7A9524D522669A8B00C55CDC /* InsertMediaSettingsButtonView.swift */, + 7A9524D622669A8B00C55CDC /* InsertMediaSettingsButtonView.xib */, + 7A9524CA22665E6400C55CDC /* InsertMediaSettingsImageView.xib */, + ); + name = Settings; + sourceTree = ""; + }; + 83023C1720E6581A00EC7592 /* Transitions */ = { + isa = PBXGroup; + children = ( + 83023C1020E6561900EC7592 /* ViewControllerTransitionsController.swift */, + 83023C1E20E6584F00EC7592 /* SearchTransition.swift */, + 83E776A220FFA4D700E26A47 /* DetailTransition.swift */, + ); + name = Transitions; + sourceTree = ""; + }; + 83510B0428F4CED800B6235B /* View */ = { + isa = PBXGroup; + children = ( + 00EACEC528E39D470054DDB4 /* TalkPageEmptyView.swift */, + 00CB6897288B0CD3002EBB0A /* TalkPageHeaderView.swift */, + 00D46DA42889B7F50015DE9B /* TalkPageView.swift */, + 83510B0628F4CF6400B6235B /* TalkPageErrorStateView.swift */, + ); + name = View; + sourceTree = ""; + }; + 83510B0528F4CF0200B6235B /* View Model */ = { + isa = PBXGroup; + children = ( + 00D1F58E28885BA300127169 /* TalkPageViewModel.swift */, + 0072990A28AC455500DCD2E6 /* TalkPageCellViewModel.swift */, + 00DEE61828AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift */, + ); + name = "View Model"; + sourceTree = ""; + }; + 83ACAA9F24E6DC8E003B3035 /* Command Line Tools */ = { + isa = PBXGroup; + children = ( + 83B019CC24F6ACAA0014B5EF /* Update Languages */, + ); + path = "Command Line Tools"; + sourceTree = ""; + }; + 83ACAAA024E6E371003B3035 /* Wikipedia */ = { + isa = PBXGroup; + children = ( + 83ACAAA124E6E38A003B3035 /* Wikipedia.swift */, + 83ACAAAA24E6E745003B3035 /* WikipediaLookup.swift */, + 83ACAAA324E6E42A003B3035 /* wikipedia-languages.json */, + 533AB8AD259792A9003A43D9 /* wikipedia-language-variants.json */, + ); + name = Wikipedia; + sourceTree = ""; + }; + 83ACF8EA28E5D7E3000F3B6F /* Model */ = { + isa = PBXGroup; + children = ( + 67BEFFD428AD9DF000606B38 /* TalkPageType.swift */, + 837A15F228DA591E00AAC3FC /* TalkPageCache.swift */, + ); + name = Model; + sourceTree = ""; + }; + 83B019CC24F6ACAA0014B5EF /* Update Languages */ = { + isa = PBXGroup; + children = ( + 83B019CD24F6ACAA0014B5EF /* WikipediaLanguageCommandLineUtility.swift */, + 83B019CE24F6ACAA0014B5EF /* WikipediaLanguageCommandLineUtilityAPI.swift */, + 83B019CF24F6ACAA0014B5EF /* main.swift */, + ); + path = "Update Languages"; + sourceTree = ""; + }; + 83C0688B292EEBE300DF1403 /* Coffee Roll */ = { + isa = PBXGroup; + children = ( + 00474A2928DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift */, + 005E004028DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift */, + 00474A2E28DD1B13002E3C09 /* TalkPageCoffeeRollView.swift */, + ); + name = "Coffee Roll"; + sourceTree = ""; + }; + 83C0688C292EEC0200DF1403 /* Formatting Toolbar */ = { + isa = PBXGroup; + children = ( + 83C06881292EC85700DF1403 /* TalkPageFormattingToolbarView.swift */, + 83C06886292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift */, + 83C0688D292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift */, + 83C06892292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift */, + 8303783F2940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift */, + ); + name = "Formatting Toolbar"; + sourceTree = ""; + }; + A452F9F524081A3400D8ED09 /* MockLocationManager */ = { + isa = PBXGroup; + children = ( + A452F9FC24081B0200D8ED09 /* MockUIDevice.swift */, + A452F9F724081A5500D8ED09 /* MockCLHeading.swift */, + A452F9F624081A5500D8ED09 /* MockCLLocationManager.swift */, + ); + name = MockLocationManager; + sourceTree = ""; + }; + B011FA471D470F4700AD7C5E /* FindInPage */ = { + isa = PBXGroup; + children = ( + 67E069072238A5A5008550AC /* WMFFindAndReplaceKeyboardBar.xib */, + 67E069052238A396008550AC /* FindAndReplaceKeyboardBar.swift */, + ); + name = FindInPage; + sourceTree = ""; + }; + B014909E1DB96A4C007F5391 /* Phone */ = { + isa = PBXGroup; + children = ( + 834C269D240D49F400245BE7 /* ReferenceViewController.swift */, + B01490A01DB96BD6007F5391 /* WMFReferencePanels.storyboard */, + B01490A21DB96E5F007F5391 /* WMFReferencePageViewController.swift */, + B0338A831DBF0AF20012F588 /* WMFReferencePageBackgroundView.swift */, + B01490A41DB96EA7007F5391 /* WMFReferencePanelViewController.swift */, + ); + name = Phone; + sourceTree = ""; + }; + B014909F1DB96A55007F5391 /* Tablet */ = { + isa = PBXGroup; + children = ( + B0379A291D8B756C00D973CF /* WMFReferencePopoverMessageViewController.h */, + B0379A2A1D8B756C00D973CF /* WMFReferencePopoverMessageViewController.m */, + B0379A361D8B9D2400D973CF /* WMFReferencePopoverMessageViewController.storyboard */, + ); + name = Tablet; + sourceTree = ""; + }; + B0267CE71E31698F006B6D8D /* ForgotPassword */ = { + isa = PBXGroup; + children = ( + B0267CE81E316A79006B6D8D /* WMFForgotPasswordViewController.storyboard */, + B0267CEC1E316AE3006B6D8D /* WMFForgotPasswordViewController.swift */, + B0267CF21E32A0CB006B6D8D /* WMFPasswordResetter.swift */, + ); + name = ForgotPassword; + sourceTree = ""; + }; + B03103251F677AF600E2FCF6 /* Exploration */ = { + isa = PBXGroup; + children = ( + B03103271F677BB400E2FCF6 /* WMFWelcomeExplorationViewController.swift */, + B03103261F677BB300E2FCF6 /* WMFWelcomeExplorationAnimationView.swift */, + B0C7A0781F710E74008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift */, + ); + name = Exploration; + sourceTree = ""; + }; + B043C9831F871AA8005400D3 /* Pages */ = { + isa = PBXGroup; + children = ( + 0E26B0891C0FD7F70004D687 /* Intro */, + B03103251F677AF600E2FCF6 /* Exploration */, + 0E26B08A1C0FD8040004D687 /* Language */, + 0E26B08B1C0FD80B0004D687 /* Analytics */, + ); + name = Pages; + sourceTree = ""; + }; + B04C44491E5695C900C6DFB0 /* Array */ = { + isa = PBXGroup; + children = ( + B04C444A1E56966B00C6DFB0 /* Array+WMFAllFieldsFilled.swift */, + ); + name = Array; + sourceTree = ""; + }; + B04DA3C71D9F044100F45DB7 /* Search Languages Bar */ = { + isa = PBXGroup; + children = ( + 83023C0420E51DDF00EC7592 /* SearchLanguagesBarViewController.swift */, + 83023C0520E51DDF00EC7592 /* SearchLanguagesBarViewController.xib */, + ); + name = "Search Languages Bar"; + sourceTree = ""; + }; + B0524B0121484F1F00D8FD8D /* Description Editing */ = { + isa = PBXGroup; + children = ( + 671DF9B425F2AE380011799E /* Controllers */, + B0D3E709214AF54F007578BA /* Form */, + B08013D22149F5DA00B52D03 /* Help */, + B0524B7A2148846F00D8FD8D /* Welcome */, + ); + name = "Description Editing"; + sourceTree = ""; + }; + B0524B7A2148846F00D8FD8D /* Welcome */ = { + isa = PBXGroup; + children = ( + B0524B0221484FB400D8FD8D /* DescriptionWelcome.storyboard */, + B0524B17214854E800D8FD8D /* DescriptionWelcomeImageViewController.swift */, + B0524B13214854E700D8FD8D /* DescriptionWelcomeContainerViewController.swift */, + B0524B19214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift */, + B0524B09214854E500D8FD8D /* DescriptionWelcomeInitialViewController.swift */, + B0524B11214854E700D8FD8D /* DescriptionWelcomePageViewController.swift */, + B0524B0B214854E600D8FD8D /* DescriptionWelcomePanelViewController.swift */, + B0524B74214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift */, + ); + name = Welcome; + sourceTree = ""; + }; + B0606EAF20AA6FF0006EC6B9 /* WikipediaUITests */ = { + isa = PBXGroup; + children = ( + B0606EB020AA6FF0006EC6B9 /* SnapshotRecorderTests.swift */, + B0606EC420AA955B006EC6B9 /* SnapshotHelper.swift */, + B0BDA58120B09A090098DB65 /* XCUIApplication+SnapshotUtilities.swift */, + B0606EB220AA6FF0006EC6B9 /* Info.plist */, + ); + path = WikipediaUITests; + sourceTree = ""; + }; + B08013D22149F5DA00B52D03 /* Help */ = { + isa = PBXGroup; + children = ( + B0524AEF2144D7BE00D8FD8D /* DescriptionHelpViewController.swift */, + B0524AF02144D7BE00D8FD8D /* DescriptionHelpViewController.xib */, + ); + name = Help; + sourceTree = ""; + }; + B08423DC2384E260005E93A0 /* URLTranslations */ = { + isa = PBXGroup; + children = ( + B08423DD2384E2C7005E93A0 /* WikipediaURLTranslations.swift */, + 83ACAAAC24E6EED9003B3035 /* WikipediaSiteInfoLookup.swift */, + B077A51323861E2200223526 /* wikipedia-namespaces */, + ); + name = URLTranslations; + sourceTree = ""; + }; + B0866F421CCAEB290088A789 /* Footer */ = { + isa = PBXGroup; + children = ( + B0866F431CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.h */, + B0866F441CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.m */, + B0866F451CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.xib */, + ); + name = Footer; + sourceTree = ""; + }; + B09D64E51C250B6400A29514 /* Footer Menu */ = { + isa = PBXGroup; + children = ( + BC45D5B41C330CB0007C72F3 /* Subviews */, + ); + name = "Footer Menu"; + sourceTree = ""; + }; + B0B423491EF204C200D3DC4C /* On This Day */ = { + isa = PBXGroup; + children = ( + B0B4234A1EF2055200D3DC4C /* WMFOnThisDayEventsFetcher.h */, + B0B4234B1EF2055200D3DC4C /* WMFOnThisDayEventsFetcher.m */, + ); + name = "On This Day"; + sourceTree = ""; + }; + B0B4235F1EF9D65F00D3DC4C /* On This Day */ = { + isa = PBXGroup; + children = ( + B0B423601EF9D69C00D3DC4C /* OnThisDayViewController.swift */, + B0B423661EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift */, + B0B423671EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib */, + ); + name = "On This Day"; + sourceTree = ""; + }; + B0BCF0B02025087B00986F72 /* Popover */ = { + isa = PBXGroup; + children = ( + B0BCF0AA2023AC7700986F72 /* ScrollableEducationPanelViewController.swift */, + B01EA07D2022856200813726 /* ScrollableEducationPanelView.xib */, + B0BCF0B8202537D800986F72 /* Panels.swift */, + B0016CB821354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift */, + ); + name = Popover; + sourceTree = ""; + }; + B0C17B9E21FB0F6F009ED5C3 /* Save */ = { + isa = PBXGroup; + children = ( + B0D4916E21F999A3002BBDD3 /* EditSaveViewController.swift */, + B0E804031C0CDE480065EBC0 /* EditSaveViewController.storyboard */, + B0F4761921F921D300C4E254 /* EditSummaryViewController.swift */, + B0F4761A21F921D300C4E254 /* EditSummaryViewController.xib */, + ); + name = Save; + sourceTree = ""; + }; + B0C6BE4D1E45255D0033BD6E /* Change Password */ = { + isa = PBXGroup; + children = ( + B0C6BE521E4526810033BD6E /* WMFChangePasswordViewController.storyboard */, + B0C6BE561E4526A40033BD6E /* WMFChangePasswordViewController.swift */, + ); + name = "Change Password"; + sourceTree = ""; + }; + B0D3E709214AF54F007578BA /* Form */ = { + isa = PBXGroup; + children = ( + 83B01F8A23DB399E001185F4 /* DescriptionEditViewController.storyboard */, + B0D3E70A214AF776007578BA /* DescriptionEditViewController.swift */, + ); + name = Form; + sourceTree = ""; + }; + B0DE92301D6E3A2000EC76A7 /* UIBarButtonItem Popover Message */ = { + isa = PBXGroup; + children = ( + B02376B21D6ECCF9007DBA73 /* UIViewController+WMFDynamicHeightPopoverMessage.h */, + B02376B31D6ECCF9007DBA73 /* UIViewController+WMFDynamicHeightPopoverMessage.m */, + B0DE92281D6E272B00EC76A7 /* WMFBarButtonItemPopoverMessageViewController.h */, + B0DE92291D6E272B00EC76A7 /* WMFBarButtonItemPopoverMessageViewController.m */, + B0DE92261D6E26C700EC76A7 /* WMFBarButtonItemPopoverMessageViewController.storyboard */, + ); + name = "UIBarButtonItem Popover Message"; + sourceTree = ""; + }; + B0ED17301E49129E008B70AD /* Two Factor */ = { + isa = PBXGroup; + children = ( + B0ED173C1E49831B008B70AD /* WMFTwoFactorPasswordViewController.storyboard */, + B0ED17331E4912EB008B70AD /* WMFTwoFactorPasswordViewController.swift */, + B01CFC5E1E70B00100B3546A /* WMFDeleteBackwardReportingTextField.swift */, + ); + name = "Two Factor"; + sourceTree = ""; + }; + B0EFCD761EBFBC6B008F36E5 /* Libraries Used */ = { + isa = PBXGroup; + children = ( + B0EFCD631EBEC231008F36E5 /* LibrariesUsed.storyboard */, + B0EFCD6C1EBF12E5008F36E5 /* LibrariesUsed.swift */, + ); + name = "Libraries Used"; + sourceTree = ""; + }; + B0F7CB521C8A87B300996DE0 /* Animation */ = { + isa = PBXGroup; + children = ( + B00DDEE21DB5B16E00615FA2 /* WMFWelcomeAnimationViewControllers.swift */, + B0F84EEC1C8E444400801560 /* WMFWelcomeAnimationView.swift */, + B08624301F72EA1900B770FD /* WMFWelcomeAnimationBackgroundView.swift */, + B0F0874F1C860E910086F710 /* WMFWelcomeAnimationExtensions.swift */, + ); + name = Animation; + sourceTree = ""; + }; + B3632E7E1EE5F97C007A2464 /* Event Logging New */ = { + isa = PBXGroup; + children = ( + B32535FE1EE87A6200372E93 /* EventRecord+CoreDataClass.swift */, + B32535FF1EE87A6200372E93 /* EventRecord+CoreDataProperties.swift */, + B3369A341EE1F69E0075953E /* EventLoggingService.swift */, + B32535EF1EE856FF00372E93 /* EventLogging.xcdatamodeld */, + 7A79A39220A24A7C00F9BDF9 /* EventLoggingStandardEventProviding.swift */, + ); + name = "Event Logging New"; + sourceTree = ""; + }; + B389CFCC1E6F234000483C06 /* Activities */ = { + isa = PBXGroup; + children = ( + B389CFCD1E6F238300483C06 /* WMFMapsActivity.swift */, + ); + name = Activities; + sourceTree = ""; + }; + BAFCE8401F1D7F8A0077D5E9 /* Appearance */ = { + isa = PBXGroup; + children = ( + BAFCE8411F1D7FD30077D5E9 /* AppearanceSettingsViewController.swift */, + BA7683C01F30C56300A487AA /* ImageDimmingExampleViewController.swift */, + BA7683C11F30C56300A487AA /* ImageDimmingExampleViewController.xib */, + BA4524161F324C3100439C42 /* FontSizeSliderViewController.swift */, + BA4524171F324C3100439C42 /* FontSizeSliderViewController.xib */, + BA4524221F32500C00439C42 /* TextSizeChangeExampleViewController.swift */, + BA4524231F32500C00439C42 /* TextSizeChangeExampleViewController.xib */, + ); + name = Appearance; + sourceTree = ""; + }; + BC23E4DE1C223EAB00B5AFDE /* Revisions */ = { + isa = PBXGroup; + children = ( + BC23E4DB1C223DCB00B5AFDE /* WMFArticleRevisionFetcher.h */, + BC23E4DC1C223DCB00B5AFDE /* WMFArticleRevisionFetcher.m */, + BC23E4E01C223FAE00B5AFDE /* WMFArticleRevision.h */, + BC23E4E31C22429100B5AFDE /* WMFRevisionQueryResults.h */, + BC23E4E41C22429100B5AFDE /* WMFRevisionQueryResults.m */, + BC23E4E11C223FAE00B5AFDE /* WMFArticleRevision.m */, + ); + name = Revisions; + sourceTree = ""; + }; + BC45D5451C31E092007C72F3 /* Application */ = { + isa = PBXGroup; + children = ( + D4B0ADFF19365F4600F0AC90 /* Analytics */, + BC45D57D1C32F3EA007C72F3 /* Diagnostics */, + BC45D5461C31E0C0007C72F3 /* App View Controller */, + 0EF5BB6B1C110C2100DE75E1 /* AppDelegate.h */, + 0EF5BB6C1C110C2100DE75E1 /* AppDelegate.m */, + 0EBCA7411C162ECF004F1FD9 /* MWKTitleLanguageController.m */, + 0EBCA7421C162ECF004F1FD9 /* MWKTitleLanguageController.h */, + B0E8071E1C0CEC8A0065EBC0 /* main.m */, + 83A642742226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift */, + 7AC92E65228D8C7B0035E7F0 /* NavigationStateController.swift */, + 8320332222B906A0004A9EDA /* NavigationState.swift */, + 8320332022B90548004A9EDA /* NSManagedObjectContext+NavigationState.swift */, + 830C0DD423D9AFBE006471C4 /* UIViewController+Push.swift */, + 0E69CD5A1C8773410095918B /* Launch Screen.storyboard */, + BCA15AE01C0DFCC900D0A3EA /* Supporting Files */, + ); + name = Application; + sourceTree = ""; + }; + BC45D5461C31E0C0007C72F3 /* App View Controller */ = { + isa = PBXGroup; + children = ( + B0E802BF1C0CD27F0065EBC0 /* WMFAppViewController.h */, + B0E802C01C0CD27F0065EBC0 /* WMFAppViewController.m */, + 53A575F92602C845009835E6 /* WMFAppViewController+Extensions.swift */, + 8321FCCB2387231E0079F3C7 /* ViewControllerRouter.swift */, + B0421AA1206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift */, + FFE891452445150B0058B642 /* AppTabBarDelegate.swift */, + ); + name = "App View Controller"; + sourceTree = ""; + }; + BC45D5471C31E1EA007C72F3 /* Common */ = { + isa = PBXGroup; + children = ( + BC45D5561C31EEBE007C72F3 /* Error Handling */, + BC45D5591C31EFF8007C72F3 /* i18n */, + BC628C791B389E2B00B3F85C /* Images */, + BCB669A31A83F6C300C7B1FE /* Model */, + 0487041519F824D700B7D307 /* Networking */, + 0EFB48091B3BB01300381F99 /* Style */, + BC45D54E1C31EAD6007C72F3 /* UI Components */, + BC45D58B1C32FC15007C72F3 /* Utilities */, + 0E281A0E1DC1391900FA1AB1 /* Housekeeping */, + ); + name = Common; + sourceTree = ""; + }; + BC45D54E1C31EAD6007C72F3 /* UI Components */ = { + isa = PBXGroup; + children = ( + 671AC2542226FB26005B37F8 /* Reading Themes Controls */, + 0EE151921BF540420039828A /* Compass View */, + BC45D56C1C32E910007C72F3 /* Buttons */, + B0BCF0B02025087B00986F72 /* Popover */, + D896C7941D6F2E4E007101DF /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.h */, + D896C7951D6F2E4E007101DF /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m */, + 83DB440F244A57590046FABE /* RootNavigationController.swift */, + D850A5381F8686DE006FD295 /* WMFThemeableNavigationController.h */, + D850A5391F8686DE006FD295 /* WMFThemeableNavigationController.m */, + 67E06918223B32DF008550AC /* FocusNavigationView.xib */, + 67E0691A223B32F1008550AC /* FocusNavigationView.swift */, + 672C35EA22D8E7C9007B8D46 /* EmptyViewController.swift */, + 671F5E0A236B8CAF00111116 /* EmptyViewController.xib */, + 67282FBC24855B7B00B73E20 /* ArticleContextMenuPresenting.swift */, + 6747118725072D1500287951 /* IconTitleBadge.swift */, + 00BCB71726DEE04D002C3F72 /* InsetLabelView.swift */, + 00BCB71C26DEE1C7002C3F72 /* VerticalSpacerView.swift */, + 00BCB72126DEEB1C002C3F72 /* RoundedImageView.swift */, + 007F5C6C275AA74200E4B02C /* StackedImageLabelView.swift */, + 0010F93827A49C7700D77848 /* HorizontalSpacerView.swift */, + 67C78F7028B6DA1300AC207A /* SwiftUITextView.swift */, + 6780CF3129676DB500D45927 /* Shifting Top Views */, + ); + name = "UI Components"; + sourceTree = ""; + }; + BC45D54F1C31EB3B007C72F3 /* Buttons */ = { + isa = PBXGroup; + children = ( + B0E802B61C0CD2140065EBC0 /* UIBarButtonItem+WMFButtonConvenience.h */, + B0E802B71C0CD2140065EBC0 /* UIBarButtonItem+WMFButtonConvenience.m */, + B0E802BC1C0CD2360065EBC0 /* UIButton+WMFButton.h */, + B0E802BD1C0CD2360065EBC0 /* UIButton+WMFButton.m */, + B0EF42CE1C43FDD200D125A8 /* UIApplicationShortcutItem+WMFShortcutItem.h */, + B0EF42CF1C43FDD200D125A8 /* UIApplicationShortcutItem+WMFShortcutItem.m */, + 00EBB7C627D6878E002025AC /* BarButtonImageStyle.swift */, + ); + name = Buttons; + sourceTree = ""; + }; + BC45D5501C31EB4E007C72F3 /* Utilities */ = { + isa = PBXGroup; + children = ( + BC45D54F1C31EB3B007C72F3 /* Buttons */, + 672D69A3273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift */, + 672D69A8273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift */, + ); + name = Utilities; + sourceTree = ""; + }; + BC45D5531C31ECB1007C72F3 /* RTL Utilities */ = { + isa = PBXGroup; + children = ( + B00050131C52D73800515F70 /* UIApplication+RTL.swift */, + ); + name = "RTL Utilities"; + sourceTree = ""; + }; + BC45D5561C31EEBE007C72F3 /* Error Handling */ = { + isa = PBXGroup; + children = ( + 0EBCA7471C176389004F1FD9 /* WMFAlertManager.swift */, + B08E7E991C1FA57A00EC3C99 /* UIViewController+WMFEmptyView.h */, + B08E7E9A1C1FA57A00EC3C99 /* UIViewController+WMFEmptyView.m */, + 0EF8634F1C19E4F100006D2D /* WMFEmptyView.h */, + 0EF863501C19E4F100006D2D /* WMFEmptyView.m */, + 0EF8634D1C19E02700006D2D /* WMFEmptyView.xib */, + ); + name = "Error Handling"; + sourceTree = ""; + }; + BC45D5591C31EFF8007C72F3 /* i18n */ = { + isa = PBXGroup; + children = ( + BC45D5531C31ECB1007C72F3 /* RTL Utilities */, + ); + name = i18n; + sourceTree = ""; + }; + BC45D5661C32E6E8007C72F3 /* Grand Central Dispatch */ = { + isa = PBXGroup; + children = ( + B0E807301C0CED810065EBC0 /* WMFGCDHelpers.h */, + 0EF5BB661C110BFC00DE75E1 /* WMFBlockDefinitions.h */, + ); + name = "Grand Central Dispatch"; + sourceTree = ""; + }; + BC45D56C1C32E910007C72F3 /* Buttons */ = { + isa = PBXGroup; + children = ( + 67D9D1F42970D8B000BFCD4F /* Button Styles */, + 67D9D1EF2970D88E00BFCD4F /* DisclosureButton.swift */, + 672F0557222F24FB00FB1084 /* IconBarButtonItem.swift */, + 7A4B333B2136EDED00C6C820 /* UnderlineButton.swift */, + B0E803CA1C0CDC9B0065EBC0 /* WMFTitleInsetRespectingButton.h */, + B0E803CB1C0CDC9B0065EBC0 /* WMFTitleInsetRespectingButton.m */, + 00AA5AA6276BF29E005295B0 /* StatusTextBarButtonItem.swift */, + 00AA5AAB276BF2AE005295B0 /* TextBarButtonItem.swift */, + ); + name = Buttons; + sourceTree = ""; + }; + BC45D56E1C32E97B007C72F3 /* Common */ = { + isa = PBXGroup; + children = ( + B0E806291C0CE7670065EBC0 /* MTLValueTransformer+WMFNumericValueTransformer.h */, + B0E8062A1C0CE7670065EBC0 /* MTLValueTransformer+WMFNumericValueTransformer.m */, + ); + name = Common; + sourceTree = ""; + }; + BC45D5711C32EADD007C72F3 /* NSObject */ = { + isa = PBXGroup; + children = ( + B0E8072F1C0CED810065EBC0 /* WMFComparison.h */, + B0E807311C0CED810065EBC0 /* WMFHashing.h */, + ); + name = NSObject; + sourceTree = ""; + }; + BC45D5731C32EB47007C72F3 /* Logging */ = { + isa = PBXGroup; + children = ( + B0E804731C0CE0B40065EBC0 /* DDLog+WMFLogger.h */, + B0E804741C0CE0B40065EBC0 /* DDLog+WMFLogger.m */, + BCA15AE41C0E213300D0A3EA /* LoggingDefaults.swift */, + B0E807341C0CED810065EBC0 /* WMFLogFormatter.h */, + B0E807351C0CED810065EBC0 /* WMFLogFormatter.m */, + FFA0641825A943EB00B9460B /* BasicLogger.swift */, + ); + name = Logging; + sourceTree = ""; + }; + BC45D5751C32EE01007C72F3 /* Utilities */ = { + isa = PBXGroup; + children = ( + B0E8052D1C0CE0DC0065EBC0 /* WKWebView+ElementLocation.h */, + B0E8052E1C0CE0DC0065EBC0 /* WKWebView+ElementLocation.m */, + 7A2BB1D321F27AC5004C0FDF /* WKWebView+OffsetHack.swift */, + B0DF6F7F1CFE1D0B0046E507 /* WKWebView+WMFWebViewControllerJavascript.h */, + B0DF6F801CFE1D0B0046E507 /* WKWebView+WMFWebViewControllerJavascript.m */, + B09CE599222F623800067D2A /* WKWebView+EditSelectionJavascript.swift */, + ); + name = Utilities; + sourceTree = ""; + }; + BC45D57D1C32F3EA007C72F3 /* Diagnostics */ = { + isa = PBXGroup; + children = ( + BC45D5731C32EB47007C72F3 /* Logging */, + ); + name = Diagnostics; + sourceTree = ""; + }; + BC45D5811C32F6BC007C72F3 /* UIViewController */ = { + isa = PBXGroup; + children = ( + 0ED2E9F91CB2B3B300D1C844 /* UIVIewController+WMFCommonRotationSupport.swift */, + B0E803E41C0CDD450065EBC0 /* UIViewController+WMFStoryboardUtilities.h */, + B0E803E51C0CDD450065EBC0 /* UIViewController+WMFStoryboardUtilities.m */, + B09B30CE1DB813760012281F /* UIViewController+WMFStoryboardUtilities.swift */, + B027447E1E665D9F00E7B248 /* UIViewController+WMFChildViewController.swift */, + B066F0D41E513DAA00A199F8 /* UIViewController+WMFHideKeyboard.swift */, + ); + name = UIViewController; + sourceTree = ""; + }; + BC45D5821C32F745007C72F3 /* UIView */ = { + isa = PBXGroup; + children = ( + B0E8038F1C0CDABE0065EBC0 /* UIView+WMFSnapshotting.h */, + B0E803901C0CDABE0065EBC0 /* UIView+WMFSnapshotting.m */, + B0E805181C0CE0DC0065EBC0 /* UIView+IBExtras.swift */, + B0E8051F1C0CE0DC0065EBC0 /* UIView+WMFFrameUtils.h */, + B0E805201C0CE0DC0065EBC0 /* UIView+WMFFrameUtils.m */, + B00DDEDA1DB4B76B00615FA2 /* UIView+WMFSubviews.swift */, + ); + name = UIView; + sourceTree = ""; + }; + BC45D5831C32F79A007C72F3 /* UIScrollView */ = { + isa = PBXGroup; + children = ( + B0E805081C0CE0DC0065EBC0 /* UIScrollView+ScrollSubviewToLocation.h */, + B0E805091C0CE0DC0065EBC0 /* UIScrollView+ScrollSubviewToLocation.m */, + B0E8050A1C0CE0DC0065EBC0 /* UIScrollView+WMFContentOffsetUtils.h */, + B0E8050B1C0CE0DC0065EBC0 /* UIScrollView+WMFContentOffsetUtils.m */, + ); + name = UIScrollView; + sourceTree = ""; + }; + BC45D5851C32F813007C72F3 /* Strings */ = { + isa = PBXGroup; + children = ( + B0E804831C0CE0B40065EBC0 /* NSAttributedString+WMFModify.h */, + B0E804841C0CE0B40065EBC0 /* NSAttributedString+WMFModify.m */, + B0E8048B1C0CE0B40065EBC0 /* NSCharacterSet+WMFExtras.h */, + B0E8048C1C0CE0B40065EBC0 /* NSCharacterSet+WMFExtras.m */, + B0E804A61C0CE0B40065EBC0 /* NSString+FormattedAttributedString.h */, + B0E804A71C0CE0B40065EBC0 /* NSString+FormattedAttributedString.m */, + 8356115C28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift */, + 00FCCBCE2900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift */, + 009C8EC129071E720056A3AC /* NSString+Range.swift */, + ); + name = Strings; + sourceTree = ""; + }; + BC45D5871C32F849007C72F3 /* Settings */ = { + isa = PBXGroup; + children = ( + 7A0DE4F120CEEBE40032AB57 /* Explore Feed */, + BAFCE8401F1D7F8A0077D5E9 /* Appearance */, + D84BF6281DB96D4700E0C85E /* Notifications */, + 042A5B1419253D2A0095E172 /* View Controller */, + 045D871D19FAD2D00035C1F9 /* About The App */, + 0E8DC0961C7632B500622CBD /* Settings Item */, + 7AF49F85204EEDD800578861 /* Storage and syncing */, + 0EE2438B1DC9889B00066CBD /* WMFTableHeaderFooterLabelView.h */, + 673FC3CF273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift */, + 0EE2438C1DC9889B00066CBD /* WMFTableHeaderFooterLabelView.m */, + 0EE2438E1DC988AA00066CBD /* WMFTableHeaderFooterLabelView.xib */, + 7A9C215921022E91000505EF /* Search */, + 83F1097223D0F115003F3E9E /* HelpViewController.swift */, + 7A16C4E4212D941C00F0D5EC /* SubSettingsViewController.swift */, + 7A16C4E5212D941C00F0D5EC /* SubSettingsViewController.xib */, + 00EBB7CB27D6A86A002025AC /* SettingsPresentationDelegate.swift */, + 67B5333B28416A3B00C33E13 /* UserDataExportCache.swift */, + ); + name = Settings; + sourceTree = ""; + }; + BC45D58B1C32FC15007C72F3 /* Utilities */ = { + isa = PBXGroup; + children = ( + 67DB110822613EF700F789B0 /* SchemeHandler */, + 04C43AB7183442FC006C643B /* Categories */, + 83ACAAA024E6E371003B3035 /* Wikipedia */, + B08423DC2384E260005E93A0 /* URLTranslations */, + 7616D4941C5A67D20077ADF7 /* WMFUtilityMacros.h */, + D84BF62E1DBE616D00E0C85E /* UIViewController+WMFAlerts.swift */, + 7AFEB1BB1FA236A100B8DF32 /* UIViewController+Peekable.swift */, + D8FEECCB1DE3729400B883F0 /* WMFChange.h */, + D8FEECCC1DE3729400B883F0 /* WMFChange.m */, + D87914DC1DFA04E10012C5DA /* NSUserDefaults+WMFApplicationDefaults.swift */, + BA8203E11F15B4CC00925E93 /* ShareActivityController.swift */, + 7AEBAD442102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift */, + 7AB6F0FE22AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift */, + 83DB0A5D23EEDE4400DA5F58 /* LegacyArticle.swift */, + 83DB0A5623EEDE2100DA5F58 /* MobileviewToMobileHTMLConverter.swift */, + 0022DD2825829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift */, + B087F8D023E3AE3B00ACA012 /* MWKDataStore+LegacyMobileview.swift */, + 676C869226D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift */, + 8397601A2811ABC7000FE3B1 /* UNAuthorizationStatus+String.swift */, + 67BEFFD928AEDF3600606B38 /* WikimediaProject.swift */, + ); + name = Utilities; + sourceTree = ""; + }; + BC45D58D1C32FD58007C72F3 /* Features */ = { + isa = PBXGroup; + children = ( + 6798035A24F94CAE00D765AA /* Article as a Living Document */, + 67DC5BDB23A00DF500B03A84 /* Article */, + 6782DB8F2343B691003FA21B /* Diff */, + 7A1469BB220BBD5D000A20F1 /* Hint */, + 007CCF0526D5A10700D5EA7C /* Notifications Center */, + D87233FC1E1FF05800751E83 /* Places */, + 0EFB48071B3BAFE900381F99 /* OldArticle */, + 0E98805F1DA2A0C00058D7F2 /* Feed */, + 041A3B5718E11ED90079FF1C /* Languages */, + D84F2BF61D2FEE4B00963D42 /* Random */, + BCD67E7E1C1F1433005179E1 /* Saved Pages */, + 0E2B06F91B2D128D00EA2F53 /* Search */, + BC45D5871C32F849007C72F3 /* Settings */, + 67F73E6A2267B7650079DEEF /* Account */, + 67F73E6B2267B77C0079DEEF /* Old Talk Pages */, + BC45D59F1C33018C007C72F3 /* User */, + B0524B0121484F1F00D8FD8D /* Description Editing */, + 0E26B0541C0E28E60004D687 /* Welcome */, + 00D1F58D28885B8200127169 /* Talk Pages */, + ); + name = Features; + sourceTree = ""; + }; + BC45D59F1C33018C007C72F3 /* User */ = { + isa = PBXGroup; + children = ( + 0E26628F1D09F583006D4A46 /* Authentication */, + ); + name = User; + sourceTree = ""; + }; + BC45D5A01C330393007C72F3 /* Sharing */ = { + isa = PBXGroup; + children = ( + B389CFCC1E6F234000483C06 /* Activities */, + 0EC0447C1C7974590033D773 /* Share Sources */, + ); + name = Sharing; + sourceTree = ""; + }; + BC45D5A11C33042B007C72F3 /* Editing */ = { + isa = PBXGroup; + children = ( + 7AF6F74C2239383100949393 /* Welcome */, + 7A7857E3219F196C00ED1579 /* Tools */, + 044BD6B218849AD000FFE4BE /* SectionEditor */, + 04DD89AE18BFE63A00DD5DAD /* Preview */, + B0C17B9E21FB0F6F009ED5C3 /* Save */, + ); + name = Editing; + sourceTree = ""; + }; + BC45D5A21C330480007C72F3 /* Model */ = { + isa = PBXGroup; + children = ( + B0E807C91C0CF04A0065EBC0 /* MWKSearchRedirectMapping.h */, + B0E807CA1C0CF04A0065EBC0 /* MWKSearchRedirectMapping.m */, + B0E803451C0CD7AA0065EBC0 /* WMFSearchResults_Internal.h */, + B0E803461C0CD7AA0065EBC0 /* WMFSearchResults.h */, + B0E803471C0CD7AA0065EBC0 /* WMFSearchResults.m */, + ); + name = Model; + sourceTree = ""; + }; + BC45D5A71C33090A007C72F3 /* Views */ = { + isa = PBXGroup; + children = ( + 0EF2249B1CC5536B00FDF78E /* Cell */, + 0EF2249C1CC5537100FDF78E /* Header */, + B0866F421CCAEB290088A789 /* Footer */, + ); + name = Views; + sourceTree = ""; + }; + BC45D5AB1C330A8E007C72F3 /* Components */ = { + isa = PBXGroup; + children = ( + 0E74DC821BEBBF4200A8A005 /* Article Footer */, + BC45D5A11C33042B007C72F3 /* Editing */, + 04F27B6D18FE0F2E00EDD838 /* PageHistory */, + BC45D5A01C330393007C72F3 /* Sharing */, + 04CCA0BD19830837000E982A /* References */, + 0E4D1CFD1BBDC72F009BEB64 /* Table of Contents */, + 04478625185145090050563B /* WebView */, + ); + name = Components; + sourceTree = ""; + }; + BC45D5AF1C330B61007C72F3 /* Model */ = { + isa = PBXGroup; + children = ( + B0E807C71C0CF04A0065EBC0 /* MWKLocationSearchResult.h */, + B0E807C81C0CF04A0065EBC0 /* MWKLocationSearchResult.m */, + B0E802FE1C0CD5000065EBC0 /* WMFLocationSearchResults.h */, + B0E802FF1C0CD5000065EBC0 /* WMFLocationSearchResults.m */, + ); + name = Model; + sourceTree = ""; + }; + BC45D5B11C330C64007C72F3 /* Page Issues */ = { + isa = PBXGroup; + children = ( + BAA0D91B1F4F165A00091284 /* PageIssuesTableViewController.swift */, + ); + name = "Page Issues"; + sourceTree = ""; + }; + BC45D5B41C330CB0007C72F3 /* Subviews */ = { + isa = PBXGroup; + children = ( + BC45D5B11C330C64007C72F3 /* Page Issues */, + ); + name = Subviews; + sourceTree = ""; + }; + BC45D5B71C330D27007C72F3 /* About the article */ = { + isa = PBXGroup; + children = ( + B09D64E51C250B6400A29514 /* Footer Menu */, + ); + name = "About the article"; + sourceTree = ""; + }; + BC628C791B389E2B00B3F85C /* Images */ = { + isa = PBXGroup; + children = ( + BCA15B111C0E9B1600D0A3EA /* Gallery */, + ); + name = Images; + sourceTree = ""; + }; + BC7C3A371C0FF94A0057F023 /* Code */ = { + isa = PBXGroup; + children = ( + D84C36461F32485E00895FA1 /* Model */, + D818D37F1ED725240076110D /* View Controllers */, + D83F5C051F0E5111006130FF /* Views */, + BC45D5451C31E092007C72F3 /* Application */, + BC45D5471C31E1EA007C72F3 /* Common */, + D810C34C1DF1C36F003427DA /* Third Party */, + BC45D58D1C32FD58007C72F3 /* Features */, + 7AB961D6202CB88E005DB2BA /* Protocols */, + ); + path = Code; + sourceTree = ""; + }; + BC7C3A381C0FF9620057F023 /* Code */ = { + isa = PBXGroup; + children = ( + 6714D6C9245A2B6A00CE5A4A /* Manual Tests */, + BCA6764F1AC05FE200A16160 /* Utilities */, + BCD67E7D1C1F12F2005179E1 /* Tests */, + 0042816E25E6EF58004945B3 /* Third Party */, + ); + name = Code; + sourceTree = ""; + }; + BC7FA4BF1BD6A687006CA1A3 /* View Controller */ = { + isa = PBXGroup; + children = ( + B0E8036C1C0CD98B0065EBC0 /* TableOfContentsViewController.swift */, + ); + name = "View Controller"; + sourceTree = ""; + }; + BC7FA4C01BD6A693006CA1A3 /* Views */ = { + isa = PBXGroup; + children = ( + B0E803721C0CD9C10065EBC0 /* TableOfContentsCell.swift */, + B0E803731C0CD9C10065EBC0 /* TableOfContentsCell.xib */, + D84692DE1D5E1E3F000A7058 /* TableOfContentsHeader.swift */, + D84692DF1D5E1E3F000A7058 /* TableOfContentsHeader.xib */, + ); + name = Views; + sourceTree = ""; + }; + BC8309941A7BF935003FC5C7 /* Tests */ = { + isa = PBXGroup; + children = ( + BC7C3A381C0FF9620057F023 /* Code */, + BCA15AE11C0DFD1600D0A3EA /* Supporting Files */, + ); + name = Tests; + path = WikipediaUnitTests; + sourceTree = ""; + }; + BCA15AE01C0DFCC900D0A3EA /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D8EC3FB61E9BDAAB006712EB /* Staging-Info.plist */, + D8A42C2D1E815B0700D8E281 /* User Testing-Info.plist */, + 0ED79A3B1CB5B507005D9AF5 /* Wikipedia-Info.plist */, + 83A422B422786E2000BA446A /* Local-Info.plist */, + D8CE26B01E698E2500DAE2E0 /* Experimental-Info.plist */, + 0E10C4FD1C81046300CEB5C2 /* Wikipedia.entitlements */, + D8A42C2F1E815C1300D8E281 /* Wikipedia User Testing.entitlements */, + B0E808281C0D07EA0065EBC0 /* Wikipedia-Bridging-Header.h */, + ); + name = "Supporting Files"; + path = ..; + sourceTree = ""; + }; + BCA15AE11C0DFD1600D0A3EA /* Supporting Files */ = { + isa = PBXGroup; + children = ( + B0E8086B1C0D15170065EBC0 /* WMFCodingStyle.h */, + B0E8086C1C0D15170065EBC0 /* WMFCodingStyle.m */, + B0E8096D1C0D1DD50065EBC0 /* WikipediaUnitTests-Bridging-Header.h */, + B0E8096E1C0D21530065EBC0 /* Info.plist */, + B0E809701C0D215D0065EBC0 /* WikipediaUnitTests-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + BCA15B111C0E9B1600D0A3EA /* Gallery */ = { + isa = PBXGroup; + children = ( + 0E4A34701CBBFCD400A400F6 /* WMFImageGalleryViewController.h */, + 0E4A34711CBBFCD400A400F6 /* WMFImageGalleryViewController.m */, + 8330532D23EF107D00123141 /* MediaListGalleryViewController.swift */, + 0E9B9E341CBF3262001E4C3C /* Overlay View */, + ); + name = Gallery; + sourceTree = ""; + }; + BCA6764F1AC05FE200A16160 /* Utilities */ = { + isa = PBXGroup; + children = ( + A452F9F524081A3400D8ED09 /* MockLocationManager */, + BCD41E071B11D2ED00231BB1 /* Fixture Utilities */, + BCD41E081B11D30B00231BB1 /* Persistence Utilities */, + B0E8088D1C0D16140065EBC0 /* WMFAsyncTestCase.h */, + B0E8088E1C0D16140065EBC0 /* WMFAsyncTestCase.m */, + B0E808931C0D16330065EBC0 /* NSArray+WMFShuffle.h */, + B0E808941C0D16330065EBC0 /* NSArray+WMFShuffle.m */, + B0E808961C0D16430065EBC0 /* XCTestCase+WMFLocaleTesting.h */, + B0E808971C0D16430065EBC0 /* XCTestCase+WMFLocaleTesting.m */, + B0E8089B1C0D165B0065EBC0 /* XCTestCase+SwiftDefaults.swift */, + B0E808A01C0D16730065EBC0 /* XCTAssert+CGGeometry.h */, + B0E808A71C0D16A00065EBC0 /* UIView+VisualTestSizingUtils.h */, + B0E808A81C0D16A00065EBC0 /* UIView+VisualTestSizingUtils.m */, + D8D5507F1DF0D2BD00B90177 /* NSArray+WMFMatching.h */, + D8D550801DF0D2BD00B90177 /* NSArray+WMFMatching.m */, + B0E808B41C0D17070065EBC0 /* LSStubResponseDSL+WithJSON.h */, + B0E808B51C0D17070065EBC0 /* LSStubResponseDSL+WithJSON.m */, + B0E808B71C0D17160065EBC0 /* WMFHTTPHangingProtocol.h */, + B0E808B81C0D17160065EBC0 /* WMFHTTPHangingProtocol.m */, + BC45FF461C1B1F9F00BAE501 /* NSUserDefaults+WMFReset.h */, + BC45FF471C1B1F9F00BAE501 /* NSUserDefaults+WMFReset.m */, + BC45FF491C1B22C200BAE501 /* NSObject+WMFReflection.h */, + BC45FF4A1C1B22C200BAE501 /* NSObject+WMFReflection.m */, + BC8B4F1D1C77B29A009B06F7 /* LSNocilla+AnyRequest.h */, + BCD557B91C45B1600060A51A /* UIApplication+VisualTestUtils.h */, + BCD557BA1C45B1600060A51A /* UIApplication+VisualTestUtils.m */, + BC62AE601C58FC810064C589 /* NSProcessInfo+WMFTestEnvironment.h */, + BC62AE611C58FC810064C589 /* NSProcessInfo+WMFTestEnvironment.m */, + BCD320081C6EC6BC00317D08 /* NSTimeZone+WMFTestingUtils.h */, + BCD320091C6EC6BC00317D08 /* NSTimeZone+WMFTestingUtils.m */, + 6714D6CC245A2C1D00CE5A4A /* ArticleTestHelpers.swift */, + ); + name = Utilities; + sourceTree = ""; + }; + BCB669621A83DB8100C7B1FE /* Serializers */ = { + isa = PBXGroup; + children = ( + BC45D56E1C32E97B007C72F3 /* Common */, + 83E880E723EB19270087223F /* MediaList.swift */, + ); + name = Serializers; + sourceTree = ""; + }; + BCB669A31A83F6C300C7B1FE /* Model */ = { + isa = PBXGroup; + children = ( + 229C20D91CB08FA500BC17AD /* PageHistory */, + ); + name = Model; + path = Wikipedia/Code; + sourceTree = SOURCE_ROOT; + }; + BCCB813F1C110721008BC602 /* Picture of the Day */ = { + isa = PBXGroup; + children = ( + BC62FFBE1C11064200533DA9 /* MWKImageInfoFetcher+PicOfTheDayInfo.h */, + BC62FFBF1C11064200533DA9 /* MWKImageInfoFetcher+PicOfTheDayInfo.m */, + BCCB813B1C110702008BC602 /* NSDate+WMFPOTDTitle.h */, + BCCB813C1C110702008BC602 /* NSDate+WMFPOTDTitle.m */, + ); + name = "Picture of the Day"; + sourceTree = ""; + }; + BCCB81461C110857008BC602 /* POTD */ = { + isa = PBXGroup; + children = ( + 8382F8CC20D9206000AE5250 /* ImageCollectionViewCell.swift */, + ); + name = POTD; + sourceTree = ""; + }; + BCD41E071B11D2ED00231BB1 /* Fixture Utilities */ = { + isa = PBXGroup; + children = ( + B0E8086E1C0D15330065EBC0 /* WMFTestFixtureUtilities.h */, + B0E808721C0D154C0065EBC0 /* NSBundle+TestAssets.h */, + B0E808731C0D154C0065EBC0 /* NSBundle+TestAssets.m */, + B0E808751C0D155A0065EBC0 /* XCTestCase+WMFBundleConvenience.h */, + B0E808761C0D155A0065EBC0 /* XCTestCase+WMFBundleConvenience.m */, + ); + name = "Fixture Utilities"; + sourceTree = ""; + }; + BCD41E081B11D30B00231BB1 /* Persistence Utilities */ = { + isa = PBXGroup; + children = ( + B0E8087B1C0D15760065EBC0 /* WMFRandomFileUtilities.h */, + B0E8087C1C0D15760065EBC0 /* WMFRandomFileUtilities.m */, + B0E808801C0D15A20065EBC0 /* MWKDataStore+TemporaryDataStore.h */, + B0E808811C0D15A20065EBC0 /* MWKDataStore+TemporaryDataStore.m */, + ); + name = "Persistence Utilities"; + sourceTree = ""; + }; + BCD67E7D1C1F12F2005179E1 /* Tests */ = { + isa = PBXGroup; + children = ( + 67F73384273C1FA200D7D713 /* Notifications Tests */, + 671DF9D625F2B57B0011799E /* Article Description Tests */, + 67ED8EAF24F99F1900DD5D39 /* Significant Events Tests */, + 679FA102242E64FC0095F3C6 /* Article Tests */, + 67E8B0AA226A6DB000537BC9 /* TalkPageTests */, + 8330533223F0388E00123141 /* DataStoreTests.swift */, + D8BDA8C01E71C0760031F4BF /* WMFBlocksKitTests.m */, + B39427411E71F79700D3146D /* NSDictionaryBlocksKitTest.m */, + B39427421E71F79700D3146D /* NSSetBlocksKitTest.m */, + D864D68B1DA3EA3800B86934 /* NumberFormatterExtrasTests.swift */, + D858A7DE1DA6A04A009C3DEB /* WMFDateCalculationTests.m */, + 83BBBE5523F56F9400AD0994 /* LocaleTests.swift */, + D84649AC1D4514F7009DB4A0 /* WMFTaskGroupTests.m */, + B0E809041C0D18A00065EBC0 /* CircularBitwiseRotationTests.m */, + B0E809081C0D18BC0065EBC0 /* NSString+WMFHTMLParsingTests.m */, + B0E8090A1C0D18D90065EBC0 /* NSString+FormattedAttributedStringTests.m */, + B0E8090C1C0D18E70065EBC0 /* WMFImageURLParsingTests.m */, + D8EC64021D007B1F00C286EE /* WMFLinkParsingTests.m */, + B0E8090E1C0D18F30065EBC0 /* WMFMathTests.m */, + B0E809101C0D18FD0065EBC0 /* WMFSubstringUtilsTests.m */, + B0E809121C0D19090065EBC0 /* WMFDateFormatterTests.m */, + B0E8092E1C0D1A0B0065EBC0 /* NSURL+WMFExtrasTests.m */, + B01662B11D1B8A40006F4544 /* NSURL+WMFQueryParametersTests.m */, + B0E809341C0D1A2F0065EBC0 /* WMFGeometryTests.m */, + B0E809361C0D1A420065EBC0 /* MWKLanguageLinkControllerTests.m */, + B0E8093A1C0D1A590065EBC0 /* WMFSafeAssignTests.m */, + B0E809401C0D1A820065EBC0 /* NSURL+WMFLinkParsingTests.m */, + B0E809541C0D1B510065EBC0 /* CLLocation+WMFBearingTests.m */, + B0E8095D1C0D1B930065EBC0 /* WMFMTLModelSerializationTests.m */, + B0E8095F1C0D1BA30065EBC0 /* WMFSearchFetcherTests.m */, + BC52D0F61C207D3300F625A9 /* TWNStringsTests.m */, + BC90DE781C57C5AD007E0E81 /* WMFWelcomeLanguageViewControllerVisualTests.m */, + B0D530EA1CE151C10078BAED /* CodeFileLocationTests.m */, + 19A172FA6AE61E76FCEF4259 /* NSUserActivity+WMFExtensionsTest.m */, + B02F96651DFA11DC007DA007 /* WMFArticleListTableViewCell+DynamicTypeFontTests.swift */, + D8800CB01E2FF5B70035D2DB /* QuadKeyTests.swift */, + B389CFCA1E6784B600483C06 /* WMFDatabaseHousekeeperTests.swift */, + B37B6FE81EEAFE11007CBB12 /* EventLoggingServiceTests.swift */, + 830ECAD51FBDE77F0080B1EF /* ReadingListsTests.swift */, + B0C06B9E218240CA00E481CC /* Collection+AsyncMapTests.swift */, + D8396D1A22CF7052005625D8 /* WMFArticleTests.swift */, + 8386BDE623857F87007EE89D /* URLParsingAndRoutingTests.swift */, + A452F9FA24081A7200D8ED09 /* LocationManagerTests.swift */, + 00D280FB247F019C006BEE23 /* Date+ExtensionTests.swift */, + FFBA8C1827D824D8009E9B65 /* URL+ExtensionTests.swift */, + 00A8F58526BDD5E700175B8E /* WidgetSampleContentTests.swift */, + ); + name = Tests; + path = WikipediaUnitTests/Code; + sourceTree = SOURCE_ROOT; + }; + BCD67E7E1C1F1433005179E1 /* Saved Pages */ = { + isa = PBXGroup; + children = ( + BCD67E821C1F14C5005179E1 /* Event Logging */, + BCD67E801C1F1471005179E1 /* Fetchers */, + ); + name = "Saved Pages"; + path = Wikipedia/Code; + sourceTree = SOURCE_ROOT; + }; + BCD67E801C1F1471005179E1 /* Fetchers */ = { + isa = PBXGroup; + children = ( + B01E54AE206479CC00374FEE /* ProgressContainer.swift */, + B068EDDF206B183500C827D1 /* Progress+ProgressUI.swift */, + 67DAEDA223CE24DA003AA208 /* SavedArticlesFetcher.swift */, + ); + name = Fetchers; + sourceTree = ""; + }; + BCD67E821C1F14C5005179E1 /* Event Logging */ = { + isa = PBXGroup; + children = ( + B0E805851C0CE2C60065EBC0 /* SavedPagesFunnel.h */, + B0E805861C0CE2C60065EBC0 /* SavedPagesFunnel.m */, + ); + name = "Event Logging"; + sourceTree = ""; + }; + BCD67E871C1F17D4005179E1 /* Face Detection */ = { + isa = PBXGroup; + children = ( + B0E804FA1C0CE0DC0065EBC0 /* UIImage+WMFImageProcessing.h */, + B0E804FB1C0CE0DC0065EBC0 /* UIImage+WMFImageProcessing.m */, + B0E807751C0CEEB00065EBC0 /* UIImageView+WMFImageFetchingInternal.h */, + B0E807761C0CEEB00065EBC0 /* UIImageView+WMFImageFetchingInternal.m */, + B0E805021C0CE0DC0065EBC0 /* UIImageView+WMFContentOffset.h */, + B0E805031C0CE0DC0065EBC0 /* UIImageView+WMFContentOffset.m */, + B0E804FC1C0CE0DC0065EBC0 /* UIImage+WMFNormalization.h */, + B0E804FD1C0CE0DC0065EBC0 /* UIImage+WMFNormalization.m */, + ); + name = "Face Detection"; + sourceTree = ""; + }; + D499142C181D51DE00E6073C = { + isa = PBXGroup; + children = ( + D499143E181D51DE00E6073C /* Wikipedia */, + D801C8501EB8E131001FA294 /* Localizations */, + BC8309941A7BF935003FC5C7 /* Tests */, + 0E8380661D64989F0076EDE4 /* ContinueReadingWidget */, + D844D96D1D6CB2600042D692 /* WMF Framework */, + D87021611EBA63EF000D02D6 /* Update Localizations */, + D8479FAC1F222FE90025FD7A /* Wikipedia Stickers */, + 83ACAA9F24E6DC8E003B3035 /* Command Line Tools */, + B0606EAF20AA6FF0006EC6B9 /* WikipediaUITests */, + 00021DE624D48EFD00476F97 /* Widgets */, + 676C864526D40AEB00A704C1 /* NotificationServiceExtension */, + D4991437181D51DE00E6073C /* Frameworks */, + D4991436181D51DE00E6073C /* Products */, + 67D6C0152405A3E2005709B1 /* Recovered References */, + ); + sourceTree = ""; + usesTabs = 0; + }; + D4991436181D51DE00E6073C /* Products */ = { + isa = PBXGroup; + children = ( + D4991435181D51DE00E6073C /* Wikipedia.app */, + BC4273521A7C736800068882 /* WikipediaUnitTests.xctest */, + 0E8380631D64989F0076EDE4 /* ContinueReadingWidget.appex */, + D844D96C1D6CB2600042D692 /* WMF.framework */, + D8CE26AF1E698E2400DAE2E0 /* Experimental.app */, + D8A42C2B1E815A9C00D8E281 /* User Testing.app */, + D8EC3FB41E9BDA35006712EB /* Staging.app */, + D87021601EBA63EE000D02D6 /* localization */, + D8479FAB1F222FE80025FD7A /* Wikipedia Stickers.appex */, + B0606EAE20AA6FF0006EC6B9 /* WikipediaUITests.xctest */, + 00021DE124D48EFD00476F97 /* WidgetsExtension.appex */, + D8B589A521CD05070027083A /* languages */, + 676C864426D40AEA00A704C1 /* NotificationServiceExtension.appex */, + ); + name = Products; + sourceTree = ""; + }; + D4991437181D51DE00E6073C /* Frameworks */ = { + isa = PBXGroup; + children = ( + 83B019D524F6C31B0014B5EF /* WidgetKit.framework */, + 83AF34F624D3341D000046D6 /* BackgroundTasks.framework */, + B09BE6A01FB3DA45007F52E3 /* WebKit.framework */, + D8CD975F1E83F68400ECCA9D /* SafariServices.framework */, + D8D553611DF1B63200B90177 /* QuartzCore.framework */, + D8DC16FC1D6F709300D6D9FB /* CoreImage.framework */, + 041EFC361996A1F800B2CB28 /* MapKit.framework */, + 040E5C4E184566F4007AFE6F /* CoreData.framework */, + D4991438181D51DE00E6073C /* Foundation.framework */, + D499143A181D51DE00E6073C /* CoreGraphics.framework */, + D499143C181D51DE00E6073C /* UIKit.framework */, + 0E8380641D64989F0076EDE4 /* NotificationCenter.framework */, + D895D0841D9C1EB8005418C1 /* UserNotifications.framework */, + D895D0861D9C1EB8005418C1 /* UserNotificationsUI.framework */, + 00021DE224D48EFD00476F97 /* WidgetKit.framework */, + 00021DE424D48EFD00476F97 /* SwiftUI.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + D499143E181D51DE00E6073C /* Wikipedia */ = { + isa = PBXGroup; + children = ( + D4991453181D51DE00E6073C /* Images.xcassets */, + BC7C3A371C0FF94A0057F023 /* Code */, + BCF012321AD2FA38008D3675 /* assets */, + D8BA1F1F1DF1E19700502877 /* Resources */, + ); + path = Wikipedia; + sourceTree = ""; + }; + D4B0ADFF19365F4600F0AC90 /* Analytics */ = { + isa = PBXGroup; + children = ( + 0E7AAEEA1C21F4160046B5B6 /* WIkimedia Event Logging */, + ); + name = Analytics; + sourceTree = ""; + }; + D801C8501EB8E131001FA294 /* Localizations */ = { + isa = PBXGroup; + children = ( + D801C8511EB8E131001FA294 /* InfoPlist.strings */, + D801C8531EB8E131001FA294 /* Localizable.strings */, + D801C8611EB8E131001FA294 /* Localizable.stringsdict */, + ); + name = Localizations; + path = "Wikipedia/iOS Native Localizations"; + sourceTree = ""; + }; + D810C34C1DF1C36F003427DA /* Third Party */ = { + isa = PBXGroup; + children = ( + 004280F725E6E841004945B3 /* NYTPhotoViewer */, + D84DAA081EEEF527008E4B18 /* SWStepSlider */, + D8E27B9C1F82AFE600F9D2B3 /* RMessage */, + D8C4D3CF1FD5D9250089CEC2 /* TUSafariActivity */, + ); + name = "Third Party"; + sourceTree = ""; + }; + D818D37F1ED725240076110D /* View Controllers */ = { + isa = PBXGroup; + children = ( + 83023C1720E6581A00EC7592 /* Transitions */, + 41FCAA3521C844CB001D8411 /* ReadingListEntryCollectionViewController.swift */, + 41CCB67321CC1F9700206B47 /* SavedArticlesCollectionViewController.swift */, + D8B1668A1FD97FE000097D8B /* WMFViewController.h */, + D8B1668B1FD97FE000097D8B /* WMFViewController.m */, + D837CC36231FE9CC00BA6130 /* ThemeableViewController.swift */, + D8B166841FD97A0500097D8B /* ViewController.swift */, + 8386BDEE2386CAAB007EE89D /* ViewController+URLHandling.swift */, + 83F1096823D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift */, + 8368BB8324129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift */, + 83F1096D23D0E787003F3E9E /* RandomArticleViewController.swift */, + 8386BDFA2386D754007EE89D /* SinglePageWebViewController.swift */, + D818D3801ED7254D0076110D /* ColumnarCollectionViewController.swift */, + 8367A27E20D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift */, + 8382F8D220D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift */, + 830ECACE1FBDD8C00080B1EF /* ReadingListsViewController.swift */, + 7A35CB861FD82B6300AAF3B7 /* ReadingListDetailViewController.swift */, + D818D3851ED750E40076110D /* ArticleCollectionViewController.swift */, + 830D71C21F703C980080078B /* ArticleURLListViewController.swift */, + 830D71CE1F704DD40080078B /* ArticleFetchedResultsViewController.swift */, + 7A29A5C71F6C405900E8F42B /* HistoryViewController.swift */, + D818D38A1ED765470076110D /* ArticleLocationCollectionViewController.swift */, + 83927D7A1F70570400051890 /* DisambiguationPagesViewController.swift */, + 83927D801F705B7B00051890 /* SearchResultsViewController.swift */, + 83FBE96E1F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift */, + 83FBE9741F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift */, + 83836ECA1F615E5B007D1A05 /* ShareViewController.swift */, + 83836ECB1F615E5B007D1A05 /* ShareViewController.xib */, + 83E52BB21F681F940045E776 /* ShareAFactViewController.swift */, + 83E52BB31F681F940045E776 /* ShareAFactViewController.xib */, + 7A29A5CD1F6C49C600E8F42B /* CollectionViewUpdater.swift */, + 7A77B7801F9FCB2500753FF5 /* ArticlePeekPreviewViewController.swift */, + 7AE1D3381FCD10B900393471 /* SavedViewController.swift */, + B0845E0F20618DA400CDD98E /* SavedProgressViewController.swift */, + B0845E1B2061B44A00CDD98E /* SavedProgressViewController.storyboard */, + 7A2432BC1FCF401900FB4BA5 /* CreateReadingListViewController.swift */, + 7A2432BD1FCF401900FB4BA5 /* CreateReadingListViewController.xib */, + 7ADEAB011FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift */, + 7A13A8982028BB3600F28254 /* ReadingListsAlertController.swift */, + 7ABAD6B220338CFA006A364C /* ReadingListDetailUnderBarViewController.swift */, + 7ABAD6B320338CFB006A364C /* ReadingListDetailUnderBarViewController.xib */, + 7A741DC8207FB9CB00CBAAE2 /* SearchBarExtendedViewController.swift */, + 7A741DC9207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib */, + 83E3E7242440F1FE00AA2E9A /* LoadingAnimationViewController.swift */, + 83E3E7292440F24300AA2E9A /* LoadingAnimationViewController.xib */, + 83DE45B72449C09B00671878 /* SplashScreenViewController.swift */, + D8940CEA1DB56C0500E17F9E /* In The News */, + ); + name = "View Controllers"; + sourceTree = ""; + }; + D82E951A1F101D7D007BD960 /* Extensions */ = { + isa = PBXGroup; + children = ( + D82E95691F156F77007BD960 /* UIView+SubviewEnumeration.swift */, + 7A20AE072057F39C005FB5DF /* UIView+Identifier.swift */, + 83987ACF20E4FB2C00C92C60 /* UISearchBar+Theme.swift */, + 834CC34A21075B7600F62818 /* UITabBar+Theme.swift */, + ); + name = Extensions; + sourceTree = ""; + }; + D83F5C051F0E5111006130FF /* Views */ = { + isa = PBXGroup; + children = ( + 7AE1D3321FCD057200393471 /* Saved.storyboard */, + 83E52BBE1F682E3E0045E776 /* LicenseView.swift */, + BA7683C41F30D86A00A487AA /* ProminentSwitch.swift */, + 7A998AC01FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift */, + D858C7B5210B91CD0039E0C9 /* PassthroughView.swift */, + 7A9A611721124CF500403154 /* CreateNewReadingListButtonView.xib */, + 7A9A611D21124D0E00403154 /* CreateNewReadingListButtonView.swift */, + D82E951A1F101D7D007BD960 /* Extensions */, + D83F5C061F0E511C006130FF /* Collection View Cells */, + D83F5C0B1F0E51E4006130FF /* Accessory Views */, + 6798331922C174ED0073CE6F /* LinkOnlyTextView.swift */, + FF2090EE2500247100849774 /* ThreeLineHeaderView.swift */, + ); + name = Views; + sourceTree = ""; + }; + D83F5C061F0E511C006130FF /* Collection View Cells */ = { + isa = PBXGroup; + children = ( + 7AFEB3F41FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift */, + 7A0161B31FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift */, + 7A0161DF1FE8B4CA00AEDC3D /* TagCollectionViewCell.swift */, + ); + name = "Collection View Cells"; + sourceTree = ""; + }; + D83F5C071F0E514B006130FF /* Side Scrolling */ = { + isa = PBXGroup; + children = ( + B0B423521EF47DCD00D3DC4C /* SideScrollingCollectionViewCell.swift */, + D89845281ECC8A1700849DA4 /* NewsCollectionViewCell.swift */, + B0B423591EF4845500D3DC4C /* OnThisDayCollectionViewCell.swift */, + B04034371F059243001B837B /* OnThisDayExploreCollectionViewCell.swift */, + ); + name = "Side Scrolling"; + path = ../Wikipedia/Code; + sourceTree = ""; + }; + D83F5C081F0E5177006130FF /* Article */ = { + isa = PBXGroup; + children = ( + D87721F21EC0CDB60005E634 /* ArticleCollectionViewCell.swift */, + 83D5EC861F755E1F003DE6F2 /* SwipeableCell.swift */, + D8831D381EC33F1D008CA89A /* ArticleFullWidthImageCollectionViewCell.swift */, + D813FD9F1EC3419400FA4690 /* ArticleRightAlignedImageCollectionViewCell.swift */, + D8497F6D1EE09FA100100CBD /* RankedArticleCollectionViewCell.swift */, + ); + name = Article; + path = ../Wikipedia/Code; + sourceTree = ""; + }; + D83F5C091F0E5187006130FF /* Extensions */ = { + isa = PBXGroup; + children = ( + B04034491F0722B3001B837B /* NewsCollectionViewCell+WMFFeedContentDisplaying.swift */, + B040343D1F0592F7001B837B /* OnThisDayCollectionViewCell+WMFFeedContentDisplaying.swift */, + B040343E1F0592F7001B837B /* OnThisDayExploreCollectionViewCell+WMFFeedContentDisplaying.swift */, + D84C363F1F3245A200895FA1 /* ArticleCollectionViewCell+Themeable.swift */, + 83B87EC61F713BC200F342F1 /* ArticleCollectionViewCell+ListDisplay.swift */, + D8733C8C1ECA14DD0011E379 /* ArticleCollectionViewCell+WMFFeedContentDisplaying.swift */, + ); + name = Extensions; + path = ../Wikipedia/Code; + sourceTree = ""; + }; + D83F5C0B1F0E51E4006130FF /* Accessory Views */ = { + isa = PBXGroup; + children = ( + 832289DA1F7291BA0081A5FB /* SizeThatFitsReusableView.swift */, + ); + name = "Accessory Views"; + sourceTree = ""; + }; + D844D96D1D6CB2600042D692 /* WMF Framework */ = { + isa = PBXGroup; + children = ( + 67E9A11B25536B6F00C5ED31 /* ABTestsController.swift */, + 0E28C4611D74AA55000C5919 /* Article Content */, + D87F1D3C1EC0ACC400575CF8 /* AsyncOperation.swift */, + 7A976F81207BAC2100F7EFE6 /* Authentication */, + D826C51621766F1A0012F940 /* BackgroundFetcher.swift */, + 0E28C4651D74AB54000C5919 /* Base Model Classes */, + 0E728D151DAEE0C60074EB4B /* Base Networking */, + D88E0E1C1EBB5A97005B8E9E /* Bundle.swift */, + 83A6D44225100BEE00F9F909 /* Bundle+IsAppExtension.swift */, + D89845211ECB3F6C00849DA4 /* CGRect+Layout.swift */, + B0E8046B1C0CE0B40065EBC0 /* CIContext+WMFImageProcessing.h */, + B0E8046C1C0CE0B40065EBC0 /* CIContext+WMFImageProcessing.m */, + B0E8046D1C0CE0B40065EBC0 /* CIDetector+WMFFaceDetection.h */, + B0E8046E1C0CE0B40065EBC0 /* CIDetector+WMFFaceDetection.m */, + D826C51421766E570012F940 /* Collection+AsyncMap.swift */, + D87647471F1F9C2500D02CA4 /* CommonStrings.swift */, + 0E6A6F501D9E9A1300189C80 /* Content Sources */, + D895D09A1D9C2983005418C1 /* Controllers */, + D8CC94D62178972C007293E7 /* Core Data */, + D82C3A98213451100073EEAC /* DeviceInfo.swift */, + 7A45AB7F20AB2A4C006A92F5 /* Dictionary+Equality.swift */, + 0E728D141DAEE0790074EB4B /* Event Logging */, + B3632E7E1EE5F97C007A2464 /* Event Logging New */, + 70416BF62565D6C000D5BC33 /* Event Platform */, + D81A28BD231E8F4C001CC77D /* ExtensionViewController.swift */, + 83E9A2111F56FE5E006EB091 /* FakeProgressController.swift */, + 836BF56B2869EC2600B98321 /* FeatureFlags.swift */, + 0E728D271DAEE3200074EB4B /* Feed */, + D80ED2581EE178A800CE8C50 /* Gradient.swift */, + D8733C931ECA16940011E379 /* HasText.swift */, + 0E728D161DAEE0F20074EB4B /* HTML Parsing */, + 0E28C4631D74AA81000C5919 /* Image Caching */, + 0E728D191DAEE2390074EB4B /* Images */, + D844D96F1D6CB2600042D692 /* Info.plist */, + 0463639518A844380049EE4F /* Keychain */, + D8733C8A1ECA10930011E379 /* LabelGroupAccessibilityElement.swift */, + 0E28C4601D74A68D000C5919 /* Language */, + 0E28C45F1D74A673000C5919 /* Legacy Models */, + D8F36F021EEEBA130087D4DD /* Licenses.swift */, + D8726D421EBA052900A107D0 /* Localization.swift */, + 8336F1422119BD6E000CDE02 /* MediaWikiAcceptLanguageMapping.json */, + 0E03E27E1B82EF7600C1FBD7 /* Nearby */, + 8330531E23EF051900123141 /* NSArray+WMFMapping.h */, + 8330531D23EF051900123141 /* NSArray+WMFMapping.m */, + B0E804971C0CE0B40065EBC0 /* NSLocale+WMFExtras.swift */, + D8650B7920350FEE0044DFFA /* NSString+SHA256.h */, + D8650B7A20350FEE0044DFFA /* NSString+SHA256.m */, + B0E804A41C0CE0B40065EBC0 /* NSString+WMFExtras.h */, + B0E804A51C0CE0B40065EBC0 /* NSString+WMFExtras.m */, + 0E5DC8611C6D716100C39A6F /* NSUserActivity+WMFExtensions.h */, + 0E5DC8621C6D716100C39A6F /* NSUserActivity+WMFExtensions.m */, + D8494ADC1D6C85C500337433 /* NSUserDefaults+WMFExtensions.swift */, + B0B423491EF204C200D3DC4C /* On This Day */, + D8E892242176124F00587F61 /* PeriodicWorker.swift */, + D8FB46A11E26BC6600F2620F /* QuadKey.swift */, + 0E52FD681DA40FA300587426 /* Random */, + 0E728D171DAEE1200074EB4B /* Recent Searches */, + 0E2691041B86BBD1009B8605 /* Related */, + 7A255D3D215162AF0081A068 /* Remote Notifications */, + 0E568D471C78273B00E68FC1 /* SavedPageSpotlightManager.swift */, + 0E728D131DAEE06F0074EB4B /* Search */, + 0E728D181DAEE2210074EB4B /* Sharing */, + 6798036F24F99A5300D765AA /* Significant Events Endpoint */, + D837B5A51F06AA8C00DCB9CD /* Theme.swift */, + D82E954B1F15397D007BD960 /* ThemeableTextField.swift */, + D8D5513E1DF1A33D00B90177 /* Third Party */, + D8FA19081E1BDA72009675C3 /* UI */, + D8AAF6B71FE93DE9005760E6 /* UIScrollView+Limits.swift */, + B019FECE2029347200BDE9C9 /* UIStackView+SubviewVerification.swift */, + 8383446B1F62EBD000BD5A37 /* UIView+Constraints.swift */, + D8733C911ECA16580011E379 /* UIView+SemanticContent.swift */, + 0E28C4641D74AAB0000C5919 /* User DataStore */, + D8FA18A91E1BD84D009675C3 /* Utilities */, + 006694FA265D9ECD00E23AE4 /* Widget */, + B0E8072D1C0CED810065EBC0 /* WikipediaAppUtils.h */, + B0E8072E1C0CED810065EBC0 /* WikipediaAppUtils.m */, + D84C36511F33866C00895FA1 /* WMF Framework.xcassets */, + D844D96E1D6CB2600042D692 /* WMF.h */, + D81E5F891E5F949B00E1A80C /* WMFAssertions.h */, + 8330532123EF05D000123141 /* WMFBlocksKit.swift */, + B0E805CF1C0CE5420065EBC0 /* WMFFaceDetectionCache.h */, + B0E805D01C0CE5420065EBC0 /* WMFFaceDetectionCache.m */, + 83A8E33F21A431F100B3FF82 /* WMFLegacySerializer.h */, + 83A8E34021A431F100B3FF82 /* WMFLegacySerializer.m */, + D801C93A1EB9404A001FA294 /* WMFLocalization.h */, + D801C93B1EB9404A001FA294 /* WMFLocalization.m */, + D8F36EFC1EEAEAF20087D4DD /* WMFQuoteMacros.h */, + D880652E218C732800BF7B91 /* WorkerController.swift */, + 6761AEEA270613B400E47BAD /* SharedContainerCache.swift */, + 6779618C29245BF300C2A65F /* PageIDToURLFetcher.swift */, + 6779618E29246BC900C2A65F /* NSUserActivity+Extensions.swift */, + 67B7E7792988768C00708A81 /* MediaWikiApiErrors.swift */, + ); + path = "WMF Framework"; + sourceTree = ""; + }; + D8479FAC1F222FE90025FD7A /* Wikipedia Stickers */ = { + isa = PBXGroup; + children = ( + D8479FAD1F222FE90025FD7A /* Stickers.xcassets */, + D8479FAF1F222FE90025FD7A /* Info.plist */, + ); + path = "Wikipedia Stickers"; + sourceTree = ""; + }; + D8494AFC1D6C8D6700337433 /* System */ = { + isa = PBXGroup; + children = ( + B0E804A21C0CE0B40065EBC0 /* NSProcessInfo+WMFOperatingSystemVersionChecks.h */, + B0E804A31C0CE0B40065EBC0 /* NSProcessInfo+WMFOperatingSystemVersionChecks.m */, + ); + name = System; + sourceTree = ""; + }; + D8494B011D6C8DBA00337433 /* Parsing */ = { + isa = PBXGroup; + children = ( + B0E804AE1C0CE0B40065EBC0 /* NSURL+WMFExtras.h */, + B0E804AF1C0CE0B40065EBC0 /* NSURL+WMFExtras.m */, + D80A79271F31E63C00EC06AB /* NSCharacterSet+WMFLinkParsing.h */, + D80A79281F31E63C00EC06AB /* NSCharacterSet+WMFLinkParsing.m */, + D8494AD81D6C85C500337433 /* NSURL+WMFLinkParsing.h */, + D8494AD91D6C85C500337433 /* NSURL+WMFLinkParsing.m */, + 831937E823E1CEAC006A9FF3 /* CharacterSet+LinkParsing.swift */, + D837B5B11F0D68B800DCB9CD /* URL+LinkParsing.swift */, + 831937E623E1CE80006A9FF3 /* String+LinkParsing.swift */, + 832A7A5F23EAE03200D0A750 /* String+JavaScript.swift */, + 832B2B8323D9F9420087EB5F /* NSRegularExpression+Utilities.swift */, + D8494ADA1D6C85C500337433 /* NSURLComponents+WMFLinkParsing.h */, + D8494ADB1D6C85C500337433 /* NSURLComponents+WMFLinkParsing.m */, + B0E805761C0CE24B0065EBC0 /* WMFDeprecationMacros.h */, + ); + name = Parsing; + sourceTree = ""; + }; + D84BF6281DB96D4700E0C85E /* Notifications */ = { + isa = PBXGroup; + children = ( + 00F5AECF27C6C80C006390A8 /* PushNotificationsSettingsViewController.swift */, + ); + name = Notifications; + sourceTree = ""; + }; + D84C36061F323FEE00895FA1 /* Collection View Cells */ = { + isa = PBXGroup; + children = ( + D8733C951ECA48490011E379 /* CollectionViewCell.swift */, + 83EE476F20D01A9A00A21F34 /* ExploreCardCollectionViewCell.swift */, + D8497F631EE09BE600100CBD /* CircledRankView.swift */, + 7ADB2A081FD1E8C400B84818 /* BatchEditSelectView.swift */, + BA7FF0B31F6188C70054CF02 /* CollectionViewCellActionsView.swift */, + BA7FF0B51F618F5A0054CF02 /* CollectionViewEditController.swift */, + D83F5C081F0E5177006130FF /* Article */, + D83C5ABA1F2281A90066C892 /* AnnouncementCollectionViewCell.swift */, + D83F5C091F0E5187006130FF /* Extensions */, + D83F5C071F0E514B006130FF /* Side Scrolling */, + ); + name = "Collection View Cells"; + sourceTree = ""; + }; + D84C36461F32485E00895FA1 /* Model */ = { + isa = PBXGroup; + children = ( + D84C36471F32486500895FA1 /* Extensions */, + ); + name = Model; + sourceTree = ""; + }; + D84C36471F32486500895FA1 /* Extensions */ = { + isa = PBXGroup; + children = ( + 8382F8D820D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift */, + ); + name = Extensions; + sourceTree = ""; + }; + D84DAA081EEEF527008E4B18 /* SWStepSlider */ = { + isa = PBXGroup; + children = ( + D84DAA091EEEF527008E4B18 /* SWStepSlider.swift */, + ); + name = SWStepSlider; + path = "Wikipedia/Third Party Code/SWStepSlider"; + sourceTree = SOURCE_ROOT; + }; + D84F2BF61D2FEE4B00963D42 /* Random */ = { + isa = PBXGroup; + children = ( + D84F2C011D30162700963D42 /* WMFFirstRandomViewController.h */, + D84F2C021D30162700963D42 /* WMFFirstRandomViewController.m */, + D84F2BF71D2FEE6300963D42 /* WMFRandomDiceButton.h */, + D84F2BF91D2FEE6300963D42 /* WMFRandomDiceButton.m */, + D84F2BF81D2FEE6300963D42 /* WMFRandomDiceButton.html */, + D84F2BFA1D2FEE6300963D42 /* WMFRandomDiceButtonRoll.js */, + ); + name = Random; + sourceTree = ""; + }; + D86706C01D3D4AD3003AB2FC /* Columnar Collection View Layout */ = { + isa = PBXGroup; + children = ( + 83A1561320DBE08C0052487B /* ColumnarCollectionViewLayout.swift */, + 8380753A20DC7D04000D222C /* ColumnarCollectionViewLayoutMetrics.swift */, + 8380753620DC7481000D222C /* ColumnarCollectionViewLayoutInfo.swift */, + 8380753820DC7684000D222C /* ColumarCollectionViewLayoutSection.swift */, + ); + name = "Columnar Collection View Layout"; + sourceTree = ""; + }; + D87021611EBA63EF000D02D6 /* Update Localizations */ = { + isa = PBXGroup; + children = ( + 83A72BBE24E70BB200732493 /* localization.swift */, + 83ACAAA624E6E655003B3035 /* main.swift */, + ); + name = "Update Localizations"; + sourceTree = ""; + }; + D87233FC1E1FF05800751E83 /* Places */ = { + isa = PBXGroup; + children = ( + D82972861E3A49980061550A /* ArticlePopoverViewController.swift */, + D82972871E3A49980061550A /* ArticlePopoverViewController.xib */, + D87233FF1E1FF0A500751E83 /* PlacesViewController.swift */, + D88C70171EE595E90022A26A /* MapView.swift */, + D82117FB1EE58C080076C040 /* MapAnnotation.swift */, + D88FCAE01E4B776600505A9F /* MapUtilities.swift */, + D88FCADE1E4B74D300505A9F /* WikidataFetcher+Places.swift */, + D808DCEC1E438C0C00A3E89C /* PlaceSearch.swift */, + D808DCEA1E438BE300A3E89C /* PlaceSearchSuggestionController.swift */, + D80ED25A1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift */, + D80ED25B1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib */, + D808DCEE1E438C5100A3E89C /* MKCoordinateRegion+Dimensions.swift */, + D82972821E3950100061550A /* ArticlePlace.swift */, + D8A6BAEE1E4C9C0700A981C8 /* ArticlePlaceView.swift */, + D8A6BAEC1E4C9BF400A981C8 /* UserLocationAnnotationView.swift */, + D8CB32AC1E79D8A0008A0966 /* RoundedCornerView.swift */, + D87234031E1FF18100751E83 /* Places.storyboard */, + B3F21D0E1EB0EBB4000ED0BB /* PlaceSearchService.swift */, + ); + name = Places; + sourceTree = ""; + }; + D8940CEA1DB56C0500E17F9E /* In The News */ = { + isa = PBXGroup; + children = ( + D8940CED1DB56C8A00E17F9E /* NewsViewController.swift */, + D8533ED31ECF581600E44F86 /* NewsCollectionViewHeader.swift */, + D8533ED41ECF581600E44F86 /* NewsCollectionViewHeader.xib */, + ); + name = "In The News"; + sourceTree = ""; + }; + D895D09A1D9C2983005418C1 /* Controllers */ = { + isa = PBXGroup; + children = ( + D8F1BF241D9C2AFB00036E71 /* WMFNotificationsController.h */, + D8F1BF251D9C2AFB00036E71 /* WMFNotificationsController.m */, + 6761AEF227065DE400E47BAD /* WMFNotificationsController+Extensions.swift */, + D8E78FA31FB4C8250094B968 /* ReadingListsController.swift */, + 830177F91FBF3E490005681C /* ReadingListsAPIController.swift */, + D82CA32E2020E87D005C2D5C /* ReadingListsOperation.swift */, + D82CA3322020E8D8005C2D5C /* ReadingListsSyncOperation.swift */, + 83F1095623D07E3B003F3E9E /* APIURLComponentsBuilder.swift */, + D8543230218879D000E895B5 /* Configuration.swift */, + 8321FCC923871D8F0079F3C7 /* Router.swift */, + D8181FA42188DC1400FDEC59 /* String+Domains.swift */, + 8386BDF02386D3E1007EE89D /* RequestError.swift */, + D8E78FA51FB4C8740094B968 /* Session.swift */, + D826C51A217741C50012F940 /* ReachabilityNotifier.swift */, + D8635AE7216E2BFC001A7C00 /* HTTPCookieStorage+Migration.swift */, + 7AE5248C21383D9C00CDC817 /* WikidataFetcher.swift */, + 83E9C45A2419193C006BDBC2 /* WikipediaSiteInfo.swift */, + D85F56A1219C45C900AF3E13 /* URLComponents+Extensions.swift */, + 835A042C223AD63000D4D758 /* ArticleSummaryController.swift */, + 670AF19A26C1CA38005F76D0 /* EchoSubscriptionFetcher.swift */, + ); + name = Controllers; + path = ../Wikipedia/Code; + sourceTree = ""; + }; + D8BA1F1F1DF1E19700502877 /* Resources */ = { + isa = PBXGroup; + children = ( + D8D92B481DF22E1700B95311 /* Third Party */, + B0EFCD711EBF13B2008F36E5 /* LibrariesUsed.plist */, + ); + path = Resources; + sourceTree = ""; + }; + D8C4D3CF1FD5D9250089CEC2 /* TUSafariActivity */ = { + isa = PBXGroup; + children = ( + D8C4D3D01FD5D9250089CEC2 /* TUSafariActivity.bundle */, + D8C4D3D11FD5D9250089CEC2 /* TUSafariActivity.h */, + D8C4D3D21FD5D9250089CEC2 /* TUSafariActivity.m */, + ); + name = TUSafariActivity; + path = "Wikipedia/Third Party Code/TUSafariActivity"; + sourceTree = SOURCE_ROOT; + }; + D8CC94D62178972C007293E7 /* Core Data */ = { + isa = PBXGroup; + children = ( + 678C7C2823BE6766001AC4D5 /* Cache */, + D8CC94D721789763007293E7 /* Wikipedia */, + D8CC94D9217897FB007293E7 /* NSManagedObject+Extensions.swift */, + ); + name = "Core Data"; + sourceTree = ""; + }; + D8CC94D721789763007293E7 /* Wikipedia */ = { + isa = PBXGroup; + children = ( + D844480D1DDA33D900425630 /* Wikipedia.xcdatamodeld */, + D84448241DDB632100425630 /* WMFArticle+CoreDataClass.h */, + D84448251DDB632100425630 /* WMFArticle+CoreDataClass.m */, + D84448261DDB632100425630 /* WMFArticle+CoreDataProperties.h */, + D84448271DDB632100425630 /* WMFArticle+CoreDataProperties.m */, + D84448201DDB60FF00425630 /* WMFArticle+Extensions.h */, + D84448211DDB60FF00425630 /* WMFArticle+Extensions.m */, + D881B1121E32874500D33F62 /* WMFArticle+QuadKey.swift */, + D813FDA41EC34B2600FA4690 /* WMFArticle+Extensions.swift */, + 831C15C52099EB3A001B04BF /* WMFArticle+Errors.swift */, + 7A4170D8229EFC2A00251582 /* PageNamespace.swift */, + 83ACAA9D24E6D94C003B3035 /* MWKSearchResult+PageNamespace.swift */, + D8987E001E325C7900E75DA6 /* WMFKeyValue+CoreDataClass.h */, + D8987E011E325C7A00E75DA6 /* WMFKeyValue+CoreDataClass.m */, + D8987E021E325C7A00E75DA6 /* WMFKeyValue+CoreDataProperties.h */, + D8987E031E325C7A00E75DA6 /* WMFKeyValue+CoreDataProperties.m */, + D85BD2441F8F9D6900D0D478 /* NSManagedObjectContext+WMFKeyValue.h */, + D85BD2451F8F9D6900D0D478 /* NSManagedObjectContext+WMFKeyValue.m */, + 8387CE8724C8C70A00439D93 /* WMFSecureUnarchiveFromDataTransformer.swift */, + D84448541DDCE49D00425630 /* WMFContentGroup+CoreDataClass.h */, + D84448551DDCE49D00425630 /* WMFContentGroup+CoreDataClass.m */, + D84448561DDCE49D00425630 /* WMFContentGroup+CoreDataProperties.h */, + D84448571DDCE49D00425630 /* WMFContentGroup+CoreDataProperties.m */, + D844485C1DDCE4E500425630 /* WMFContentGroup+Extensions.h */, + D844485D1DDCE4E500425630 /* WMFContentGroup+Extensions.m */, + 7A07A46720AA3F5100F7B2BB /* WMFContentGroup+EventLogging.swift */, + 8380754420DE627D000D222C /* WMFContentGroup+Display.swift */, + 83222DAD1F8E554800338BE5 /* WMFContent+CoreDataProperties.h */, + 83222DB01F8E554800338BE5 /* WMFContent+CoreDataProperties.m */, + 83222DAE1F8E554800338BE5 /* WMFContent+CoreDataClass.h */, + 83222DAF1F8E554800338BE5 /* WMFContent+CoreDataClass.m */, + D8619B9E1FBB10240045C8BC /* ReadingList+CoreDataClass.swift */, + D8619B9F1FBB10240045C8BC /* ReadingList+CoreDataProperties.swift */, + D8EBD1B71FBB13EE00AA7DA9 /* ReadingList+JSON.swift */, + D8619BA01FBB10240045C8BC /* ReadingListEntry+CoreDataClass.swift */, + D8619BA11FBB10240045C8BC /* ReadingListEntry+CoreDataProperties.swift */, + D8EBD1BB1FBB177D00AA7DA9 /* ReadingListEntry+JSON.swift */, + ); + name = Wikipedia; + sourceTree = ""; + }; + D8D5513E1DF1A33D00B90177 /* Third Party */ = { + isa = PBXGroup; + children = ( + 0042803E25E6E395004945B3 /* FLAnimatedImage */, + 0042804525E6E395004945B3 /* Mantle */, + D8D551411DF1A33D00B90177 /* EXTScope.h */, + D8D551421DF1A33D00B90177 /* EXTScope.m */, + D8D551431DF1A33D00B90177 /* metamacros.h */, + ); + name = "Third Party"; + path = "WMF Framework/Third Party"; + sourceTree = SOURCE_ROOT; + }; + D8D92B481DF22E1700B95311 /* Third Party */ = { + isa = PBXGroup; + children = ( + D8D92B491DF22E1700B95311 /* NotificationBackgroundError.png */, + D8D92B4A1DF22E1700B95311 /* NotificationBackgroundError@2x.png */, + D8D92B4B1DF22E1700B95311 /* NotificationBackgroundErrorIcon.png */, + D8D92B4C1DF22E1700B95311 /* NotificationBackgroundErrorIcon@2x.png */, + D8D92B4D1DF22E1700B95311 /* NotificationBackgroundMessage.png */, + D8D92B4E1DF22E1700B95311 /* NotificationBackgroundMessage@2x.png */, + D8D92B4F1DF22E1700B95311 /* NotificationBackgroundSuccess.png */, + D8D92B501DF22E1700B95311 /* NotificationBackgroundSuccess@2x.png */, + D8D92B511DF22E1700B95311 /* NotificationBackgroundSuccessIcon.png */, + D8D92B521DF22E1700B95311 /* NotificationBackgroundSuccessIcon@2x.png */, + D8D92B531DF22E1700B95311 /* NotificationBackgroundWarning.png */, + D8D92B541DF22E1700B95311 /* NotificationBackgroundWarning@2x.png */, + D8D92B551DF22E1700B95311 /* NotificationBackgroundWarningIcon.png */, + D8D92B561DF22E1700B95311 /* NotificationBackgroundWarningIcon@2x.png */, + D8D92B571DF22E1700B95311 /* NotificationButtonBackground.png */, + D8D92B581DF22E1700B95311 /* NotificationButtonBackground@2x.png */, + D8D92B591DF22E1700B95311 /* TSMessagesDefaultDesign.json */, + ); + path = "Third Party"; + sourceTree = ""; + }; + D8E27B9C1F82AFE600F9D2B3 /* RMessage */ = { + isa = PBXGroup; + children = ( + D8E27BAC1F82B4FF00F9D2B3 /* RMessageDefaultDesign.json */, + D8E27B9D1F82AFE600F9D2B3 /* RMessage.m */, + D8E27B9E1F82AFE600F9D2B3 /* RMessageView.m */, + D8E27B9F1F82AFE600F9D2B3 /* RMessage.h */, + D8E27BA01F82AFE600F9D2B3 /* RMessageView.h */, + D8E27BB21F82B5CB00F9D2B3 /* RMessageView.xib */, + ); + name = RMessage; + path = "Wikipedia/Third Party Code/RMessage"; + sourceTree = SOURCE_ROOT; + }; + D8FA18A91E1BD84D009675C3 /* Utilities */ = { + isa = PBXGroup; + children = ( + 7A630F6B217A3FB100FC93FC /* Array+Chunked.swift */, + 7A3EE1532267DC3800709CF6 /* Array+SafeIndex.swift */, + D837B5A71F06E5C600DCB9CD /* DateFormatter+WikipediaLanguage.swift */, + BC45D5661C32E6E8007C72F3 /* Grand Central Dispatch */, + B0E804891C0CE0B40065EBC0 /* NSBundle+WMFInfoUtils.h */, + B0E8048A1C0CE0B40065EBC0 /* NSBundle+WMFInfoUtils.m */, + BCD31FFC1C6EB3EE00317D08 /* NSCalendar+WMFCommonCalendars.h */, + BCD31FFD1C6EB3EE00317D08 /* NSCalendar+WMFCommonCalendars.m */, + 0E78419A1C7CF5CE004F9D36 /* NSDate+WMFRelativeDate.h */, + 0E78419B1C7CF5CE004F9D36 /* NSDate+WMFRelativeDate.m */, + B0E8048F1C0CE0B40065EBC0 /* NSDateFormatter+WMFExtensions.h */, + B0E804901C0CE0B40065EBC0 /* NSDateFormatter+WMFExtensions.m */, + D84F92401DC161DA00114C2F /* NSDictionary+WMFPageViewsSortedByDate.h */, + D84F92411DC161DA00114C2F /* NSDictionary+WMFPageViewsSortedByDate.m */, + BCAF23141C6CF74C005F2D8D /* NSDictionary+WMFRequiredValueForKey.h */, + BCAF23151C6CF74C005F2D8D /* NSDictionary+WMFRequiredValueForKey.m */, + B0E804911C0CE0B40065EBC0 /* NSError+WMFExtensions.h */, + B0E804921C0CE0B40065EBC0 /* NSError+WMFExtensions.m */, + 7A5A2776206D288C004CC837 /* NSFileManager+DirectorySize.swift */, + D81082F61D0983AE0070DAC3 /* NSFileManager+WMFExtendedFileAttributes.h */, + D81082F71D0983AE0070DAC3 /* NSFileManager+WMFExtendedFileAttributes.m */, + D8EEA0F81D6E21A400D88143 /* NSFileManager+WMFGroup.h */, + D8EEA0F91D6E21A400D88143 /* NSFileManager+WMFGroup.m */, + B0E804951C0CE0B40065EBC0 /* NSIndexSet+BKReduce.h */, + B0E804961C0CE0B40065EBC0 /* NSIndexSet+BKReduce.m */, + 830177FB1FBF3EF70005681C /* NSManagedObjectContext+WMFUtilities.swift */, + 83D05188246EA70D00DA92C6 /* NSMutableAttributedString+Mutations.swift */, + D864D68D1DA3EDF900B86934 /* NSNumberFormatter+WMFExtras.swift */, + BC45D5711C32EADD007C72F3 /* NSObject */, + 8392E8671F557FC0007E2EE2 /* NSTextAttachment+WMFExtras.swift */, + B01662AE1D1B8997006F4544 /* NSURL+WMFQueryParameters.h */, + B01662AF1D1B8997006F4544 /* NSURL+WMFQueryParameters.m */, + D8494B011D6C8DBA00337433 /* Parsing */, + 7A0F2588217221D10028871B /* RepeatingTimer.swift */, + B01CFC601E71069000B3546A /* String?+WMFExtras.swift */, + D8494AFC1D6C8D6700337433 /* System */, + B085536B2399E368002100F8 /* UIAccessibility+Grouping.swift */, + B0E804261C0CDF510065EBC0 /* WMFGeometry.c */, + B0E804271C0CDF510065EBC0 /* WMFGeometry.h */, + B0E807361C0CED810065EBC0 /* WMFLogging.h */, + B0E807371C0CED810065EBC0 /* WMFMath.h */, + B0E807381C0CED810065EBC0 /* WMFMath.m */, + B0E803961C0CDB150065EBC0 /* WMFNumberOfExtractCharacters.h */, + B0E807391C0CED810065EBC0 /* WMFOutParamUtils.h */, + B0E8073A1C0CED810065EBC0 /* WMFRangeUtils.h */, + D8A76D811D6F2B2E00E5A798 /* WMFTaskGroup.h */, + D8A76D821D6F2B2E00E5A798 /* WMFTaskGroup.m */, + 67F73382273C163700D7D713 /* TimeInterval+Extensions.swift */, + 0015712B27D92F6B00F1EB26 /* RetryBlockTask.swift */, + 67FF9C6A28076ADA000963D1 /* NSError+Utilities.swift */, + 67A7CA7428665CEF008D4BF6 /* HTTPStatusCode.swift */, + ); + name = Utilities; + sourceTree = ""; + }; + D8FA19081E1BDA72009675C3 /* UI */ = { + isa = PBXGroup; + children = ( + 67E2E4932504E1C70070F12D /* TimelineView.swift */, + 7A00D16C208FB61200A9C7BA /* BatchEditToolbarViewController.xib */, + 7A79CCE6200C29A10099B01F /* BatchEditToolbarViewController.swift */, + 0E28C4871D751ED6000C5919 /* WMFFeedContentDisplaying.h */, + 0E6A6F511D9E9AB300189C80 /* WMFContentGroup+WMFFeedContentDisplaying.h */, + 0E6A6F521D9E9AB300189C80 /* WMFContentGroup+WMFFeedContentDisplaying.m */, + B0E8043D1C0CDF850065EBC0 /* WMFGradientView.h */, + B0E8043E1C0CDF850065EBC0 /* WMFGradientView.m */, + 7A2432EC1FCF469100FB4BA5 /* SetupView.swift */, + B0016CBE2136105900FA1096 /* SetupButton.swift */, + D8497F681EE09D0200100CBD /* SizeThatFitsView.swift */, + B0D1B4591DDD02BB004FCAE6 /* WMFDynamicTypeExtentions.swift */, + 7A3159CE206458B000143119 /* ReadingListAlertType.swift */, + D84C36061F323FEE00895FA1 /* Collection View Cells */, + D876C2851E5CDE6500FCA00A /* AlignedImageButton.swift */, + D87721FC1EC0DCA30005E634 /* SaveButton.swift */, + D803F8951DC53B0C00656F20 /* GroupedAccessibilityView.swift */, + B0E8051D1C0CE0DC0065EBC0 /* UIView+WMFDefaultNib.h */, + D8D270391D75ED5000D093A8 /* WMFArticlePreviewViewController.swift */, + D8D2703A1D75ED5000D093A8 /* WMFArticlePreviewViewController.xib */, + B0E8051E1C0CE0DC0065EBC0 /* UIView+WMFDefaultNib.m */, + B0E805001C0CE0DC0065EBC0 /* UIImage+WMFStyle.h */, + B0E805011C0CE0DC0065EBC0 /* UIImage+WMFStyle.m */, + B0E805D51C0CE59B0065EBC0 /* UIImageView+WMFImageFetching.h */, + B0E805D61C0CE59B0065EBC0 /* UIImageView+WMFImageFetching.m */, + B0E804F21C0CE0DC0065EBC0 /* UIColor+WMFStyle.h */, + B0E804F31C0CE0DC0065EBC0 /* UIColor+WMFStyle.m */, + D8D0CC361DF6F8C30031EDD9 /* UIFont+WMFDynamicType.swift */, + D8FA39B71D7F556400D89889 /* WMFSparklineView.swift */, + 831835301FD1AC490025DD3D /* NavigationBar.swift */, + D8CE9AFD1FDEB14E00AE7D49 /* NavigationBarHider.swift */, + D86706C01D3D4AD3003AB2FC /* Columnar Collection View Layout */, + BCD67E871C1F17D4005179E1 /* Face Detection */, + 6734F051227B634900BDDB94 /* ActionButton.swift */, + ); + name = UI; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + D844D9691D6CB2600042D692 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0042807D25E6E395004945B3 /* MTLValueTransformer.h in Headers */, + 0042808525E6E395004945B3 /* MTLModel.h in Headers */, + 0042807C25E6E395004945B3 /* NSObject+MTLComparisonAdditions.h in Headers */, + 0042806F25E6E395004945B3 /* FLAnimatedImageView.h in Headers */, + 0042807E25E6E395004945B3 /* MTLTransformerErrorHandling.h in Headers */, + 0042808725E6E395004945B3 /* NSArray+MTLManipulationAdditions.h in Headers */, + 0042808325E6E395004945B3 /* MTLJSONAdapter.h in Headers */, + 0042808025E6E395004945B3 /* NSDictionary+MTLManipulationAdditions.h in Headers */, + 0042808225E6E395004945B3 /* NSValueTransformer+MTLPredefinedTransformerAdditions.h in Headers */, + 0042808625E6E395004945B3 /* NSDictionary+MTLMappingAdditions.h in Headers */, + 0042807F25E6E395004945B3 /* NSValueTransformer+MTLInversionAdditions.h in Headers */, + 0042808425E6E395004945B3 /* MTLModel+NSCoding.h in Headers */, + 0042808125E6E395004945B3 /* Mantle.h in Headers */, + 0042806E25E6E395004945B3 /* FLAnimatedImage.h in Headers */, + B0B423471EF1FEE000D3DC4C /* WMFFeedOnThisDayEvent.h in Headers */, + D8FA19011E1BDA5B009675C3 /* UIView+WMFDefaultNib.h in Headers */, + D844485E1DDCE4E500425630 /* WMFContentGroup+Extensions.h in Headers */, + D844D97F1D6CB32D0042D692 /* MWKSiteDataObject.h in Headers */, + D84C36431F3245DF00895FA1 /* WMFContentGroup+WMFFeedContentDisplaying.h in Headers */, + D8AC391D1D6F2324007E3C14 /* UIScreen+WMFImageWidth.h in Headers */, + D8FA18CD1E1BD891009675C3 /* WMFOutParamUtils.h in Headers */, + 0E728D3B1DAEEADB0074EB4B /* WMFLocationSearchResults.h in Headers */, + D89D44021D74D3ED00F7862E /* MWKSearchResult.h in Headers */, + 7A29A5D31F6C4B9C00E8F42B /* WMFChange.h in Headers */, + D81E5F8A1E5F949B00E1A80C /* WMFAssertions.h in Headers */, + D8FA18D61E1BD899009675C3 /* NSURL+WMFExtras.h in Headers */, + 8338AF8D21F7B33E000C4055 /* WMFLegacyFetcher.h in Headers */, + 83A8E34121A431F100B3FF82 /* WMFLegacySerializer.h in Headers */, + D844D9DE1D6CBC0E0042D692 /* WMFImageURLParsing.h in Headers */, + D8FA18C11E1BD891009675C3 /* NSURL+WMFQueryParameters.h in Headers */, + D8FA18D01E1BD891009675C3 /* WMFMath.h in Headers */, + 0E728D281DAEE8FF0074EB4B /* WMFContentSource.h in Headers */, + D8FA18EB1E1BD8C0009675C3 /* WMFGCDHelpers.h in Headers */, + 0E728D4A1DAEEE910074EB4B /* CLLocation+WMFBearing.h in Headers */, + D84B22531DAFD1E1007C44AA /* CIDetector+WMFFaceDetection.h in Headers */, + D844D9B61D6CB7940042D692 /* MWKRecentSearchEntry.h in Headers */, + 83CCB289209CA4E600D31565 /* NSRegularExpression+HTML.h in Headers */, + D8FA18B51E1BD891009675C3 /* NSDictionary+WMFRequiredValueForKey.h in Headers */, + D8FA18DA1E1BD899009675C3 /* NSURLComponents+WMFLinkParsing.h in Headers */, + D84B224D1DAFD0F7007C44AA /* WMFNotificationsController.h in Headers */, + D84C363A1F3241F100895FA1 /* WMFGradientView.h in Headers */, + 0E87683F1DDE012300B8CACD /* WMFAnnouncement.h in Headers */, + D8FA18E91E1BD8B9009675C3 /* WMFComparison.h in Headers */, + 83222DB21F8E554800338BE5 /* WMFContent+CoreDataClass.h in Headers */, + D801C93C1EB9404A001FA294 /* WMFLocalization.h in Headers */, + 0E728D4C1DAEEE910074EB4B /* CLLocation+WMFComparison.h in Headers */, + B0B4234C1EF2055200D3DC4C /* WMFOnThisDayEventsFetcher.h in Headers */, + D844D9F01D6CC01C0042D692 /* MWKLicense.h in Headers */, + D881B1101E326ABA00D33F62 /* WMFKeyValue+CoreDataProperties.h in Headers */, + 0E728D1A1DAEE2B50074EB4B /* WMFFeedDayResponse.h in Headers */, + D8F36EFD1EEAEAF20087D4DD /* WMFQuoteMacros.h in Headers */, + D844D9971D6CB5CA0042D692 /* WikipediaAppUtils.h in Headers */, + 8330532023EF051900123141 /* NSArray+WMFMapping.h in Headers */, + 0E87683A1DDE00D600B8CACD /* WMFAnnouncementsFetcher.h in Headers */, + D80ACD281EA0DD0000DC3F20 /* FLAnimatedImage+SafeForSwift.h in Headers */, + D84C36421F3245DF00895FA1 /* WMFFeedContentDisplaying.h in Headers */, + 0E728D3C1DAEEADB0074EB4B /* WMFLocationSearchFetcher.h in Headers */, + 0E728D1C1DAEE2B50074EB4B /* WMFFeedTopReadResponse.h in Headers */, + D8FA18D81E1BD899009675C3 /* NSURL+WMFLinkParsing.h in Headers */, + B0B423501EF32D2700D3DC4C /* WMFOnThisDayContentSource.h in Headers */, + D84448581DDCE49D00425630 /* WMFContentGroup+CoreDataClass.h in Headers */, + D8FA18BB1E1BD891009675C3 /* NSFileManager+WMFGroup.h in Headers */, + D8FA18B31E1BD891009675C3 /* NSCalendar+WMFCommonCalendars.h in Headers */, + D844D9BE1D6CB7B30042D692 /* MWKDataStore.h in Headers */, + D8FA18ED1E1BDA2F009675C3 /* UIImage+WMFImageProcessing.h in Headers */, + D84448221DDB60FF00425630 /* WMFArticle+Extensions.h in Headers */, + D84B22511DAFD1E1007C44AA /* CIContext+WMFImageProcessing.h in Headers */, + 0E728D2F1DAEE8FF0074EB4B /* WMFNearbyContentSource.h in Headers */, + 0E728D2B1DAEE8FF0074EB4B /* WMFRelatedPagesContentSource.h in Headers */, + D844DA061D6CC4C90042D692 /* MWKLanguageFilter.h in Headers */, + D844485A1DDCE49D00425630 /* WMFContentGroup+CoreDataProperties.h in Headers */, + D8FA18D21E1BD891009675C3 /* WMFTaskGroup.h in Headers */, + D8FA19061E1BDA66009675C3 /* UIColor+WMFStyle.h in Headers */, + D844D9701D6CB2600042D692 /* WMF.h in Headers */, + D8FA18C61E1BD891009675C3 /* NSIndexSet+BKReduce.h in Headers */, + D8FA18CB1E1BD891009675C3 /* WMFLogging.h in Headers */, + D8FA18EE1E1BDA2F009675C3 /* UIImageView+WMFImageFetchingInternal.h in Headers */, + D844D9A61D6CB7230042D692 /* MWKList.h in Headers */, + D83FA6B51D74CDE0008CAB00 /* EventLoggingFunnel.h in Headers */, + D8FA18EF1E1BDA2F009675C3 /* UIImageView+WMFContentOffset.h in Headers */, + D8FA18B71E1BD891009675C3 /* NSDateFormatter+WMFExtensions.h in Headers */, + D844D9DD1D6CBBFE0042D692 /* NSString+WMFHTMLParsing.h in Headers */, + D8650B7B20350FEE0044DFFA /* NSString+SHA256.h in Headers */, + D844D97E1D6CB2A10042D692 /* MWKDataObject.h in Headers */, + D85BD2461F8F9D6900D0D478 /* NSManagedObjectContext+WMFKeyValue.h in Headers */, + D844D9EE1D6CBFFD0042D692 /* MWKDataStoreList.h in Headers */, + 0E728D351DAEE8FF0074EB4B /* WMFRandomContentSource.h in Headers */, + 0E728D4B1DAEEE910074EB4B /* NSString+WMFDistance.h in Headers */, + D8FA19051E1BDA66009675C3 /* UIImageView+WMFImageFetching.h in Headers */, + D881B1111E326ABE00D33F62 /* WMFKeyValue+CoreDataClass.h in Headers */, + D8FA18DC1E1BD89C009675C3 /* WMFDeprecationMacros.h in Headers */, + D8FA18CA1E1BD891009675C3 /* WMFRangeUtils.h in Headers */, + 0E728D3A1DAEEADB0074EB4B /* MWKLocationSearchResult.h in Headers */, + 0E728D201DAEE2B50074EB4B /* WMFFeedImage.h in Headers */, + D8FA19031E1BDA66009675C3 /* UIImage+WMFStyle.h in Headers */, + D844D9B21D6CB7770042D692 /* MWKSavedPageList.h in Headers */, + D8FA18F01E1BDA2F009675C3 /* UIImage+WMFNormalization.h in Headers */, + 7A5AB82C22940D8500B91C9C /* WMFHTMLElement.h in Headers */, + D80A79291F31E63C00EC06AB /* NSCharacterSet+WMFLinkParsing.h in Headers */, + D8FA18E81E1BD8B2009675C3 /* NSProcessInfo+WMFOperatingSystemVersionChecks.h in Headers */, + D8FA18AB1E1BD86E009675C3 /* EXTScope.h in Headers */, + D844D9EF1D6CC0010042D692 /* MWKList+Subclass.h in Headers */, + D8FA18D41E1BD891009675C3 /* NSError+WMFExtensions.h in Headers */, + D844DA021D6CC3C40042D692 /* MWKLanguageLink.h in Headers */, + D8FA18C81E1BD891009675C3 /* WMFGeometry.h in Headers */, + 0E728D331DAEE8FF0074EB4B /* WMFFeedContentSource.h in Headers */, + 0E728D311DAEE8FF0074EB4B /* WMFContinueReadingContentSource.h in Headers */, + 0E728D241DAEE2B50074EB4B /* WMFFeedContentFetcher.h in Headers */, + D8FA18AA1E1BD867009675C3 /* metamacros.h in Headers */, + D8FA18B91E1BD891009675C3 /* NSDate+WMFRelativeDate.h in Headers */, + D844D9C31D6CB7D40042D692 /* MWKImageInfo.h in Headers */, + D84448281DDB632100425630 /* WMFArticle+CoreDataClass.h in Headers */, + 0E728D1E1DAEE2B50074EB4B /* WMFFeedArticlePreview.h in Headers */, + 8387CE8F24C99C2600439D93 /* WMFMTLModel.h in Headers */, + D81930DA1E9F97B200554B19 /* WMFExploreFeedContentController.h in Headers */, + D844DA041D6CC4C90042D692 /* MWKLanguageLinkController.h in Headers */, + D844D99C1D6CB6170042D692 /* NSString+WMFExtras.h in Headers */, + 834400B120B3368E005F087D /* NSCharacterSet+WMFExtras.h in Headers */, + D844DA031D6CC4C90042D692 /* MWKLanguageLinkController_Private.h in Headers */, + D81EFDE31D775B180035F2EB /* NSUserActivity+WMFExtensions.h in Headers */, + D8FA18EC1E1BD8C4009675C3 /* WMFBlockDefinitions.h in Headers */, + 0E728D221DAEE2B50074EB4B /* WMFFeedNewsStory.h in Headers */, + D844482A1DDB632100425630 /* WMFArticle+CoreDataProperties.h in Headers */, + 6779D45D24008654002840CA /* MWKImageInfoFetcher.h in Headers */, + 0E8768361DDE002C00B8CACD /* WMFAnnouncementsContentSource.h in Headers */, + D84B224F1DAFD14D007C44AA /* WMFFaceDetectionCache.h in Headers */, + 83222DB11F8E554800338BE5 /* WMFContent+CoreDataProperties.h in Headers */, + D844D9B71D6CB7940042D692 /* MWKRecentSearchList.h in Headers */, + 0042807625E6E395004945B3 /* MTLMetamacros.h in Headers */, + 0042807325E6E395004945B3 /* NSDictionary+MTLJSONKeyPath.h in Headers */, + 0042808C25E6E395004945B3 /* NSError+MTLModelException.h in Headers */, + 0042807425E6E395004945B3 /* MTLEXTRuntimeExtensions.h in Headers */, + 0042807525E6E395004945B3 /* MTLEXTScope.h in Headers */, + 0042808B25E6E395004945B3 /* MTLReflection.h in Headers */, + 0042807725E6E395004945B3 /* MTLEXTKeyPathCoding.h in Headers */, + D8FA18BD1E1BD891009675C3 /* NSFileManager+WMFExtendedFileAttributes.h in Headers */, + D8FA18B01E1BD891009675C3 /* NSBundle+WMFInfoUtils.h in Headers */, + 83A933462514C491006EB48A /* WMFCrossProcessCoreDataSynchronizer.h in Headers */, + D8FA18AD1E1BD891009675C3 /* NSDictionary+WMFPageViewsSortedByDate.h in Headers */, + 83DF1D1424F53878007E08D8 /* WMFPreferredLanguageInfoProvider.h in Headers */, + D8FA18EA1E1BD8B9009675C3 /* WMFHashing.h in Headers */, + D8FA18B21E1BD891009675C3 /* WMFNumberOfExtractCharacters.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 00021DE024D48EFD00476F97 /* WidgetsExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 00021DFD24D48EFE00476F97 /* Build configuration list for PBXNativeTarget "WidgetsExtension" */; + buildPhases = ( + 00021DDD24D48EFD00476F97 /* Sources */, + 00021DDE24D48EFD00476F97 /* Frameworks */, + 00021DDF24D48EFD00476F97 /* Resources */, + 00AB75C124D4E8FB0041056A /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 00AB75C024D4E8FB0041056A /* PBXTargetDependency */, + ); + name = WidgetsExtension; + productName = WidgetsExtension; + productReference = 00021DE124D48EFD00476F97 /* WidgetsExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; + 0E8380621D64989F0076EDE4 /* ContinueReadingWidget */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0E8380761D64989F0076EDE4 /* Build configuration list for PBXNativeTarget "ContinueReadingWidget" */; + buildPhases = ( + 0E83805F1D64989F0076EDE4 /* Sources */, + 0E8380601D64989F0076EDE4 /* Frameworks */, + 0E8380611D64989F0076EDE4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ContinueReadingWidget; + productName = ContinueReadingWidget; + productReference = 0E8380631D64989F0076EDE4 /* ContinueReadingWidget.appex */; + productType = "com.apple.product-type.app-extension"; + }; + 676C864326D40AEA00A704C1 /* NotificationServiceExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 676C865726D40AEB00A704C1 /* Build configuration list for PBXNativeTarget "NotificationServiceExtension" */; + buildPhases = ( + 676C864026D40AEA00A704C1 /* Sources */, + 676C864126D40AEA00A704C1 /* Frameworks */, + 676C864226D40AEA00A704C1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 676C868426D4545300A704C1 /* PBXTargetDependency */, + ); + name = NotificationServiceExtension; + productName = NotificationServiceExtension; + productReference = 676C864426D40AEA00A704C1 /* NotificationServiceExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; + B0606EAD20AA6FF0006EC6B9 /* WikipediaUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B0606EC320AA6FF0006EC6B9 /* Build configuration list for PBXNativeTarget "WikipediaUITests" */; + buildPhases = ( + B0606EAA20AA6FF0006EC6B9 /* Sources */, + B0606EAB20AA6FF0006EC6B9 /* Frameworks */, + B0606EAC20AA6FF0006EC6B9 /* Resources */, + B018501720BC847A00A508F1 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + B018501A20BC85E400A508F1 /* PBXTargetDependency */, + B0606EB420AA6FF0006EC6B9 /* PBXTargetDependency */, + ); + name = WikipediaUITests; + productName = WikipediaUITests; + productReference = B0606EAE20AA6FF0006EC6B9 /* WikipediaUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + BC4273511A7C736800068882 /* WikipediaUnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BC42735A1A7C736800068882 /* Build configuration list for PBXNativeTarget "WikipediaUnitTests" */; + buildPhases = ( + BC42734E1A7C736800068882 /* Sources */, + BC42734F1A7C736800068882 /* Frameworks */, + D8B724B41ECF09F700D10836 /* Copy Test Fixtures */, + BC4273501A7C736800068882 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + D8FA19131E1BDFD7009675C3 /* PBXTargetDependency */, + BCBDE0AE1AA76F19006BD29A /* PBXTargetDependency */, + ); + name = WikipediaUnitTests; + productName = WikipediaUnitTests; + productReference = BC4273521A7C736800068882 /* WikipediaUnitTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + D4991434181D51DE00E6073C /* Wikipedia */ = { + isa = PBXNativeTarget; + buildConfigurationList = D499146A181D51DF00E6073C /* Build configuration list for PBXNativeTarget "Wikipedia" */; + buildPhases = ( + 00966EAA284033ED006C6E27 /* SwiftLint */, + D4991431181D51DE00E6073C /* Sources */, + D4991432181D51DE00E6073C /* Frameworks */, + D4991433181D51DE00E6073C /* Resources */, + 0E8380771D64989F0076EDE4 /* Embed Foundation Extensions */, + D84581101D67572D00B09640 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + D88DBBBB1D8B322400134A50 /* PBXTargetDependency */, + 0E83806F1D64989F0076EDE4 /* PBXTargetDependency */, + D8479FB11F222FE90025FD7A /* PBXTargetDependency */, + 00021DED24D48EFE00476F97 /* PBXTargetDependency */, + 676C864A26D40AEB00A704C1 /* PBXTargetDependency */, + ); + name = Wikipedia; + packageProductDependencies = ( + 46EB334729E1D204001D5EAF /* Shared */, + ); + productName = "Wikipedia-iOS"; + productReference = D4991435181D51DE00E6073C /* Wikipedia.app */; + productType = "com.apple.product-type.application"; + }; + D844D96B1D6CB2600042D692 /* WMF */ = { + isa = PBXNativeTarget; + buildConfigurationList = D844D97B1D6CB2600042D692 /* Build configuration list for PBXNativeTarget "WMF" */; + buildPhases = ( + D87021721EBA69B7000D02D6 /* Update Localizations */, + D844D9691D6CB2600042D692 /* Headers */, + D844D9671D6CB2600042D692 /* Sources */, + D844D9681D6CB2600042D692 /* Frameworks */, + D8FA19151E1BE05B009675C3 /* Resources */, + D8D987FD1E1C468F00789CA0 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = WMF; + packageProductDependencies = ( + 67A770C7251BFE0400F94EF9 /* CocoaLumberjackSwift */, + 83FFFFB929AEC094005506A0 /* Components */, + ); + productName = WMF; + productReference = D844D96C1D6CB2600042D692 /* WMF.framework */; + productType = "com.apple.product-type.framework"; + }; + D8479FAA1F222FE80025FD7A /* Wikipedia Stickers */ = { + isa = PBXNativeTarget; + buildConfigurationList = D8479FC01F222FE90025FD7A /* Build configuration list for PBXNativeTarget "Wikipedia Stickers" */; + buildPhases = ( + D8479FA91F222FE80025FD7A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Wikipedia Stickers"; + productName = "Wikipedia Stickers"; + productReference = D8479FAB1F222FE80025FD7A /* Wikipedia Stickers.appex */; + productType = "com.apple.product-type.app-extension.messages-sticker-pack"; + }; + D870215F1EBA63EE000D02D6 /* localization */ = { + isa = PBXNativeTarget; + buildConfigurationList = D870216F1EBA63EF000D02D6 /* Build configuration list for PBXNativeTarget "localization" */; + buildPhases = ( + D870215C1EBA63EE000D02D6 /* Sources */, + D870215D1EBA63EE000D02D6 /* Frameworks */, + D870215E1EBA63EE000D02D6 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = localization; + productName = localization; + productReference = D87021601EBA63EE000D02D6 /* localization */; + productType = "com.apple.product-type.tool"; + }; + D8A42A4D1E815A9C00D8E281 /* User Testing */ = { + isa = PBXNativeTarget; + buildConfigurationList = D8A42C211E815A9C00D8E281 /* Build configuration list for PBXNativeTarget "User Testing" */; + buildPhases = ( + 00966EAD28403EA7006C6E27 /* SwiftLint */, + D8A42A561E815A9C00D8E281 /* Sources */, + D8A42B9B1E815A9C00D8E281 /* Frameworks */, + D8A42BB21E815A9C00D8E281 /* Resources */, + D8A42C1D1E815A9C00D8E281 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + D8A42A4E1E815A9C00D8E281 /* PBXTargetDependency */, + ); + name = "User Testing"; + packageProductDependencies = ( + ); + productName = "Wikipedia-iOS"; + productReference = D8A42C2B1E815A9C00D8E281 /* User Testing.app */; + productType = "com.apple.product-type.application"; + }; + D8B589A421CD05070027083A /* languages */ = { + isa = PBXNativeTarget; + buildConfigurationList = D8B589A921CD05080027083A /* Build configuration list for PBXNativeTarget "languages" */; + buildPhases = ( + D8B589A121CD05070027083A /* Sources */, + D8B589A221CD05070027083A /* Frameworks */, + D8B589A321CD05070027083A /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = languages; + packageProductDependencies = ( + ); + productName = "codemirror-config"; + productReference = D8B589A521CD05070027083A /* languages */; + productType = "com.apple.product-type.tool"; + }; + D8CE24D71E698E2400DAE2E0 /* Experimental */ = { + isa = PBXNativeTarget; + buildConfigurationList = D8CE26A81E698E2400DAE2E0 /* Build configuration list for PBXNativeTarget "Experimental" */; + buildPhases = ( + 00966EAC28403E80006C6E27 /* SwiftLint */, + D8CE24E01E698E2400DAE2E0 /* Sources */, + D8CE261E1E698E2400DAE2E0 /* Frameworks */, + D8CE26391E698E2400DAE2E0 /* Resources */, + D8CE26A01E698E2400DAE2E0 /* Embed Foundation Extensions */, + D8CE26A41E698E2400DAE2E0 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + D8CE24D81E698E2400DAE2E0 /* PBXTargetDependency */, + D8CE24DA1E698E2400DAE2E0 /* PBXTargetDependency */, + 676C867526D4170100A704C1 /* PBXTargetDependency */, + ); + name = Experimental; + packageProductDependencies = ( + ); + productName = "Wikipedia-iOS"; + productReference = D8CE26AF1E698E2400DAE2E0 /* Experimental.app */; + productType = "com.apple.product-type.application"; + }; + D8EC3DCE1E9BDA35006712EB /* Staging */ = { + isa = PBXNativeTarget; + buildConfigurationList = D8EC3FA81E9BDA35006712EB /* Build configuration list for PBXNativeTarget "Staging" */; + buildPhases = ( + 00966EAB28403D4D006C6E27 /* SwiftLint */, + D8EC3DD71E9BDA35006712EB /* Sources */, + D8EC3F1D1E9BDA35006712EB /* Frameworks */, + D8EC3F391E9BDA35006712EB /* Resources */, + D8EC3FA01E9BDA35006712EB /* Embed Foundation Extensions */, + D8EC3FA41E9BDA35006712EB /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + D8EC3DCF1E9BDA35006712EB /* PBXTargetDependency */, + D8EC3DD11E9BDA35006712EB /* PBXTargetDependency */, + 676C867226D416FB00A704C1 /* PBXTargetDependency */, + ); + name = Staging; + packageProductDependencies = ( + ); + productName = "Wikipedia-iOS"; + productReference = D8EC3FB41E9BDA35006712EB /* Staging.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D499142D181D51DE00E6073C /* Project object */ = { + isa = PBXProject; + attributes = { + DefaultBuildSystemTypeForWorkspace = Latest; + KnownAssetTags = ( + WMFWelcome, + ); + LastSwiftMigration = 0700; + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1420; + ORGANIZATIONNAME = "Wikimedia Foundation"; + TargetAttributes = { + 00021DE024D48EFD00476F97 = { + CreatedOnToolsVersion = 12.0; + }; + 0E8380621D64989F0076EDE4 = { + CreatedOnToolsVersion = 8.0; + DevelopmentTeam = AKK7J2GV64; + LastSwiftMigration = 1020; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + com.apple.SafariKeychain = { + enabled = 1; + }; + }; + }; + 676C864326D40AEA00A704C1 = { + CreatedOnToolsVersion = 12.5.1; + }; + B0606EAD20AA6FF0006EC6B9 = { + CreatedOnToolsVersion = 9.3.1; + DevelopmentTeam = AKK7J2GV64; + LastSwiftMigration = 1020; + ProvisioningStyle = Automatic; + TestTargetID = D4991434181D51DE00E6073C; + }; + BC4273511A7C736800068882 = { + CreatedOnToolsVersion = 6.1.1; + DevelopmentTeam = AKK7J2GV64; + LastSwiftMigration = 1020; + ProvisioningStyle = Automatic; + TestTargetID = D4991434181D51DE00E6073C; + }; + D4991434181D51DE00E6073C = { + DevelopmentTeam = AKK7J2GV64; + LastSwiftMigration = 1020; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + com.apple.BackgroundModes = { + enabled = 1; + }; + com.apple.DataProtection = { + enabled = 1; + }; + com.apple.SafariKeychain = { + enabled = 1; + }; + }; + }; + D844D96B1D6CB2600042D692 = { + CreatedOnToolsVersion = 8.0; + LastSwiftMigration = 1020; + ProvisioningStyle = Manual; + }; + D8479FAA1F222FE80025FD7A = { + CreatedOnToolsVersion = 8.3.3; + DevelopmentTeam = AKK7J2GV64; + ProvisioningStyle = Automatic; + }; + D870215F1EBA63EE000D02D6 = { + CreatedOnToolsVersion = 8.3.2; + LastSwiftMigration = 0900; + }; + D8A42A4D1E815A9C00D8E281 = { + DevelopmentTeam = AKK7J2GV64; + LastSwiftMigration = 1020; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 0; + }; + com.apple.SafariKeychain = { + enabled = 0; + }; + }; + }; + D8B589A421CD05070027083A = { + CreatedOnToolsVersion = 10.1; + LastSwiftMigration = 1150; + }; + D8CE24D71E698E2400DAE2E0 = { + DevelopmentTeam = AKK7J2GV64; + }; + D8EC3DCE1E9BDA35006712EB = { + DevelopmentTeam = AKK7J2GV64; + }; + }; + }; + buildConfigurationList = D4991430181D51DE00E6073C /* Build configuration list for PBXProject "Wikipedia" */; + compatibilityVersion = "Xcode 10.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + fr, + ru, + fi, + ar, + ast, + az, + bcl, + bn, + br, + ca, + ce, + de, + dsb, + "en-gb", + eo, + es, + fa, + gl, + gu, + haw, + he, + hi, + hsb, + hu, + hy, + it, + ja, + ko, + krc, + lb, + lt, + mg, + mk, + ml, + mr, + ms, + nl, + oc, + pl, + ps, + pt, + ro, + sco, + si, + sk, + "sr-ec", + sv, + ta, + te, + tr, + uk, + vi, + yi, + "zh-hans", + "zh-hant", + da, + diq, + fo, + frp, + hrx, + id, + kn, + ne, + or, + "pt-br", + "tg-cyrl", + th, + bik, + zza, + tg, + qqq, + av, + bto, + cs, + ksh, + af, + as, + eu, + is, + ka, + km, + nb, + om, + pa, + sah, + sq, + sw, + uz, + ckb, + ur, + el, + azb, + ba, + "be-tarask", + bgn, + cnh, + fy, + "gom-latn", + "ku-latn", + mai, + mt, + nah, + olo, + sa, + sd, + tl, + vec, + wuu, + xmf, + lv, + my, + bs, + cy, + su, + "sr-ec", + jv, + "sr-EC", + tcy, + kab, + "gom-Latn", + "ku-Latn", + fil, + ga, + sl, + hr, + nqo, + kcg, + "kcg-Latn", + ); + mainGroup = D499142C181D51DE00E6073C; + packageReferences = ( + 67A770C6251BFE0400F94EF9 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */, + 67359892299ED915002EE8D1 /* XCRemoteSwiftPackageReference "wikipedia-ios-components" */, + ); + productRefGroup = D4991436181D51DE00E6073C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D4991434181D51DE00E6073C /* Wikipedia */, + D8EC3DCE1E9BDA35006712EB /* Staging */, + D8CE24D71E698E2400DAE2E0 /* Experimental */, + D8A42A4D1E815A9C00D8E281 /* User Testing */, + BC4273511A7C736800068882 /* WikipediaUnitTests */, + 0E8380621D64989F0076EDE4 /* ContinueReadingWidget */, + D8479FAA1F222FE80025FD7A /* Wikipedia Stickers */, + D844D96B1D6CB2600042D692 /* WMF */, + D870215F1EBA63EE000D02D6 /* localization */, + B0606EAD20AA6FF0006EC6B9 /* WikipediaUITests */, + D8B589A421CD05070027083A /* languages */, + 00021DE024D48EFD00476F97 /* WidgetsExtension */, + 676C864326D40AEA00A704C1 /* NotificationServiceExtension */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 00021DDF24D48EFD00476F97 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00021DEA24D48EFE00476F97 /* Assets.xcassets in Resources */, + 00550D2626B1E7DB0055C496 /* Featured Article Widget Preview Content.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0E8380611D64989F0076EDE4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0E83806C1D64989F0076EDE4 /* MainInterface.storyboard in Resources */, + D890C85D1D772ED3007132C9 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 676C864226D40AEA00A704C1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B0606EAC20AA6FF0006EC6B9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BC4273501A7C736800068882 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D4991433181D51DE00E6073C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B0E8040C1C0CDE480065EBC0 /* WMFCaptchaViewController.storyboard in Resources */, + 7A9524DC22669A8B00C55CDC /* InsertMediaSettingsButtonView.xib in Resources */, + BA4524291F32500C00439C42 /* TextSizeChangeExampleViewController.xib in Resources */, + D8E27BB41F82B5DB00F9D2B3 /* RMessageView.xib in Resources */, + B0E806961C0CEA7B0065EBC0 /* AboutViewController.plist in Resources */, + BA45241D1F324C3100439C42 /* FontSizeSliderViewController.xib in Resources */, + D8D92B5E1DF22E1700B95311 /* NotificationBackgroundMessage.png in Resources */, + 7A741DCF207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib in Resources */, + 67CEF25F234FCA8100D5CA6C /* DiffListContextCell.xib in Resources */, + D84F2BFD1D2FEE6300963D42 /* WMFRandomDiceButtonRoll.js in Resources */, + D8D92B601DF22E1700B95311 /* NotificationBackgroundSuccess.png in Resources */, + 7A9A611821124CF500403154 /* CreateNewReadingListButtonView.xib in Resources */, + 7A16C4EB212D941C00F0D5EC /* SubSettingsViewController.xib in Resources */, + 7A8422582268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib in Resources */, + 6782DBF7234537CF003FA21B /* DiffHeaderTitleView.xib in Resources */, + D8D92B5D1DF22E1700B95311 /* NotificationBackgroundErrorIcon@2x.png in Resources */, + B0866F471CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.xib in Resources */, + B0B4236D1EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib in Resources */, + 7AB20A1122FC8432006FECB4 /* PageHistoryCountsViewController.xib in Resources */, + D8D92B5C1DF22E1700B95311 /* NotificationBackgroundErrorIcon.png in Resources */, + 0E4D071D1CC5526200AE968B /* WMFLanguageCell.xib in Resources */, + 6780D5C0237AF8AE0087A5D1 /* DiffToolbarView.xib in Resources */, + D801C9361EB9344E001FA294 /* InfoPlist.strings in Resources */, + D8D92B631DF22E1700B95311 /* NotificationBackgroundSuccessIcon@2x.png in Resources */, + 83B01F8B23DB399E001185F4 /* DescriptionEditViewController.storyboard in Resources */, + 7AFA21C020110D7900E957E7 /* HintViewController.xib in Resources */, + 7A71567322697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib in Resources */, + B0524B0321484FB400D8FD8D /* DescriptionWelcome.storyboard in Resources */, + B0ED173D1E49831B008B70AD /* WMFTwoFactorPasswordViewController.storyboard in Resources */, + D80ED2601EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib in Resources */, + D8421B58203CC8420040F50B /* DebugReadingListsViewController.xib in Resources */, + B0E803771C0CD9C10065EBC0 /* TableOfContentsCell.xib in Resources */, + 7A82765E226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib in Resources */, + B0379A371D8B9D2400D973CF /* WMFReferencePopoverMessageViewController.storyboard in Resources */, + B0EFCD721EBF13B2008F36E5 /* LibrariesUsed.plist in Resources */, + 83E52BB91F681F940045E776 /* ShareAFactViewController.xib in Resources */, + 7A84224C2268BBE70074648E /* InsertMediaImageInfoView.xib in Resources */, + D8533ED91ECF581600E44F86 /* NewsCollectionViewHeader.xib in Resources */, + 0EF8634E1C19E02700006D2D /* WMFEmptyView.xib in Resources */, + D8D92B5F1DF22E1700B95311 /* NotificationBackgroundMessage@2x.png in Resources */, + 6782DC0523453D6B003FA21B /* DiffHeaderCompareItemView.xib in Resources */, + D8D92B661DF22E1700B95311 /* NotificationBackgroundWarningIcon.png in Resources */, + 7ADF498021B45CEE009EA338 /* TextFormattingPlainToolbarView.xib in Resources */, + 830C0DDA23D9C218006471C4 /* Properties.js in Resources */, + 67CEF2612350C29D00D5CA6C /* DiffListUneditedCell.xib in Resources */, + B0E8040F1C0CDE480065EBC0 /* WMFLoginViewController.storyboard in Resources */, + B083371F1DADBD7F002860D2 /* WMFWelcome.storyboard in Resources */, + 7ABE17292239BB54006BA309 /* WelcomePanelViewController.xib in Resources */, + 7ADF498C21B45E42009EA338 /* TextFormattingGroupedToolbarView.xib in Resources */, + D8D92B651DF22E1700B95311 /* NotificationBackgroundWarning@2x.png in Resources */, + 6782DBF5234537CF003FA21B /* DiffHeaderExtendedView.xib in Resources */, + 7A25367C21B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib in Resources */, + B0EFCD681EBEC2F6008F36E5 /* LibrariesUsed.storyboard in Resources */, + 67E069082238A5A6008550AC /* WMFFindAndReplaceKeyboardBar.xib in Resources */, + D8D92B611DF22E1700B95311 /* NotificationBackgroundSuccess@2x.png in Resources */, + B0524AF62144D7BE00D8FD8D /* DescriptionHelpViewController.xib in Resources */, + 7AF0265B22985CB9000E0A06 /* BeKindInputAccessoryView.xib in Resources */, + B0C6BE531E4526810033BD6E /* WMFChangePasswordViewController.storyboard in Resources */, + D8D92B5A1DF22E1700B95311 /* NotificationBackgroundError.png in Resources */, + 7ABAD6B920338CFB006A364C /* ReadingListDetailUnderBarViewController.xib in Resources */, + 0042811525E6E841004945B3 /* NYTPhotoViewer.bundle in Resources */, + B01490A11DB96BD6007F5391 /* WMFReferencePanels.storyboard in Resources */, + D87234041E1FF18100751E83 /* Places.storyboard in Resources */, + 676070A42280987C00A81F09 /* TalkPageTopicNewViewController.xib in Resources */, + 83023C0B20E51DDF00EC7592 /* SearchLanguagesBarViewController.xib in Resources */, + BA7683C31F30C56300A487AA /* ImageDimmingExampleViewController.xib in Resources */, + 7A9F061E2266432200856321 /* InsertMediaSettingsTextTableViewCell.xib in Resources */, + B0E8040B1C0CDE480065EBC0 /* WMFAccountCreationViewController.storyboard in Resources */, + D8D92B621DF22E1700B95311 /* NotificationBackgroundSuccessIcon.png in Resources */, + 7AE1FE3621B4A9790068BE9F /* TextFormattingButtonView.xib in Resources */, + B02B82761C696ECA00B19309 /* WMFSettingsTableViewCell.xib in Resources */, + D8D92B671DF22E1700B95311 /* NotificationBackgroundWarningIcon@2x.png in Resources */, + D84692E11D5E1E3F000A7058 /* TableOfContentsHeader.xib in Resources */, + B0DE92271D6E26C700EC76A7 /* WMFBarButtonItemPopoverMessageViewController.storyboard in Resources */, + 7A2432C31FCF401900FB4BA5 /* CreateReadingListViewController.xib in Resources */, + 6782DC172347EE59003FA21B /* DiffListChangeCell.xib in Resources */, + D8D92B6A1DF22E1700B95311 /* TSMessagesDefaultDesign.json in Resources */, + 7A393286236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib in Resources */, + 6780D5B4237A1F490087A5D1 /* DiffResponse.json in Resources */, + 6782DBEA23453787003FA21B /* DiffHeaderEditorView.xib in Resources */, + B0B4CF0C1CC0501B0051DF7A /* WMFArticleLanguagesSectionHeader.xib in Resources */, + 67E06919223B32DF008550AC /* FocusNavigationView.xib in Resources */, + D82972891E3A49980061550A /* ArticlePopoverViewController.xib in Resources */, + D8C4D3D31FD5D9260089CEC2 /* TUSafariActivity.bundle in Resources */, + 0E69CD5B1C8773410095918B /* Launch Screen.storyboard in Resources */, + 6771C9542509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib in Resources */, + B0E804121C0CDE480065EBC0 /* EditSaveViewController.storyboard in Resources */, + D8D92B641DF22E1700B95311 /* NotificationBackgroundWarning.png in Resources */, + 7A6CA2932289AF2200C7FD47 /* EditLinkViewController.xib in Resources */, + 6782DBE42345377B003FA21B /* DiffHeaderSummaryView.xib in Resources */, + D82E958A1F16502E007BD960 /* WMFLanguagesViewController.xib in Resources */, + 7A6F55FF21AF508B0076D184 /* TextFormatting.storyboard in Resources */, + 7A2FE5562051757E00F92F8F /* EraseSavedArticlesView.xib in Resources */, + D8D92B5B1DF22E1700B95311 /* NotificationBackgroundError@2x.png in Resources */, + 0E9B9E321CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.xib in Resources */, + 7AE1D3331FCD057200393471 /* Saved.storyboard in Resources */, + D8E27BAD1F82B54D00F9D2B3 /* RMessageDefaultDesign.json in Resources */, + B01EA07E2022856300813726 /* ScrollableEducationPanelView.xib in Resources */, + D84F2BFB1D2FEE6300963D42 /* WMFRandomDiceButton.html in Resources */, + 7A9524D022665E6400C55CDC /* InsertMediaSettingsImageView.xib in Resources */, + 7ABE171D2239B8EE006BA309 /* WelcomeContainerViewController.xib in Resources */, + B0267CE91E316A79006B6D8D /* WMFForgotPasswordViewController.storyboard in Resources */, + B0845E1C2061B44A00CDD98E /* SavedProgressViewController.storyboard in Resources */, + 6782DBF023453799003FA21B /* DiffHeaderCompareView.xib in Resources */, + 672B127822A450F000CC85A5 /* OldTalkPageHeaderView.xib in Resources */, + 00A8F58826BDD88700175B8E /* Featured Article Widget Preview Content.json in Resources */, + 671F5E0B236B8CAF00111116 /* EmptyViewController.xib in Resources */, + 83836ED11F615E5B007D1A05 /* ShareViewController.xib in Resources */, + 0EE2438F1DC988AA00066CBD /* WMFTableHeaderFooterLabelView.xib in Resources */, + D8D92B681DF22E1700B95311 /* NotificationButtonBackground.png in Resources */, + B0E804181C0CDE480065EBC0 /* WMFSettingsViewController.storyboard in Resources */, + 83E3E72A2440F24300AA2E9A /* LoadingAnimationViewController.xib in Resources */, + 0E36C2271AE0B59D00C58CFF /* Images.xcassets in Resources */, + 7AC19E4A2301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib in Resources */, + B09B03F61CE0FB7700009083 /* ReadingThemesControlsViewController.xib in Resources */, + B0F4762021F921D300C4E254 /* EditSummaryViewController.xib in Resources */, + D8D92B691DF22E1700B95311 /* NotificationButtonBackground@2x.png in Resources */, + 7AEC985E219F529000BEF62B /* DefaultEditToolbarView.xib in Resources */, + 7A707982223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8479FA91F222FE80025FD7A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D8479FAE1F222FE90025FD7A /* Stickers.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8A42BB21E815A9C00D8E281 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D8A42BB61E815A9C00D8E281 /* WMFCaptchaViewController.storyboard in Resources */, + 7A9524DF22669A8B00C55CDC /* InsertMediaSettingsButtonView.xib in Resources */, + D8E27BB71F82B5DC00F9D2B3 /* RMessageView.xib in Resources */, + D8A42BBB1E815A9C00D8E281 /* AboutViewController.plist in Resources */, + D8A42BBC1E815A9C00D8E281 /* NotificationBackgroundMessage.png in Resources */, + 7A741DD2207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib in Resources */, + D8A42BC01E815A9C00D8E281 /* WMFRandomDiceButtonRoll.js in Resources */, + 83AE1C881F34BB65004B62E0 /* ImageDimmingExampleViewController.xib in Resources */, + D8A42BC21E815A9C00D8E281 /* NotificationBackgroundSuccess.png in Resources */, + D8A42BC31E815A9C00D8E281 /* NotificationBackgroundErrorIcon@2x.png in Resources */, + 7A9A611B21124CF500403154 /* CreateNewReadingListButtonView.xib in Resources */, + 7A16C4EE212D941C00F0D5EC /* SubSettingsViewController.xib in Resources */, + 7A84225B2268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib in Resources */, + D8A42BC51E815A9C00D8E281 /* WMFArticleLanguagesSectionFooter.xib in Resources */, + 6724289023612AF100490629 /* DiffListContextCell.xib in Resources */, + B0B423701EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib in Resources */, + D8A42BC61E815A9C00D8E281 /* NotificationBackgroundErrorIcon.png in Resources */, + 7AB20A1422FC8432006FECB4 /* PageHistoryCountsViewController.xib in Resources */, + D8A42BC71E815A9C00D8E281 /* WMFLanguageCell.xib in Resources */, + D8A42BCC1E815A9C00D8E281 /* NotificationBackgroundSuccessIcon@2x.png in Resources */, + D8A42BCD1E815A9C00D8E281 /* WMFTwoFactorPasswordViewController.storyboard in Resources */, + 7AFA21C320110D7900E957E7 /* HintViewController.xib in Resources */, + 7A71567622697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib in Resources */, + 6782DBE72345377B003FA21B /* DiffHeaderSummaryView.xib in Resources */, + B0524B0621484FB400D8FD8D /* DescriptionWelcome.storyboard in Resources */, + D80ED2631EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib in Resources */, + D8A42BD11E815A9C00D8E281 /* TableOfContentsCell.xib in Resources */, + D8421B5B203CC8420040F50B /* DebugReadingListsViewController.xib in Resources */, + D8A42BD21E815A9C00D8E281 /* WMFReferencePopoverMessageViewController.storyboard in Resources */, + 7A827661226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib in Resources */, + B0EFCD751EBF13B2008F36E5 /* LibrariesUsed.plist in Resources */, + D8533EDC1ECF581600E44F86 /* NewsCollectionViewHeader.xib in Resources */, + 83E52BBC1F681F940045E776 /* ShareAFactViewController.xib in Resources */, + 7A84224F2268BBE70074648E /* InsertMediaImageInfoView.xib in Resources */, + D8A42BD71E815A9C00D8E281 /* WMFEmptyView.xib in Resources */, + D8A42BD81E815A9C00D8E281 /* NotificationBackgroundMessage@2x.png in Resources */, + 7A393289236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib in Resources */, + BA4524201F324C3100439C42 /* FontSizeSliderViewController.xib in Resources */, + 670AF18F26BDE647005F76D0 /* OldTalkPageHeaderView.xib in Resources */, + BA45242C1F32500C00439C42 /* TextSizeChangeExampleViewController.xib in Resources */, + 6782DBF323453799003FA21B /* DiffHeaderCompareView.xib in Resources */, + 7ADF498321B45CEE009EA338 /* TextFormattingPlainToolbarView.xib in Resources */, + D8A42BD91E815A9C00D8E281 /* NotificationBackgroundWarningIcon.png in Resources */, + D8A42BDA1E815A9C00D8E281 /* WMFLoginViewController.storyboard in Resources */, + 7AC19E4D2301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib in Resources */, + D8A42BDC1E815A9C00D8E281 /* WMFWelcome.storyboard in Resources */, + 6780D5C3237AF8AE0087A5D1 /* DiffToolbarView.xib in Resources */, + 7ABE172C2239BB54006BA309 /* WelcomePanelViewController.xib in Resources */, + 83B01F8E23DB399E001185F4 /* DescriptionEditViewController.storyboard in Resources */, + 67DCB7A3278F8D700041272C /* InfoPlist.strings in Resources */, + 6771C9572509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib in Resources */, + 83E3E72D2440F24300AA2E9A /* LoadingAnimationViewController.xib in Resources */, + 7ADF498F21B45E42009EA338 /* TextFormattingGroupedToolbarView.xib in Resources */, + 7A25367F21B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib in Resources */, + D8A42BE41E815A9C00D8E281 /* NotificationBackgroundWarning@2x.png in Resources */, + B0EFCD6B1EBEC2FA008F36E5 /* LibrariesUsed.storyboard in Resources */, + B0524AF92144D7BE00D8FD8D /* DescriptionHelpViewController.xib in Resources */, + D8A42BE51E815A9C00D8E281 /* NotificationBackgroundSuccess@2x.png in Resources */, + 7AF0265E22985CB9000E0A06 /* BeKindInputAccessoryView.xib in Resources */, + D8A42BE61E815A9C00D8E281 /* WMFChangePasswordViewController.storyboard in Resources */, + 7ABAD6BC20338CFB006A364C /* ReadingListDetailUnderBarViewController.xib in Resources */, + D8A42BE81E815A9C00D8E281 /* NotificationBackgroundError.png in Resources */, + D8A42BE91E815A9C00D8E281 /* WMFReferencePanels.storyboard in Resources */, + 6782DBFE234537D0003FA21B /* DiffHeaderExtendedView.xib in Resources */, + 6782DBED23453787003FA21B /* DiffHeaderEditorView.xib in Resources */, + 83023C0E20E51DDF00EC7592 /* SearchLanguagesBarViewController.xib in Resources */, + 676070A72280D72500A81F09 /* TalkPageTopicNewViewController.xib in Resources */, + D8A42BEA1E815A9C00D8E281 /* Places.storyboard in Resources */, + D8A42BEC1E815A9C00D8E281 /* WMFAccountCreationViewController.storyboard in Resources */, + 6782DC00234537D0003FA21B /* DiffHeaderTitleView.xib in Resources */, + 7A9F06212266432200856321 /* InsertMediaSettingsTextTableViewCell.xib in Resources */, + D8A42BED1E815A9C00D8E281 /* NotificationBackgroundSuccessIcon.png in Resources */, + 7AE1FE3921B4A9790068BE9F /* TextFormattingButtonView.xib in Resources */, + D8A42BEE1E815A9C00D8E281 /* WMFSettingsTableViewCell.xib in Resources */, + D8A42BF11E815A9C00D8E281 /* NotificationBackgroundWarningIcon@2x.png in Resources */, + D8A42BF41E815A9C00D8E281 /* TableOfContentsHeader.xib in Resources */, + 0042811825E6E841004945B3 /* NYTPhotoViewer.bundle in Resources */, + 6780D5B7237A1F490087A5D1 /* DiffResponse.json in Resources */, + D8A42BF71E815A9C00D8E281 /* WMFBarButtonItemPopoverMessageViewController.storyboard in Resources */, + 7A2432C61FCF401900FB4BA5 /* CreateReadingListViewController.xib in Resources */, + D8A42BF91E815A9C00D8E281 /* TSMessagesDefaultDesign.json in Resources */, + D8A42BFA1E815A9C00D8E281 /* WMFArticleLanguagesSectionHeader.xib in Resources */, + D8A42BFC1E815A9C00D8E281 /* ArticlePopoverViewController.xib in Resources */, + 67861A1E223C13990044F69D /* FocusNavigationView.xib in Resources */, + D8C4D3D61FD5D9260089CEC2 /* TUSafariActivity.bundle in Resources */, + 670AF19226BDE6E9005F76D0 /* EmptyViewController.xib in Resources */, + 67861A16223C13820044F69D /* WMFFindAndReplaceKeyboardBar.xib in Resources */, + D8A42BFD1E815A9C00D8E281 /* Launch Screen.storyboard in Resources */, + D8A42BFE1E815A9C00D8E281 /* EditSaveViewController.storyboard in Resources */, + D8A42BFF1E815A9C00D8E281 /* NotificationBackgroundWarning.png in Resources */, + 7A6CA2962289AF2200C7FD47 /* EditLinkViewController.xib in Resources */, + D82E958D1F16502E007BD960 /* WMFLanguagesViewController.xib in Resources */, + 7A6F560221AF508B0076D184 /* TextFormatting.storyboard in Resources */, + 7A2FE5592051757E00F92F8F /* EraseSavedArticlesView.xib in Resources */, + D8A42C011E815A9C00D8E281 /* NotificationBackgroundError@2x.png in Resources */, + D8A42C041E815A9C00D8E281 /* WMFImageGalleryDetailOverlayView.xib in Resources */, + 6782DC1A2347EE5A003FA21B /* DiffListChangeCell.xib in Resources */, + 7AE1D3361FCD057200393471 /* Saved.storyboard in Resources */, + D8E27BB01F82B54E00F9D2B3 /* RMessageDefaultDesign.json in Resources */, + B01EA0812022856300813726 /* ScrollableEducationPanelView.xib in Resources */, + 6782DC0823453D6B003FA21B /* DiffHeaderCompareItemView.xib in Resources */, + D8A42C081E815A9C00D8E281 /* WMFRandomDiceButton.html in Resources */, + 7A9524D322665E6400C55CDC /* InsertMediaSettingsImageView.xib in Resources */, + 7ABE17202239B8EE006BA309 /* WelcomeContainerViewController.xib in Resources */, + D8A42C0D1E815A9C00D8E281 /* WMFForgotPasswordViewController.storyboard in Resources */, + B0845E1F2061B44A00CDD98E /* SavedProgressViewController.storyboard in Resources */, + 83836ED41F615E5B007D1A05 /* ShareViewController.xib in Resources */, + D8A42C101E815A9C00D8E281 /* WMFTableHeaderFooterLabelView.xib in Resources */, + D8A42C131E815A9C00D8E281 /* NotificationButtonBackground.png in Resources */, + D8A42C151E815A9C00D8E281 /* WMFSettingsViewController.storyboard in Resources */, + D8A42C161E815A9C00D8E281 /* Images.xcassets in Resources */, + D8A42C171E815A9C00D8E281 /* ReadingThemesControlsViewController.xib in Resources */, + B0F4762321F921D300C4E254 /* EditSummaryViewController.xib in Resources */, + 830C0DDD23D9C218006471C4 /* Properties.js in Resources */, + D8A42C181E815A9C00D8E281 /* NotificationButtonBackground@2x.png in Resources */, + 7AEC9861219F529000BEF62B /* DefaultEditToolbarView.xib in Resources */, + 6724289423612AF700490629 /* DiffListUneditedCell.xib in Resources */, + 7A707985223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8CE26391E698E2400DAE2E0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D8CE263D1E698E2400DAE2E0 /* WMFCaptchaViewController.storyboard in Resources */, + 7A9524DD22669A8B00C55CDC /* InsertMediaSettingsButtonView.xib in Resources */, + D8E27BB51F82B5DB00F9D2B3 /* RMessageView.xib in Resources */, + D8CE26421E698E2400DAE2E0 /* AboutViewController.plist in Resources */, + D8CE26431E698E2400DAE2E0 /* NotificationBackgroundMessage.png in Resources */, + 7A741DD0207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib in Resources */, + D8CE26471E698E2400DAE2E0 /* WMFRandomDiceButtonRoll.js in Resources */, + 83AE1C861F34BB64004B62E0 /* ImageDimmingExampleViewController.xib in Resources */, + D8CE26491E698E2400DAE2E0 /* NotificationBackgroundSuccess.png in Resources */, + D8CE264A1E698E2400DAE2E0 /* NotificationBackgroundErrorIcon@2x.png in Resources */, + 7A9A611921124CF500403154 /* CreateNewReadingListButtonView.xib in Resources */, + 7A16C4EC212D941C00F0D5EC /* SubSettingsViewController.xib in Resources */, + 7A8422592268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib in Resources */, + D8CE264C1E698E2400DAE2E0 /* WMFArticleLanguagesSectionFooter.xib in Resources */, + 6724288E23612AF000490629 /* DiffListContextCell.xib in Resources */, + B0B4236E1EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib in Resources */, + D8CE264D1E698E2400DAE2E0 /* NotificationBackgroundErrorIcon.png in Resources */, + 7AB20A1222FC8432006FECB4 /* PageHistoryCountsViewController.xib in Resources */, + D8CE264E1E698E2400DAE2E0 /* WMFLanguageCell.xib in Resources */, + D8CE26531E698E2400DAE2E0 /* NotificationBackgroundSuccessIcon@2x.png in Resources */, + D8CE26541E698E2400DAE2E0 /* WMFTwoFactorPasswordViewController.storyboard in Resources */, + 7AFA21C120110D7900E957E7 /* HintViewController.xib in Resources */, + 7A71567422697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib in Resources */, + 6782DBE52345377B003FA21B /* DiffHeaderSummaryView.xib in Resources */, + B0524B0421484FB400D8FD8D /* DescriptionWelcome.storyboard in Resources */, + D80ED2611EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib in Resources */, + D8CE26581E698E2400DAE2E0 /* TableOfContentsCell.xib in Resources */, + D8421B59203CC8420040F50B /* DebugReadingListsViewController.xib in Resources */, + D8CE26591E698E2400DAE2E0 /* WMFReferencePopoverMessageViewController.storyboard in Resources */, + 7A82765F226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib in Resources */, + B0EFCD731EBF13B2008F36E5 /* LibrariesUsed.plist in Resources */, + D8533EDA1ECF581600E44F86 /* NewsCollectionViewHeader.xib in Resources */, + 83E52BBA1F681F940045E776 /* ShareAFactViewController.xib in Resources */, + 7A84224D2268BBE70074648E /* InsertMediaImageInfoView.xib in Resources */, + D8CE265E1E698E2400DAE2E0 /* WMFEmptyView.xib in Resources */, + D8CE265F1E698E2400DAE2E0 /* NotificationBackgroundMessage@2x.png in Resources */, + 7A393287236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib in Resources */, + BA45241E1F324C3100439C42 /* FontSizeSliderViewController.xib in Resources */, + 670AF18E26BDE646005F76D0 /* OldTalkPageHeaderView.xib in Resources */, + BA45242A1F32500C00439C42 /* TextSizeChangeExampleViewController.xib in Resources */, + 6782DBF123453799003FA21B /* DiffHeaderCompareView.xib in Resources */, + 7ADF498121B45CEE009EA338 /* TextFormattingPlainToolbarView.xib in Resources */, + D8CE26601E698E2400DAE2E0 /* NotificationBackgroundWarningIcon.png in Resources */, + D8CE26611E698E2400DAE2E0 /* WMFLoginViewController.storyboard in Resources */, + 7AC19E4B2301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib in Resources */, + D8CE26631E698E2400DAE2E0 /* WMFWelcome.storyboard in Resources */, + 6780D5C1237AF8AE0087A5D1 /* DiffToolbarView.xib in Resources */, + 7ABE172A2239BB54006BA309 /* WelcomePanelViewController.xib in Resources */, + 83B01F8C23DB399E001185F4 /* DescriptionEditViewController.storyboard in Resources */, + 67DCB7A2278F8D700041272C /* InfoPlist.strings in Resources */, + 6771C9562509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib in Resources */, + 83E3E72B2440F24300AA2E9A /* LoadingAnimationViewController.xib in Resources */, + 7ADF498D21B45E42009EA338 /* TextFormattingGroupedToolbarView.xib in Resources */, + 7A25367D21B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib in Resources */, + D8CE266B1E698E2400DAE2E0 /* NotificationBackgroundWarning@2x.png in Resources */, + B0EFCD691EBEC2F7008F36E5 /* LibrariesUsed.storyboard in Resources */, + B0524AF72144D7BE00D8FD8D /* DescriptionHelpViewController.xib in Resources */, + D8CE266C1E698E2400DAE2E0 /* NotificationBackgroundSuccess@2x.png in Resources */, + 7AF0265C22985CB9000E0A06 /* BeKindInputAccessoryView.xib in Resources */, + D8CE266D1E698E2400DAE2E0 /* WMFChangePasswordViewController.storyboard in Resources */, + 7ABAD6BA20338CFB006A364C /* ReadingListDetailUnderBarViewController.xib in Resources */, + D8CE266F1E698E2400DAE2E0 /* NotificationBackgroundError.png in Resources */, + D8CE26701E698E2400DAE2E0 /* WMFReferencePanels.storyboard in Resources */, + 6782DBF8234537D0003FA21B /* DiffHeaderExtendedView.xib in Resources */, + 6782DBEB23453787003FA21B /* DiffHeaderEditorView.xib in Resources */, + 83023C0C20E51DDF00EC7592 /* SearchLanguagesBarViewController.xib in Resources */, + 676070A52280D72400A81F09 /* TalkPageTopicNewViewController.xib in Resources */, + D8CE26711E698E2400DAE2E0 /* Places.storyboard in Resources */, + D8CE26731E698E2400DAE2E0 /* WMFAccountCreationViewController.storyboard in Resources */, + 6782DBFA234537D0003FA21B /* DiffHeaderTitleView.xib in Resources */, + 7A9F061F2266432200856321 /* InsertMediaSettingsTextTableViewCell.xib in Resources */, + D8CE26741E698E2400DAE2E0 /* NotificationBackgroundSuccessIcon.png in Resources */, + 7AE1FE3721B4A9790068BE9F /* TextFormattingButtonView.xib in Resources */, + D8CE26751E698E2400DAE2E0 /* WMFSettingsTableViewCell.xib in Resources */, + D8CE26781E698E2400DAE2E0 /* NotificationBackgroundWarningIcon@2x.png in Resources */, + D8CE267B1E698E2400DAE2E0 /* TableOfContentsHeader.xib in Resources */, + 0042811725E6E841004945B3 /* NYTPhotoViewer.bundle in Resources */, + 6780D5B5237A1F490087A5D1 /* DiffResponse.json in Resources */, + D8CE267E1E698E2400DAE2E0 /* WMFBarButtonItemPopoverMessageViewController.storyboard in Resources */, + 7A2432C41FCF401900FB4BA5 /* CreateReadingListViewController.xib in Resources */, + D8CE26801E698E2400DAE2E0 /* TSMessagesDefaultDesign.json in Resources */, + D8CE26811E698E2400DAE2E0 /* WMFArticleLanguagesSectionHeader.xib in Resources */, + D8CE26831E698E2400DAE2E0 /* ArticlePopoverViewController.xib in Resources */, + 67861A1C223C13990044F69D /* FocusNavigationView.xib in Resources */, + D8C4D3D41FD5D9260089CEC2 /* TUSafariActivity.bundle in Resources */, + 670AF19126BDE6E8005F76D0 /* EmptyViewController.xib in Resources */, + 67861A14223C13820044F69D /* WMFFindAndReplaceKeyboardBar.xib in Resources */, + D8CE26841E698E2400DAE2E0 /* Launch Screen.storyboard in Resources */, + D8CE26851E698E2400DAE2E0 /* EditSaveViewController.storyboard in Resources */, + D8CE26861E698E2400DAE2E0 /* NotificationBackgroundWarning.png in Resources */, + 7A6CA2942289AF2200C7FD47 /* EditLinkViewController.xib in Resources */, + D82E958B1F16502E007BD960 /* WMFLanguagesViewController.xib in Resources */, + 7A6F560021AF508B0076D184 /* TextFormatting.storyboard in Resources */, + 7A2FE5572051757E00F92F8F /* EraseSavedArticlesView.xib in Resources */, + D8CE26881E698E2400DAE2E0 /* NotificationBackgroundError@2x.png in Resources */, + D8CE268B1E698E2400DAE2E0 /* WMFImageGalleryDetailOverlayView.xib in Resources */, + 6782DC182347EE59003FA21B /* DiffListChangeCell.xib in Resources */, + 7AE1D3341FCD057200393471 /* Saved.storyboard in Resources */, + D8E27BAE1F82B54D00F9D2B3 /* RMessageDefaultDesign.json in Resources */, + B01EA07F2022856300813726 /* ScrollableEducationPanelView.xib in Resources */, + 6782DC0623453D6B003FA21B /* DiffHeaderCompareItemView.xib in Resources */, + D8CE268F1E698E2400DAE2E0 /* WMFRandomDiceButton.html in Resources */, + 7A9524D122665E6400C55CDC /* InsertMediaSettingsImageView.xib in Resources */, + 7ABE171E2239B8EE006BA309 /* WelcomeContainerViewController.xib in Resources */, + D8CE26941E698E2400DAE2E0 /* WMFForgotPasswordViewController.storyboard in Resources */, + B0845E1D2061B44A00CDD98E /* SavedProgressViewController.storyboard in Resources */, + 83836ED21F615E5B007D1A05 /* ShareViewController.xib in Resources */, + D8CE26971E698E2400DAE2E0 /* WMFTableHeaderFooterLabelView.xib in Resources */, + D8CE269A1E698E2400DAE2E0 /* NotificationButtonBackground.png in Resources */, + D8CE269C1E698E2400DAE2E0 /* WMFSettingsViewController.storyboard in Resources */, + D8CE269D1E698E2400DAE2E0 /* Images.xcassets in Resources */, + D8CE269E1E698E2400DAE2E0 /* ReadingThemesControlsViewController.xib in Resources */, + B0F4762121F921D300C4E254 /* EditSummaryViewController.xib in Resources */, + 830C0DDB23D9C218006471C4 /* Properties.js in Resources */, + D8CE269F1E698E2400DAE2E0 /* NotificationButtonBackground@2x.png in Resources */, + 7AEC985F219F529000BEF62B /* DefaultEditToolbarView.xib in Resources */, + 6724289223612AF500490629 /* DiffListUneditedCell.xib in Resources */, + 7A707983223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8EC3F391E9BDA35006712EB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D8EC3F3D1E9BDA35006712EB /* WMFCaptchaViewController.storyboard in Resources */, + 7A9524DE22669A8B00C55CDC /* InsertMediaSettingsButtonView.xib in Resources */, + D8E27BB61F82B5DC00F9D2B3 /* RMessageView.xib in Resources */, + D8EC3F421E9BDA35006712EB /* AboutViewController.plist in Resources */, + D8EC3F431E9BDA35006712EB /* NotificationBackgroundMessage.png in Resources */, + 7A741DD1207FB9CC00CBAAE2 /* SearchBarExtendedViewController.xib in Resources */, + D8EC3F471E9BDA35006712EB /* WMFRandomDiceButtonRoll.js in Resources */, + 83AE1C871F34BB65004B62E0 /* ImageDimmingExampleViewController.xib in Resources */, + D8EC3F491E9BDA35006712EB /* NotificationBackgroundSuccess.png in Resources */, + D8EC3F4A1E9BDA35006712EB /* NotificationBackgroundErrorIcon@2x.png in Resources */, + 7A9A611A21124CF500403154 /* CreateNewReadingListButtonView.xib in Resources */, + 7A16C4ED212D941C00F0D5EC /* SubSettingsViewController.xib in Resources */, + 7A84225A2268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.xib in Resources */, + D8EC3F4C1E9BDA35006712EB /* WMFArticleLanguagesSectionFooter.xib in Resources */, + 6724288F23612AF000490629 /* DiffListContextCell.xib in Resources */, + B0B4236F1EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.xib in Resources */, + D8EC3F4D1E9BDA35006712EB /* NotificationBackgroundErrorIcon.png in Resources */, + 7AB20A1322FC8432006FECB4 /* PageHistoryCountsViewController.xib in Resources */, + D8EC3F4E1E9BDA35006712EB /* WMFLanguageCell.xib in Resources */, + D8EC3F531E9BDA35006712EB /* NotificationBackgroundSuccessIcon@2x.png in Resources */, + D8EC3F541E9BDA35006712EB /* WMFTwoFactorPasswordViewController.storyboard in Resources */, + 7AFA21C220110D7900E957E7 /* HintViewController.xib in Resources */, + 7A71567522697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.xib in Resources */, + 6782DBE62345377B003FA21B /* DiffHeaderSummaryView.xib in Resources */, + B0524B0521484FB400D8FD8D /* DescriptionWelcome.storyboard in Resources */, + D80ED2621EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.xib in Resources */, + D8EC3F581E9BDA35006712EB /* TableOfContentsCell.xib in Resources */, + D8421B5A203CC8420040F50B /* DebugReadingListsViewController.xib in Resources */, + D8EC3F591E9BDA35006712EB /* WMFReferencePopoverMessageViewController.storyboard in Resources */, + 7A827660226E4E41000A2389 /* EditPreviewInternalLinkViewController.xib in Resources */, + B0EFCD741EBF13B2008F36E5 /* LibrariesUsed.plist in Resources */, + D8533EDB1ECF581600E44F86 /* NewsCollectionViewHeader.xib in Resources */, + 83E52BBB1F681F940045E776 /* ShareAFactViewController.xib in Resources */, + 7A84224E2268BBE70074648E /* InsertMediaImageInfoView.xib in Resources */, + D8EC3F5E1E9BDA35006712EB /* WMFEmptyView.xib in Resources */, + D8EC3F5F1E9BDA35006712EB /* NotificationBackgroundMessage@2x.png in Resources */, + 7A393288236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.xib in Resources */, + BA45241F1F324C3100439C42 /* FontSizeSliderViewController.xib in Resources */, + 670AF18D26BDE645005F76D0 /* OldTalkPageHeaderView.xib in Resources */, + BA45242B1F32500C00439C42 /* TextSizeChangeExampleViewController.xib in Resources */, + 6782DBF223453799003FA21B /* DiffHeaderCompareView.xib in Resources */, + 7ADF498221B45CEE009EA338 /* TextFormattingPlainToolbarView.xib in Resources */, + D8EC3F601E9BDA35006712EB /* NotificationBackgroundWarningIcon.png in Resources */, + D8EC3F611E9BDA35006712EB /* WMFLoginViewController.storyboard in Resources */, + 7AC19E4C2301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.xib in Resources */, + D8EC3F631E9BDA35006712EB /* WMFWelcome.storyboard in Resources */, + 6780D5C2237AF8AE0087A5D1 /* DiffToolbarView.xib in Resources */, + 7ABE172B2239BB54006BA309 /* WelcomePanelViewController.xib in Resources */, + 83B01F8D23DB399E001185F4 /* DescriptionEditViewController.storyboard in Resources */, + 67DCB7A1278F8D6F0041272C /* InfoPlist.strings in Resources */, + 6771C9552509FE6B00A7254B /* ArticleAsLivingDocHeaderView.xib in Resources */, + 83E3E72C2440F24300AA2E9A /* LoadingAnimationViewController.xib in Resources */, + 7ADF498E21B45E42009EA338 /* TextFormattingGroupedToolbarView.xib in Resources */, + 7A25367E21B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.xib in Resources */, + D8EC3F6B1E9BDA35006712EB /* NotificationBackgroundWarning@2x.png in Resources */, + B0EFCD6A1EBEC2F8008F36E5 /* LibrariesUsed.storyboard in Resources */, + B0524AF82144D7BE00D8FD8D /* DescriptionHelpViewController.xib in Resources */, + D8EC3F6C1E9BDA35006712EB /* NotificationBackgroundSuccess@2x.png in Resources */, + 7AF0265D22985CB9000E0A06 /* BeKindInputAccessoryView.xib in Resources */, + D8EC3F6D1E9BDA35006712EB /* WMFChangePasswordViewController.storyboard in Resources */, + 7ABAD6BB20338CFB006A364C /* ReadingListDetailUnderBarViewController.xib in Resources */, + D8EC3F6F1E9BDA35006712EB /* NotificationBackgroundError.png in Resources */, + D8EC3F701E9BDA35006712EB /* WMFReferencePanels.storyboard in Resources */, + 6782DBFB234537D0003FA21B /* DiffHeaderExtendedView.xib in Resources */, + 6782DBEC23453787003FA21B /* DiffHeaderEditorView.xib in Resources */, + 83023C0D20E51DDF00EC7592 /* SearchLanguagesBarViewController.xib in Resources */, + 676070A62280D72400A81F09 /* TalkPageTopicNewViewController.xib in Resources */, + D8EC3F711E9BDA35006712EB /* Places.storyboard in Resources */, + D8EC3F731E9BDA35006712EB /* WMFAccountCreationViewController.storyboard in Resources */, + 6782DBFD234537D0003FA21B /* DiffHeaderTitleView.xib in Resources */, + 7A9F06202266432200856321 /* InsertMediaSettingsTextTableViewCell.xib in Resources */, + D8EC3F741E9BDA35006712EB /* NotificationBackgroundSuccessIcon.png in Resources */, + 7AE1FE3821B4A9790068BE9F /* TextFormattingButtonView.xib in Resources */, + D8EC3F751E9BDA35006712EB /* WMFSettingsTableViewCell.xib in Resources */, + D8EC3F781E9BDA35006712EB /* NotificationBackgroundWarningIcon@2x.png in Resources */, + D8EC3F7B1E9BDA35006712EB /* TableOfContentsHeader.xib in Resources */, + 0042811625E6E841004945B3 /* NYTPhotoViewer.bundle in Resources */, + 6780D5B6237A1F490087A5D1 /* DiffResponse.json in Resources */, + D8EC3F7E1E9BDA35006712EB /* WMFBarButtonItemPopoverMessageViewController.storyboard in Resources */, + 7A2432C51FCF401900FB4BA5 /* CreateReadingListViewController.xib in Resources */, + D8EC3F801E9BDA35006712EB /* TSMessagesDefaultDesign.json in Resources */, + D8EC3F811E9BDA35006712EB /* WMFArticleLanguagesSectionHeader.xib in Resources */, + D8EC3F831E9BDA35006712EB /* ArticlePopoverViewController.xib in Resources */, + 67861A1D223C13990044F69D /* FocusNavigationView.xib in Resources */, + D8C4D3D51FD5D9260089CEC2 /* TUSafariActivity.bundle in Resources */, + 670AF19026BDE6E7005F76D0 /* EmptyViewController.xib in Resources */, + 67861A15223C13820044F69D /* WMFFindAndReplaceKeyboardBar.xib in Resources */, + D8EC3F841E9BDA35006712EB /* Launch Screen.storyboard in Resources */, + D8EC3F851E9BDA35006712EB /* EditSaveViewController.storyboard in Resources */, + D8EC3F861E9BDA35006712EB /* NotificationBackgroundWarning.png in Resources */, + 7A6CA2952289AF2200C7FD47 /* EditLinkViewController.xib in Resources */, + D82E958C1F16502E007BD960 /* WMFLanguagesViewController.xib in Resources */, + 7A6F560121AF508B0076D184 /* TextFormatting.storyboard in Resources */, + 7A2FE5582051757E00F92F8F /* EraseSavedArticlesView.xib in Resources */, + D8EC3F881E9BDA35006712EB /* NotificationBackgroundError@2x.png in Resources */, + D8EC3F8B1E9BDA35006712EB /* WMFImageGalleryDetailOverlayView.xib in Resources */, + 6782DC192347EE59003FA21B /* DiffListChangeCell.xib in Resources */, + 7AE1D3351FCD057200393471 /* Saved.storyboard in Resources */, + D8E27BAF1F82B54D00F9D2B3 /* RMessageDefaultDesign.json in Resources */, + B01EA0802022856300813726 /* ScrollableEducationPanelView.xib in Resources */, + 6782DC0723453D6B003FA21B /* DiffHeaderCompareItemView.xib in Resources */, + D8EC3F8F1E9BDA35006712EB /* WMFRandomDiceButton.html in Resources */, + 7A9524D222665E6400C55CDC /* InsertMediaSettingsImageView.xib in Resources */, + 7ABE171F2239B8EE006BA309 /* WelcomeContainerViewController.xib in Resources */, + D8EC3F941E9BDA35006712EB /* WMFForgotPasswordViewController.storyboard in Resources */, + B0845E1E2061B44A00CDD98E /* SavedProgressViewController.storyboard in Resources */, + 83836ED31F615E5B007D1A05 /* ShareViewController.xib in Resources */, + D8EC3F971E9BDA35006712EB /* WMFTableHeaderFooterLabelView.xib in Resources */, + D8EC3F9A1E9BDA35006712EB /* NotificationButtonBackground.png in Resources */, + D8EC3F9C1E9BDA35006712EB /* WMFSettingsViewController.storyboard in Resources */, + D8EC3F9D1E9BDA35006712EB /* Images.xcassets in Resources */, + D8EC3F9E1E9BDA35006712EB /* ReadingThemesControlsViewController.xib in Resources */, + B0F4762221F921D300C4E254 /* EditSummaryViewController.xib in Resources */, + 830C0DDC23D9C218006471C4 /* Properties.js in Resources */, + D8EC3F9F1E9BDA35006712EB /* NotificationButtonBackground@2x.png in Resources */, + 7AEC9860219F529000BEF62B /* DefaultEditToolbarView.xib in Resources */, + 6724289323612AF600490629 /* DiffListUneditedCell.xib in Resources */, + 7A707984223AB69000A2BDFC /* WelcomePanelLabelContentViewController.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8FA19151E1BE05B009675C3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 533AB8AE259792A9003A43D9 /* wikipedia-language-variants.json in Resources */, + D8FA19161E1BE069009675C3 /* WMFArticlePreviewViewController.xib in Resources */, + D801C9301EB8E131001FA294 /* Localizable.strings in Resources */, + D801C9351EB8E131001FA294 /* Localizable.stringsdict in Resources */, + 83ACAAA824E6E6C5003B3035 /* wikipedia-languages.json in Resources */, + 7A00D177208FB72900A9C7BA /* BatchEditToolbarViewController.xib in Resources */, + D8E4CCAA1D931CE100EB6C61 /* assets in Resources */, + 83ACAAA924E6E6E3003B3035 /* wikipedia-namespaces in Resources */, + 8336F1432119BD6E000CDE02 /* MediaWikiAcceptLanguageMapping.json in Resources */, + D84C36521F33866C00895FA1 /* WMF Framework.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00966EAA284033ED006C6E27 /* SwiftLint */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftLint; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint > /dev/null; then\n\tswiftlint --fix --config \".swiftlint-autocorrect.yml\" && swiftlint\nelse\n\techo \"warning: SwiftLint not installed, run \\\"scripts/brew_install\\\" script to install\"\nfi\n"; + }; + 00966EAB28403D4D006C6E27 /* SwiftLint */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftLint; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint > /dev/null; then\n\tswiftlint --fix --config \".swiftlint-autocorrect.yml\" && swiftlint\nelse\n\techo \"warning: SwiftLint not installed, run \\\"scripts/brew_install\\\" script to install\"\nfi\n"; + }; + 00966EAC28403E80006C6E27 /* SwiftLint */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftLint; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint > /dev/null; then\n\tswiftlint --fix --config \".swiftlint-autocorrect.yml\" && swiftlint\nelse\n\techo \"warning: SwiftLint not installed, run \\\"scripts/brew_install\\\" script to install\"\nfi\n"; + }; + 00966EAD28403EA7006C6E27 /* SwiftLint */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftLint; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint > /dev/null; then\n\tswiftlint --fix --config \".swiftlint-autocorrect.yml\" && swiftlint\nelse\n\techo \"warning: SwiftLint not installed, run \\\"scripts/brew_install\\\" script to install\"\nfi\n"; + }; + D87021721EBA69B7000D02D6 /* Update Localizations */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "", + ); + name = "Update Localizations"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$SRCROOT/scripts/localization\" \"$SRCROOT\"\n"; + showEnvVarsInLog = 0; + }; + D8B724B41ECF09F700D10836 /* Copy Test Fixtures */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "$(SRCROOT)/WikipediaUnitTests/Fixtures", + ); + name = "Copy Test Fixtures"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(TARGET_BUILD_DIR)/$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/Fixtures", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cp -R \"${SCRIPT_INPUT_FILE_0}/\" \"${SCRIPT_OUTPUT_FILE_0}/\"\n"; + }; + D8D987FD1E1C468F00789CA0 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "rm -rf \"$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/qqq.lproj\"\nrm -rf \"$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/be-tarask.lproj\"\nrm -rf \"$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/bik.lproj\"\nrm -rf \"$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/nah.lproj\"\nrm -rf \"$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/xmf.lproj\"\nrm -rf \"$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/zza.lproj\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 00021DDD24D48EFD00476F97 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00021DE824D48EFD00476F97 /* Widgets.swift in Sources */, + 0033D79A24F818EC00CAB5B3 /* TopReadWidget.swift in Sources */, + 0033D79E24F8193900CAB5B3 /* CGPoint+Extensions.swift in Sources */, + 00669507265DAB7800E23AE4 /* FeaturedArticleWidget+LocalizedStrings.swift in Sources */, + 0033D79924F818EC00CAB5B3 /* TopReadWidget+LocalizedStrings.swift in Sources */, + 006D273724D8D8D100947551 /* Date+Extensions.swift in Sources */, + FF9416D824E203030070FEE7 /* OnThisDayWidget.swift in Sources */, + FF9416DE24E2098C0070FEE7 /* OnThisDayView.swift in Sources */, + 0033D79D24F8193900CAB5B3 /* UIColor+Extensions.swift in Sources */, + 006D273524D8BAFB00947551 /* View+Extensions.swift in Sources */, + 0033D7A124F8199300CAB5B3 /* Sparkline.swift in Sources */, + 00669505265DA3D300E23AE4 /* FeaturedArticleWidget.swift in Sources */, + 00B16E8E293AACC200EF847F /* UIImage+Extensions.swift in Sources */, + 00021E0424D4A42A00476F97 /* PictureOfTheDayWidget.swift in Sources */, + 002AB870250BEFBE00ADAC87 /* PictureOfTheDayWidget+LocalizedStrings.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0E83805F1D64989F0076EDE4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D8EEA0F11D6DF60600D88143 /* WMFTodayContinueReadingWidgetViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 676C864026D40AEA00A704C1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6780D76D2830A7A200265F10 /* NotificationService.swift in Sources */, + 00A988092829D92B006D800B /* PushNotificationContentIdentifier.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B0606EAA20AA6FF0006EC6B9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B0606EB120AA6FF0006EC6B9 /* SnapshotRecorderTests.swift in Sources */, + B0606EC520AA955C006EC6B9 /* SnapshotHelper.swift in Sources */, + B0BDA58220B09A090098DB65 /* XCUIApplication+SnapshotUtilities.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BC42734E1A7C736800068882 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 004281BE25E6EFC4004945B3 /* LSNSURLSessionHook.m in Sources */, + D84649AD1D4514F7009DB4A0 /* WMFTaskGroupTests.m in Sources */, + 004281C125E6EFC4004945B3 /* NSData+Matcheable.m in Sources */, + B0E808B61C0D17070065EBC0 /* LSStubResponseDSL+WithJSON.m in Sources */, + B0E809371C0D1A420065EBC0 /* MWKLanguageLinkControllerTests.m in Sources */, + 673612F224FD7210002A1989 /* ArticleAsLivingDocViewModelTests.swift in Sources */, + FFBA8C1927D824D8009E9B65 /* URL+ExtensionTests.swift in Sources */, + 004281C525E6EFC4004945B3 /* LSStringMatcher.m in Sources */, + 004281B925E6EFC4004945B3 /* NSURLRequest+DSL.m in Sources */, + B0E809411C0D1A820065EBC0 /* NSURL+WMFLinkParsingTests.m in Sources */, + 67DAEDEA27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelGenericTests.swift in Sources */, + B0E808981C0D16430065EBC0 /* XCTestCase+WMFLocaleTesting.m in Sources */, + 004281B825E6EFC4004945B3 /* LSNSURLHook.m in Sources */, + BC45FF4B1C1B22C200BAE501 /* NSObject+WMFReflection.m in Sources */, + A452F9FD24081B0200D8ED09 /* MockUIDevice.swift in Sources */, + B0E808741C0D154C0065EBC0 /* NSBundle+TestAssets.m in Sources */, + B0E8093B1C0D1A590065EBC0 /* WMFSafeAssignTests.m in Sources */, + 67DAEDE827E8FB63005CF9B6 /* NotificationsCenterDetailViewModelWelcomeTests.swift in Sources */, + B0E809111C0D18FD0065EBC0 /* WMFSubstringUtilsTests.m in Sources */, + B0E808951C0D16330065EBC0 /* NSArray+WMFShuffle.m in Sources */, + A452F9FB24081A7200D8ED09 /* LocationManagerTests.swift in Sources */, + 67C6F77427E2E78800B9C864 /* NotificationsCenterCellViewModelPageLinkTests.swift in Sources */, + 67C6F76827E2E76E00B9C864 /* NotificationsCenterCellViewModelUserTalkMessageTests.swift in Sources */, + B0E8090F1C0D18F30065EBC0 /* WMFMathTests.m in Sources */, + 671DF9D825F2B59A0011799E /* ShortDescriptionControllerTests.swift in Sources */, + 67DAEDEE27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelEditRevertedTests.swift in Sources */, + B0D530EB1CE151C10078BAED /* CodeFileLocationTests.m in Sources */, + 6790AAF422D6861500D442D6 /* OldTalkPageFetcherTests.swift in Sources */, + 679F0AAD24574AD400EF4A6A /* ArticleViewControllerTests.swift in Sources */, + 67E3992A24786E2100441831 /* ReadingListManualPerformanceTests.swift in Sources */, + D864D68C1DA3EA3800B86934 /* NumberFormatterExtrasTests.swift in Sources */, + A452F9F824081A5500D8ED09 /* MockCLLocationManager.swift in Sources */, + 67DAEDEC27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelUserTalkMessageTests.swift in Sources */, + 679F0AA92456FADE00EF4A6A /* ArticleCacheReadingTests.swift in Sources */, + 67E3992C24786E6D00441831 /* TalkPageManualPerformanceTests.swift in Sources */, + B0C06B9F218240CA00E481CC /* Collection+AsyncMapTests.swift in Sources */, + 004281CA25E6EFC4004945B3 /* LSStubRequestDSL.m in Sources */, + BC62AE621C58FC810064C589 /* NSProcessInfo+WMFTestEnvironment.m in Sources */, + D8BDA8C11E71C0760031F4BF /* WMFBlocksKitTests.m in Sources */, + B0E8092F1C0D1A0B0065EBC0 /* NSURL+WMFExtrasTests.m in Sources */, + B39427441E71F79700D3146D /* NSDictionaryBlocksKitTest.m in Sources */, + 67DAEDF027E8FB63005CF9B6 /* NotificationsCenterDetailViewModelMentionTests.swift in Sources */, + 6790AAF522D6861500D442D6 /* TalkPageControllerTests.swift in Sources */, + B0E808771C0D155A0065EBC0 /* XCTestCase+WMFBundleConvenience.m in Sources */, + 004281BC25E6EFC4004945B3 /* LSASIHTTPRequestHook.m in Sources */, + 67C6F77C27E2E78800B9C864 /* NotificationsCenterCellViewModelWelcomeTests.swift in Sources */, + B389CFCB1E6784B600483C06 /* WMFDatabaseHousekeeperTests.swift in Sources */, + 67C6F77927E2E78800B9C864 /* NotificationsCenterCellViewModelUserRightsChangeTests.swift in Sources */, + 67DAEDE927E8FB63005CF9B6 /* NotificationsCenterDetailViewModelLoginIssuesTests.swift in Sources */, + 67C6F77B27E2E78800B9C864 /* NotificationsCenterCellViewModelLoginIssuesTests.swift in Sources */, + 67C6F77527E2E78800B9C864 /* NotificationsCenterCellViewModelGenericTests.swift in Sources */, + B0E809601C0D1BA30065EBC0 /* WMFSearchFetcherTests.m in Sources */, + 004281C325E6EFC4004945B3 /* LSMatcher.m in Sources */, + 67C6F77727E2E78800B9C864 /* NotificationsCenterCellViewModelEditMilestoneTests.swift in Sources */, + 004281B325E6EFC4004945B3 /* LSStubResponse.m in Sources */, + B01662B31D1B8CAB006F4544 /* NSURL+WMFQueryParametersTests.m in Sources */, + 67F73386273C1FBA00D7D713 /* NotificationServiceHelperTests.swift in Sources */, + 67C6F75027E293C700B9C864 /* NotificationsCenterViewModelTests.swift in Sources */, + 67DAEDEB27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelUserRightsChangeTests.swift in Sources */, + D8D550811DF0D2BD00B90177 /* NSArray+WMFMatching.m in Sources */, + 8330533323F0388E00123141 /* DataStoreTests.swift in Sources */, + 00D280FC247F019C006BEE23 /* Date+ExtensionTests.swift in Sources */, + B0E8088F1C0D16140065EBC0 /* WMFAsyncTestCase.m in Sources */, + B0E8087D1C0D15760065EBC0 /* WMFRandomFileUtilities.m in Sources */, + BC90DE791C57C5AD007E0E81 /* WMFWelcomeLanguageViewControllerVisualTests.m in Sources */, + B0E809551C0D1B510065EBC0 /* CLLocation+WMFBearingTests.m in Sources */, + 004281BF25E6EFC4004945B3 /* NSRegularExpression+Matcheable.m in Sources */, + 679FA104242E651C0095F3C6 /* ArticleManualPerformanceTests.swift in Sources */, + 004281B725E6EFC4004945B3 /* NSURLRequest+LSHTTPRequest.m in Sources */, + D858A7DF1DA6A04A009C3DEB /* WMFDateCalculationTests.m in Sources */, + 67DAEDF227E8FB63005CF9B6 /* NotificationsCenterDetailViewModelPageLinkTests.swift in Sources */, + 004281BA25E6EFC4004945B3 /* LSHTTPStubURLProtocol.m in Sources */, + B0E809051C0D18A00065EBC0 /* CircularBitwiseRotationTests.m in Sources */, + B0E8095E1C0D1B930065EBC0 /* WMFMTLModelSerializationTests.m in Sources */, + D8EC64031D007B1F00C286EE /* WMFLinkParsingTests.m in Sources */, + 004281C725E6EFC4004945B3 /* NSData+Nocilla.m in Sources */, + B0E809131C0D19090065EBC0 /* WMFDateFormatterTests.m in Sources */, + 83BBBE5623F56F9400AD0994 /* LocaleTests.swift in Sources */, + B39427451E71F79700D3146D /* NSSetBlocksKitTest.m in Sources */, + 67ED8EB124F99FF400DD5D39 /* SignificantEventsFetcherTests.swift in Sources */, + B0E808B91C0D17160065EBC0 /* WMFHTTPHangingProtocol.m in Sources */, + BC52D0F71C207D3300F625A9 /* TWNStringsTests.m in Sources */, + 004281BD25E6EFC4004945B3 /* LSASIHTTPRequestAdapter.m in Sources */, + B0E808A91C0D16A00065EBC0 /* UIView+VisualTestSizingUtils.m in Sources */, + BC45FF481C1B1F9F00BAE501 /* NSUserDefaults+WMFReset.m in Sources */, + 004281BB25E6EFC4004945B3 /* ASIHTTPRequestStub.m in Sources */, + 6714D6CB245A2B9700CE5A4A /* ArticleCacheReadingManualTests.swift in Sources */, + 004281C825E6EFC4004945B3 /* LSHTTPRequestDSLRepresentation.m in Sources */, + B02F96661DFA11DC007DA007 /* WMFArticleListTableViewCell+DynamicTypeFontTests.swift in Sources */, + 004281B625E6EFC4004945B3 /* LSHTTPClientHook.m in Sources */, + 67C6F77327E2E78800B9C864 /* NotificationsCenterCellViewModelWikidataConnectionTests.swift in Sources */, + 67C6F77627E2E78800B9C864 /* NotificationsCenterCellViewModelMentionTests.swift in Sources */, + B0E809351C0D1A2F0065EBC0 /* WMFGeometryTests.m in Sources */, + 6714D6CD245A2C1D00CE5A4A /* ArticleTestHelpers.swift in Sources */, + B0E8090B1C0D18D90065EBC0 /* NSString+FormattedAttributedStringTests.m in Sources */, + D8396D1B22CF7052005625D8 /* WMFArticleTests.swift in Sources */, + B0E8090D1C0D18E70065EBC0 /* WMFImageURLParsingTests.m in Sources */, + 6790AAF722D6861500D442D6 /* TalkPageLocalHandlerTests.swift in Sources */, + 67C6F77827E2E78800B9C864 /* NotificationsCenterCellViewModelThanksTests.swift in Sources */, + 004281B525E6EFC4004945B3 /* LSHTTPRequestDiff.m in Sources */, + A452F9F924081A5500D8ED09 /* MockCLHeading.swift in Sources */, + 00A8F58626BDD5E700175B8E /* WidgetSampleContentTests.swift in Sources */, + 6790AAF322D6861500D442D6 /* TalkPageNetworkDataTests.swift in Sources */, + B0E8089C1C0D165B0065EBC0 /* XCTestCase+SwiftDefaults.swift in Sources */, + BCD3200A1C6EC6BC00317D08 /* NSTimeZone+WMFTestingUtils.m in Sources */, + B0E808831C0D15A20065EBC0 /* MWKDataStore+TemporaryDataStore.m in Sources */, + 004281B425E6EFC4004945B3 /* LSNocilla.m in Sources */, + 67C6F77A27E2E78800B9C864 /* NotificationsCenterCellViewModelEditRevertedTests.swift in Sources */, + B37B6FE91EEAFE11007CBB12 /* EventLoggingServiceTests.swift in Sources */, + B0E8086D1C0D15170065EBC0 /* WMFCodingStyle.m in Sources */, + 830ECAD61FBDE77F0080B1EF /* ReadingListsTests.swift in Sources */, + 67C6F74E27E2919B00B9C864 /* RemoteNotificationsModelController+TestExtensions.swift in Sources */, + D8800CB11E2FF5B70035D2DB /* QuadKeyTests.swift in Sources */, + 8386BDE723857F87007EE89D /* URLParsingAndRoutingTests.swift in Sources */, + 67DAEDED27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelWikidataConnectionTests.swift in Sources */, + BCD557BB1C45B1600060A51A /* UIApplication+VisualTestUtils.m in Sources */, + B0E809091C0D18BC0065EBC0 /* NSString+WMFHTMLParsingTests.m in Sources */, + 19A175F095F5197BA20EA8BA /* NSUserActivity+WMFExtensionsTest.m in Sources */, + 67DAEDEF27E8FB63005CF9B6 /* NotificationsCenterDetailViewModelThanksTests.swift in Sources */, + 67DAEDF127E8FB63005CF9B6 /* NotificationsCenterDetailViewModelEditMilestoneTests.swift in Sources */, + 004281C925E6EFC4004945B3 /* LSStubResponseDSL.m in Sources */, + 004281B225E6EFC4004945B3 /* LSStubRequest.m in Sources */, + 6790AAF622D6861500D442D6 /* TalkPageTestHelpers.swift in Sources */, + 004281C025E6EFC4004945B3 /* NSString+Matcheable.m in Sources */, + 004281C425E6EFC4004945B3 /* LSRegexMatcher.m in Sources */, + 004281C225E6EFC4004945B3 /* LSDataMatcher.m in Sources */, + 004281C625E6EFC4004945B3 /* NSString+Nocilla.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D4991431181D51DE00E6073C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8361474B24223689003E49D3 /* ArticleViewController+Announcements.swift in Sources */, + B0524B29214854E900D8FD8D /* DescriptionWelcomePanelViewController.swift in Sources */, + 7A1C498F227254EC00230ED2 /* InsertMediaSearchViewController.swift in Sources */, + 672D69A4273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift in Sources */, + D8A6BAED1E4C9BF400A981C8 /* UserLocationAnnotationView.swift in Sources */, + 00E75B5D27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift in Sources */, + 6798331A22C174ED0073CE6F /* LinkOnlyTextView.swift in Sources */, + B0E8059A1C0CE2E40065EBC0 /* WMFSearchFunnel.m in Sources */, + 00A7946B245CA4E60063BA18 /* ArticleSurveyTimerController.swift in Sources */, + 41FCAA3621C844CB001D8411 /* ReadingListEntryCollectionViewController.swift in Sources */, + 672C35EB22D8E7CA007B8D46 /* EmptyViewController.swift in Sources */, + 7AB6F0FF22AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift in Sources */, + 834C269E240D49F400245BE7 /* ReferenceViewController.swift in Sources */, + 0010F93927A49C7700D77848 /* HorizontalSpacerView.swift in Sources */, + 0EC0447F1C797DC20033D773 /* WMFImageURLActivitySource.swift in Sources */, + 7A27EDA22279F5270010CB24 /* InsertLinkViewController.swift in Sources */, + 671DF9C525F2AE4E0011799E /* ShortDescriptionController.swift in Sources */, + 00FCCBCF2900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift in Sources */, + B0CD9DDB1F70997300051843 /* WMFWelcomeLanguagesAnimationView.swift in Sources */, + 6782DB9D2343B7DB003FA21B /* DiffHeaderTitleView.swift in Sources */, + 830D71CF1F704DD40080078B /* ArticleFetchedResultsViewController.swift in Sources */, + 6730FD0E28998EFD000E5F40 /* TalkPageReplyComposeContentView.swift in Sources */, + 6747117E250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift in Sources */, + B0EFCD6D1EBF12E5008F36E5 /* LibrariesUsed.swift in Sources */, + 67DC5BE923A03FE700B03A84 /* ArticleToolbarController.swift in Sources */, + B0CD9DD61F70997300051843 /* WMFWelcomeAnimationView.swift in Sources */, + 67E2E48F250452E60070F12D /* ArticleAsLivingDocHeaderView.swift in Sources */, + B0524B6F214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift in Sources */, + 67F73E752267B9070079DEEF /* TalkPageReplyListViewController.swift in Sources */, + 67E8B0742268DA8B00537BC9 /* OldTalkPageTopicCell.swift in Sources */, + B0F92C6F1E3C580900B72802 /* WMFCaptchaResetter.swift in Sources */, + 830D71C31F703C980080078B /* ArticleURLListViewController.swift in Sources */, + 7A7AC84621B6B89B003B849B /* SectionEditorViewController.swift in Sources */, + 006ABEE82901E8F600722DF8 /* VanishAccountWarningView.swift in Sources */, + D87234011E1FF0A500751E83 /* PlacesViewController.swift in Sources */, + 678D79FC235E59B2006161FF /* DiffListUneditedViewModel.swift in Sources */, + 00474A2F28DD1B13002E3C09 /* TalkPageCoffeeRollView.swift in Sources */, + 00E75B7627EB946D00A45B78 /* ReusableCell.swift in Sources */, + 00E2EA8E26E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift in Sources */, + B01E54AF206479CC00374FEE /* ProgressContainer.swift in Sources */, + 00AA5AAC276BF2AE005295B0 /* TextBarButtonItem.swift in Sources */, + 67C6F79227E8C03A00B9C864 /* NotificationsCenterAction.swift in Sources */, + B0CD9DDC1F70997300051843 /* WMFWelcomeAnalyticsAnimationView.swift in Sources */, + 00E75B6227EB87DC00A45B78 /* NotificationsCenterDetailView.swift in Sources */, + 83987AD020E4FB2C00C92C60 /* UISearchBar+Theme.swift in Sources */, + 7A420DB422A029780005689B /* EditFunnel.swift in Sources */, + B027FD281E678F5C005644A9 /* WMFAuthButton.swift in Sources */, + 8356115D28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift in Sources */, + 67C9FBFF28C77F350065A530 /* TalkPageTopicComposeViewController.swift in Sources */, + B01490A51DB96EA7007F5391 /* WMFReferencePanelViewController.swift in Sources */, + D84DAA161EEEF527008E4B18 /* SWStepSlider.swift in Sources */, + B0ACB13321265B930078C136 /* WMFImageGalleryDescriptionTextView.swift in Sources */, + 53A575FA2602C845009835E6 /* WMFAppViewController+Extensions.swift in Sources */, + B0C7A0801F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift in Sources */, + 00E75B6C27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift in Sources */, + 8382F8D320D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift in Sources */, + 679A24032968DBFC008D7686 /* ShiftingNavigationBarView.swift in Sources */, + D88FCADF1E4B74D300505A9F /* WikidataFetcher+Places.swift in Sources */, + B0432344210680A900A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift in Sources */, + FFD7B85924B3CA7A005C2471 /* ReferenceShowing.swift in Sources */, + 676F39282745FB2000F4D33D /* NotificationsCenterFiltersViewModel.swift in Sources */, + 83F1096E23D0E787003F3E9E /* RandomArticleViewController.swift in Sources */, + B0E802BE1C0CD2360065EBC0 /* UIButton+WMFButton.m in Sources */, + 7A715661226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift in Sources */, + 7A20AE082057F39C005FB5DF /* UIView+Identifier.swift in Sources */, + 7A29A5CE1F6C49C600E8F42B /* CollectionViewUpdater.swift in Sources */, + B0E803711C0CD9A80065EBC0 /* TableOfContentsAnimator.swift in Sources */, + BA4524181F324C3100439C42 /* FontSizeSliderViewController.swift in Sources */, + B0338A841DBF0AF20012F588 /* WMFReferencePageBackgroundView.swift in Sources */, + 0042811925E6E841004945B3 /* NYTPhotosOverlayView.m in Sources */, + 67F73E792267B9510079DEEF /* TalkPageTopicNewViewController.swift in Sources */, + B00050141C52D73800515F70 /* UIApplication+RTL.swift in Sources */, + 8321FCCC2387231E0079F3C7 /* ViewControllerRouter.swift in Sources */, + FF59DF4D2555E0CB0048E66C /* InternalLinkPreviewing.swift in Sources */, + 83EDC4C128B424B5007D0192 /* VanishAccountPopUpAlertView.swift in Sources */, + 7A610CBD220A582A00C266AE /* HintController.swift in Sources */, + 7A13A8992028BB3600F28254 /* ReadingListsAlertController.swift in Sources */, + 6780CF282967690200D45927 /* TalkPageArchivesView.swift in Sources */, + 67C6F7AB27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift in Sources */, + 83510B0728F4CF6400B6235B /* TalkPageErrorStateView.swift in Sources */, + D850A53A1F8686DE006FD295 /* WMFThemeableNavigationController.m in Sources */, + B083375D1DB16A09002860D2 /* WMFWelcomePanelViewController.swift in Sources */, + 67C6F7A627E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift in Sources */, + 7A1469C5220BC223000A20F1 /* EditHintController.swift in Sources */, + 007CCF0126D5A10200D5EA7C /* NotificationsCenterViewController.swift in Sources */, + 7A610CB7220A30C900C266AE /* HintViewController.swift in Sources */, + D84BF62F1DBE616D00E0C85E /* UIViewController+WMFAlerts.swift in Sources */, + 83023C1F20E6584F00EC7592 /* SearchTransition.swift in Sources */, + 67CEF263235110F700D5CA6C /* DiffNetworkModels.swift in Sources */, + 7A23CECF211A24FC00441A79 /* FeedFunnel.swift in Sources */, + 679A23FE2968DAB9008D7686 /* ShiftingTopView.swift in Sources */, + 7AC19E322301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift in Sources */, + 676070A2227CE60800A81F09 /* TalkPageReplyFooterView.swift in Sources */, + BCCB813E1C110702008BC602 /* NSDate+WMFPOTDTitle.m in Sources */, + 67E8B0762268DE4B00537BC9 /* TalkPageContainerViewController.swift in Sources */, + 6780D5BA237AF8A10087A5D1 /* DiffToolbarView.swift in Sources */, + 7A49A20121231510005C574C /* CollectionViewFooter.swift in Sources */, + 0072991528AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift in Sources */, + 41CCB67421CC1F9700206B47 /* SavedArticlesCollectionViewController.swift in Sources */, + 0EE2438D1DC9889B00066CBD /* WMFTableHeaderFooterLabelView.m in Sources */, + 679A23F92968D865008D7686 /* ShiftingTopViewsData.swift in Sources */, + 83B4CDBF20E3DCD6007D5A6E /* SearchViewController.swift in Sources */, + D8E6FF6724054FA100686272 /* ArticleViewController+LinkPreviewing.swift in Sources */, + 67C6F77E27E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift in Sources */, + 8350FC4C20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift in Sources */, + 007CCF0726D5A17200D5EA7C /* NotificationsCenterView.swift in Sources */, + 8382F8C720D844C600AE5250 /* ArticleLocationCollectionViewCell.swift in Sources */, + 67112E3D275E603B007A9850 /* NotificationsCenterInboxViewModel.swift in Sources */, + B37B38F01E5F432F00FE11BD /* WMFDatabaseHousekeeper.swift in Sources */, + 6771299424FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift in Sources */, + 7A6CA28E2289AF2200C7FD47 /* EditLinkViewController.swift in Sources */, + 7AEC9859219F529000BEF62B /* DefaultEditToolbarView.swift in Sources */, + D808DCED1E438C0C00A3E89C /* PlaceSearch.swift in Sources */, + B0524B47214854E900D8FD8D /* DescriptionWelcomePageViewController.swift in Sources */, + 009C8EC229071E720056A3AC /* NSString+Range.swift in Sources */, + 6761AEE12704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift in Sources */, + 67C78F7128B6DA1300AC207A /* SwiftUITextView.swift in Sources */, + B3F21D0F1EB0EBB4000ED0BB /* PlaceSearchService.swift in Sources */, + 7A393281236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift in Sources */, + BA8203E21F15B4CC00925E93 /* ShareActivityController.swift in Sources */, + D808DCEB1E438BE300A3E89C /* PlaceSearchSuggestionController.swift in Sources */, + 7A8422472268BBE70074648E /* InsertMediaImageInfoView.swift in Sources */, + 6782DBA32343B7EE003FA21B /* DiffHeaderSummaryView.swift in Sources */, + 7AFC79F821B0367700BB0C50 /* TextFormattingTableViewController.swift in Sources */, + B09B03EB1CE0FB2600009083 /* WMFPageHistoryRevision.m in Sources */, + 0EF863511C19E4F100006D2D /* WMFEmptyView.m in Sources */, + 830378402940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift in Sources */, + 7AE99B2821CC4F420092BE7F /* TextSizeFormattingTableViewController.swift in Sources */, + 7A715667226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift in Sources */, + B02B82751C696ECA00B19309 /* WMFSettingsTableViewCell.m in Sources */, + 00FCCBC5290082C200C9ECD2 /* TalkPageFindInPageState.swift in Sources */, + B00DDEDB1DB4B76B00615FA2 /* UIView+WMFSubviews.swift in Sources */, + 678E7E8126432F060005439C /* NavigationEventsFunnel.swift in Sources */, + 830ECACF1FBDD8C00080B1EF /* ReadingListsViewController.swift in Sources */, + 83ACF8E528E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift in Sources */, + D82117FC1EE58C080076C040 /* MapAnnotation.swift in Sources */, + 7AF0265622985CB9000E0A06 /* BeKindInputAccessoryView.swift in Sources */, + B0379A2C1D8B756C00D973CF /* WMFReferencePopoverMessageViewController.m in Sources */, + B0E803441C0CD7980065EBC0 /* WMFSearchFetcher.m in Sources */, + 83B01F7C23DB0BA2001185F4 /* ArticleViewController+Editing.swift in Sources */, + BCA15AE51C0E213300D0A3EA /* LoggingDefaults.swift in Sources */, + 003AD72E2979C512005BDB90 /* EditNoticesViewModel.swift in Sources */, + 005E004128DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift in Sources */, + 67B5334128416C0D00C33E13 /* UserDataExportCache.swift in Sources */, + 7A741DCA207FB9CC00CBAAE2 /* SearchBarExtendedViewController.swift in Sources */, + 7A0CD24021DFA34100066F68 /* TextFormattingToolbarView.swift in Sources */, + 6782DBF6234537CF003FA21B /* DiffHeaderExtendedView.swift in Sources */, + 00D46DAA2889B9250015DE9B /* TalkPageCell.swift in Sources */, + 8351CE7820D4424100E32FC1 /* CollectionViewHeader.swift in Sources */, + D89DAE1B1D6CC6410089F7E1 /* MWKTitleLanguageController.m in Sources */, + 67B64D5C2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift in Sources */, + B0E803911C0CDABE0065EBC0 /* UIView+WMFSnapshotting.m in Sources */, + D818D3811ED7254D0076110D /* ColumnarCollectionViewController.swift in Sources */, + 009B835D298091CD00AABEA3 /* EditNoticesView.swift in Sources */, + 672428972362113400490629 /* DiffFetcher.swift in Sources */, + 83E776A320FFA4D700E26A47 /* DetailTransition.swift in Sources */, + 832BD3BC28996B68002623CA /* VanishAccountContentView.swift in Sources */, + 671DF9C925F2AE4F0011799E /* WikidataDescriptionController.swift in Sources */, + 00474A2A28DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift in Sources */, + B0E803E61C0CDD450065EBC0 /* UIViewController+WMFStoryboardUtilities.m in Sources */, + 7A4D227D21B1CD8600D889BD /* TextFormattingTableViewCell.swift in Sources */, + B0524B75214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift in Sources */, + 6734115422735788005B31DA /* OldTalkPageFetcher.swift in Sources */, + 0ED2E9FA1CB2B3B300D1C844 /* UIVIewController+WMFCommonRotationSupport.swift in Sources */, + 6741245027E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift in Sources */, + 00E2EA8926E28A9700B1A741 /* NotificationsCenterCellStyle.swift in Sources */, + B0E803CC1C0CDC9B0065EBC0 /* WMFTitleInsetRespectingButton.m in Sources */, + B0C6BE571E4526A40033BD6E /* WMFChangePasswordViewController.swift in Sources */, + B0421AA2206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift in Sources */, + B0F4761B21F921D300C4E254 /* EditSummaryViewController.swift in Sources */, + B389CFCE1E6F238300483C06 /* WMFMapsActivity.swift in Sources */, + 7A2BB1D421F27AC5004C0FDF /* WKWebView+OffsetHack.swift in Sources */, + B01E3AF921F986750015B715 /* PreviewWebViewContainer.swift in Sources */, + 00E75B7127EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift in Sources */, + 7A9524D722669A8B00C55CDC /* InsertMediaSettingsButtonView.swift in Sources */, + 83B01F7223DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift in Sources */, + 7A6ED52120ADBF950001849F /* LoginFunnel.swift in Sources */, + 00EACEC628E39D470054DDB4 /* TalkPageEmptyView.swift in Sources */, + 670F765F22B0C10600D87545 /* FakeProgressLoading.swift in Sources */, + B0016CC321362DB300FA1096 /* SetupGradientView.swift in Sources */, + 83DB4410244A57590046FABE /* RootNavigationController.swift in Sources */, + D818FEBB21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift in Sources */, + 0042812925E6E841004945B3 /* NYTPhotoViewController.m in Sources */, + BC23E4DD1C223DCB00B5AFDE /* WMFArticleRevisionFetcher.m in Sources */, + 00E75B6727EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift in Sources */, + B0B4CF0A1CC0500E0051DF7A /* WMFArticleLanguagesSectionHeader.m in Sources */, + 7ABE17352239DCF6006BA309 /* WelcomeAnimationViewController.swift in Sources */, + D837CC37231FE9CC00BA6130 /* ThemeableViewController.swift in Sources */, + 83C0688E292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift in Sources */, + 7A73B48221E54B4200249E09 /* SectionEditorNavigationItemController.swift in Sources */, + 00097D5C29660FF2000B3514 /* View+Extensions.swift in Sources */, + B0FFFB2A21C9BED1001E787E /* TextFormattingButton.swift in Sources */, + 7AF6F76622395BEC00949393 /* EditingWelcomeViewController.swift in Sources */, + B0524B51214854E900D8FD8D /* DescriptionWelcomeContainerViewController.swift in Sources */, + 7AF56C2F21DDEC1C00563A9C /* TextFormattingProviding.swift in Sources */, + B0ED17341E4912EB008B70AD /* WMFTwoFactorPasswordViewController.swift in Sources */, + 00F5AED027C6C80C006390A8 /* PushNotificationsSettingsViewController.swift in Sources */, + B0B423611EF9D69C00D3DC4C /* OnThisDayViewController.swift in Sources */, + 0042813125E6E841004945B3 /* NYTPhotosDataSource.m in Sources */, + B0CD9DDA1F70997300051843 /* WMFWelcomeExplorationAnimationView.swift in Sources */, + B0E805941C0CE2C60065EBC0 /* SavedPagesFunnel.m in Sources */, + 83CA612A20D1675800EF0C4A /* ExploreCardViewController.swift in Sources */, + D8FEECCD1DE3729400B883F0 /* WMFChange.m in Sources */, + 6747118825072D1500287951 /* IconTitleBadge.swift in Sources */, + B0016CB921354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift in Sources */, + B0E807DB1C0CF04A0065EBC0 /* MWKSearchRedirectMapping.m in Sources */, + BC62FFC01C11064200533DA9 /* MWKImageInfoFetcher+PicOfTheDayInfo.m in Sources */, + 67DAEDA323CE24DA003AA208 /* SavedArticlesFetcher.swift in Sources */, + 7A4B333C2136EDED00C6C820 /* UnderlineButton.swift in Sources */, + D8CB32AD1E79D8A0008A0966 /* RoundedCornerView.swift in Sources */, + 00CB6898288B0CD3002EBB0A /* TalkPageHeaderView.swift in Sources */, + 83E52BBF1F682E3E0045E776 /* LicenseView.swift in Sources */, + 7AE1FE3121B4A9790068BE9F /* TextFormattingButtonView.swift in Sources */, + 671AC2562226FB9B005B37F8 /* ReadingThemesControlsProtocols.swift in Sources */, + FF2090F02500247100849774 /* ThreeLineHeaderView.swift in Sources */, + 830AD2B924D1D615003EEFE6 /* WebPageUserScript.swift in Sources */, + D80ED25C1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift in Sources */, + B027447D1E6253E200E7B248 /* WMFScrollViewController.swift in Sources */, + B0B423681EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift in Sources */, + 83B01F7723DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift in Sources */, + 7AF8B7422102297A009772CC /* SearchSettingsViewController.swift in Sources */, + D8B3D7661EC34F5B00930C21 /* SaveButtonsController.swift in Sources */, + 67C6F79727E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift in Sources */, + 7ABE17002239B346006BA309 /* WelcomeViewController.swift in Sources */, + 832A7A5B23EA138C00D0A750 /* ArticleViewController+References.swift in Sources */, + EB8237532970AE6A00FD629E /* WMFItemSourceExcludingActivityTypes.swift in Sources */, + B0524B65214854E900D8FD8D /* DescriptionWelcomeImageViewController.swift in Sources */, + 0072990628AC44F100DCD2E6 /* TalkPageCellTopicView.swift in Sources */, + 7ABE170C2239B5A0006BA309 /* WelcomePageViewController.swift in Sources */, + B0F9299F1F84789D002A0788 /* WMFWelcomeInitialViewController.swift in Sources */, + 6782DBCD2343FDF2003FA21B /* DiffListUneditedCell.swift in Sources */, + D8A47C8F23D7338C002AA823 /* ArticleViewController+TableOfContents.swift in Sources */, + 0072991F28AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift in Sources */, + B09B03ED1CE0FB4200009083 /* PageHistorySection.swift in Sources */, + D8B166851FD97A0500097D8B /* ViewController.swift in Sources */, + 7AB809D022675B2300BFAB7C /* ThemeableTextView.swift in Sources */, + 83A642752226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift in Sources */, + B0C7A07A1F710E75008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift in Sources */, + 67D9D1FB29711CA700BFCD4F /* Loadable.swift in Sources */, + 673FC3D0273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift in Sources */, + 678D29AE2729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift in Sources */, + 7A827659226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift in Sources */, + 672D69A9273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift in Sources */, + B0E8036D1C0CD98B0065EBC0 /* TableOfContentsViewController.swift in Sources */, + B0E805921C0CE2C60065EBC0 /* ProtectedEditAttemptFunnel.m in Sources */, + 7A9F2776225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift in Sources */, + 83ED2E24289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift in Sources */, + 0036C8B3282C2AAA00EADB35 /* Notification+NotificationsCenter.swift in Sources */, + B0DF6F811CFE1D0B0046E507 /* WKWebView+WMFWebViewControllerJavascript.m in Sources */, + 0EC044791C7917860033D773 /* WMFArticleTextActivitySource.m in Sources */, + 67C1757628AD4D6000C5ABA4 /* TalkPageDataController.swift in Sources */, + D82E956A1F156F77007BD960 /* UIView+SubviewEnumeration.swift in Sources */, + 833D6B48229EE872003CB650 /* TalkPageTopic+Extensions.swift in Sources */, + B010E1A81E723E3600CFE1CD /* WMFAuthLinkLabel.swift in Sources */, + BA4524241F32500C00439C42 /* TextSizeChangeExampleViewController.swift in Sources */, + BA7683C51F30D86A00A487AA /* ProminentSwitch.swift in Sources */, + 7A0161E01FE8B4CA00AEDC3D /* TagCollectionViewCell.swift in Sources */, + 83E52BB41F681F940045E776 /* ShareAFactViewController.swift in Sources */, + B0267CF31E32A0CB006B6D8D /* WMFPasswordResetter.swift in Sources */, + FF921857252E8F4F00C39A8F /* ThanksGiving.swift in Sources */, + BAA0D91C1F4F165A00091284 /* PageIssuesTableViewController.swift in Sources */, + 8397601B2811ABC7000FE3B1 /* UNAuthorizationStatus+String.swift in Sources */, + B0E802B81C0CD2140065EBC0 /* UIBarButtonItem+WMFButtonConvenience.m in Sources */, + B08E7E9B1C1FA57A00EC3C99 /* UIViewController+WMFEmptyView.m in Sources */, + FFD7B85624B3B384005C2471 /* ReferenceBackLinksViewControllerDelegate.swift in Sources */, + 009B8358298091BC00AABEA3 /* EditNoticesViewController.swift in Sources */, + 003CD3E928EF7C77000158E4 /* TalkPageFindInPageSearchController.swift in Sources */, + B0524B1F214854E900D8FD8D /* DescriptionWelcomeInitialViewController.swift in Sources */, + D80BF0A32347735E00B3B522 /* AppSearchButton.swift in Sources */, + 8330532E23EF107D00123141 /* MediaListGalleryViewController.swift in Sources */, + 83E3E7252440F1FE00AA2E9A /* LoadingAnimationViewController.swift in Sources */, + D87914DD1DFA04E10012C5DA /* NSUserDefaults+WMFApplicationDefaults.swift in Sources */, + 67E0691B223B32F1008550AC /* FocusNavigationView.swift in Sources */, + 83DAA9B023FEB611002D5716 /* ReferenceBackLinksViewController.swift in Sources */, + 00FCCBCA2900848300C9ECD2 /* TalkPageViewController+FindInPage.swift in Sources */, + 7A4FE53F1FA00AEF009FA199 /* ArticlePeekPreviewViewController.swift in Sources */, + 8382F8D920D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift in Sources */, + 00EBB7CC27D6A86A002025AC /* SettingsPresentationDelegate.swift in Sources */, + D8B1668C1FD97FE000097D8B /* WMFViewController.m in Sources */, + D82E95851F16502E007BD960 /* WMFLanguagesViewController.m in Sources */, + 677129A024FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift in Sources */, + 0022DD2925829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift in Sources */, + 7A82898C21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift in Sources */, + B0E806951C0CEA7B0065EBC0 /* AboutViewController.m in Sources */, + 7A6ED51C20ADBF950001849F /* SettingsFunnel.swift in Sources */, + 7A71565B226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift in Sources */, + D818D3AB1ED87E8F0076110D /* ArticleLocationCellUpdating.swift in Sources */, + 7AFA21BB20110D7900E957E7 /* ReadingListHintViewController.swift in Sources */, + 83B01F8123DB1235001185F4 /* SectionFetcher.swift in Sources */, + 6782DBD92344EC86003FA21B /* DiffHeaderViewModels.swift in Sources */, + B0866F461CCAEBE40088A789 /* WMFArticleLanguagesSectionFooter.m in Sources */, + D8533ED51ECF581600E44F86 /* NewsCollectionViewHeader.swift in Sources */, + D876769F21E7B73C00491039 /* ToolbarSeparatorView.swift in Sources */, + B00DDEDD1DB591C400615FA2 /* WMFWelcomeContainerViewController.swift in Sources */, + 00FCB2BE26D8398700F5A47A /* NotificationsCenterCell.swift in Sources */, + 83F1097323D0F115003F3E9E /* HelpViewController.swift in Sources */, + 67134A1728A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift in Sources */, + 83FBE96F1F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift in Sources */, + 6789FA2E22E7790900E43842 /* TalkPage+Extensions.swift in Sources */, + D8A47C8523D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift in Sources */, + D8E27BA11F82B38100F9D2B3 /* RMessageView.m in Sources */, + B0C6BE481E428C940033BD6E /* WMFAccountCreator.swift in Sources */, + 7ABAD6B420338CFB006A364C /* ReadingListDetailUnderBarViewController.swift in Sources */, + B066F0D51E513DAA00A199F8 /* UIViewController+WMFHideKeyboard.swift in Sources */, + 832289DB1F7291BA0081A5FB /* SizeThatFitsReusableView.swift in Sources */, + 8368BB8424129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift in Sources */, + 00D280F7247EFFFE006BEE23 /* Date+Extensions.swift in Sources */, + 67F73E712267B8020079DEEF /* TalkPageTopicListViewController.swift in Sources */, + 7A19C64820DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift in Sources */, + 6782DBD32343FE03003FA21B /* DiffListGroupViewModel.swift in Sources */, + 67059DB52260D034009811AA /* SchemeHandler.swift in Sources */, + FFE891462445150B0058B642 /* AppTabBarDelegate.swift in Sources */, + B069FA2E1CEACB8400083D59 /* WeakScriptMessageDelegate.swift in Sources */, + 0042812D25E6E841004945B3 /* NYTPhotoCaptionView.m in Sources */, + 67985A542523D80000EBF353 /* ArticleAsLivingDocController.swift in Sources */, + 7A35CB871FD82B6300AAF3B7 /* ReadingListDetailViewController.swift in Sources */, + 7A9A611E21124D0F00403154 /* CreateNewReadingListButtonView.swift in Sources */, + D8E6FF7724058AC600686272 /* WMFWebView.m in Sources */, + B0E294CD1DB2CF4300861D04 /* UIView+Animations.swift in Sources */, + 7ABE17242239BB54006BA309 /* WelcomePanelViewController.swift in Sources */, + B0E8066B1C0CE9030065EBC0 /* MWKLanguageLinkFetcher.m in Sources */, + BA6972571F2BA0D900E35F78 /* SettingsTableViewSection.swift in Sources */, + 007CCF1126D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift in Sources */, + 7AC19E452301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift in Sources */, + 0EC0447B1C796FEF0033D773 /* WMFImageTextActivitySource.swift in Sources */, + 83EE476A20D019A100A21F34 /* ExploreViewController.swift in Sources */, + B0E805951C0CE2C60065EBC0 /* ToCInteractionFunnel.m in Sources */, + 675A7CFE227A3F7C0034D9D9 /* OldTalkPageHeaderView.swift in Sources */, + 7A8422532268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift in Sources */, + B0BCF0B9202537D800986F72 /* Panels.swift in Sources */, + 67C6F7A127E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift in Sources */, + D8E27BA61F82B38500F9D2B3 /* RMessage.m in Sources */, + 678D79F0235E5979006161FF /* DiffListChangeViewModel.swift in Sources */, + B0E8031C1C0CD6820065EBC0 /* WMFCompassView.m in Sources */, + B0E294D31DB2DC8900861D04 /* WMFWelcomeIntroductionViewController.swift in Sources */, + 6782DBC72343FDE4003FA21B /* DiffListContextCell.swift in Sources */, + 67B64D572507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift in Sources */, + D896C7961D6F2E4E007101DF /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m in Sources */, + 00EBB7C727D6878E002025AC /* BarButtonImageStyle.swift in Sources */, + 83023C0620E51DDF00EC7592 /* SearchLanguagesBarViewController.swift in Sources */, + B0C7A0861F710EB1008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift in Sources */, + B027447F1E665D9F00E7B248 /* UIViewController+WMFChildViewController.swift in Sources */, + B0CD9DD91F70997300051843 /* WMFWelcomeIntroductionAnimationView.swift in Sources */, + 67C78F7628B7407000AC207A /* VanishAccountFooterView.swift in Sources */, + B0E805591C0CE0DC0065EBC0 /* UIView+WMFFrameUtils.m in Sources */, + 0030592627DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift in Sources */, + B00DDEE31DB5B16E00615FA2 /* WMFWelcomeAnimationViewControllers.swift in Sources */, + 7AB809DC22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift in Sources */, + 67C6F78327E8BC2E00B9C864 /* NotificationsCenterIconType.swift in Sources */, + B0E803761C0CD9C10065EBC0 /* TableOfContentsCell.swift in Sources */, + B0B0EC221C6999A9006F0D9C /* WMFSettingsMenuItem.m in Sources */, + 7ADF498721B45E42009EA338 /* TextFormattingGroupedToolbarView.swift in Sources */, + FFA0641925A943EB00B9460B /* BasicLogger.swift in Sources */, + 678D79F6235E599B006161FF /* DiffListContextViewModel.swift in Sources */, + BA7683C21F30C56300A487AA /* ImageDimmingExampleViewController.swift in Sources */, + 674E8AB92382DEFF0053D206 /* DiffTransformer.swift in Sources */, + 67A5E657236775C3007749FB /* GlobalUserInfoFetcher.swift in Sources */, + 83B01F9023DB41BE001185F4 /* ArticleViewController+Sharing.swift in Sources */, + B0E8054D1C0CE0DC0065EBC0 /* UIScrollView+ScrollSubviewToLocation.m in Sources */, + B0E806331C0CE7680065EBC0 /* MTLValueTransformer+WMFNumericValueTransformer.m in Sources */, + 0042812125E6E841004945B3 /* NSBundle+NYTPhotoViewer.m in Sources */, + 679471DB275F245000621071 /* NotificationsCenterInboxView.swift in Sources */, + 6734116422739CA2005B31DA /* TalkPageLocalHandler.swift in Sources */, + D84692E01D5E1E3F000A7058 /* TableOfContentsHeader.swift in Sources */, + 83F26B2A220B62EC002D87A4 /* SectionEditorButton.swift in Sources */, + 6761AEF72707E34F00E47BAD /* NotificationsCenterModelController.swift in Sources */, + 0EE489031D4ADCA00088505C /* UIViewController+WMFScrollToTop.swift in Sources */, + 00D1F58F28885BA300127169 /* TalkPageViewModel.swift in Sources */, + 00E5B3A428EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift in Sources */, + 7A9F060D2266425700856321 /* InsertMediaSettingsViewController.swift in Sources */, + 007F5C6D275AA74200E4B02C /* StackedImageLabelView.swift in Sources */, + 7AA96D5C21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift in Sources */, + 0042811D25E6E841004945B3 /* NYTPhotoTransitionAnimator.m in Sources */, + B09CE59A222F623900067D2A /* WKWebView+EditSelectionJavascript.swift in Sources */, + B0EF42D01C43FDD200D125A8 /* UIApplicationShortcutItem+WMFShortcutItem.m in Sources */, + B0F92C821E3FFEB900B72802 /* WMFAuthAccountCreationInfoFetcher.swift in Sources */, + 83F1096923D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift in Sources */, + 837A15F328DA591E00AAC3FC /* TalkPageCache.swift in Sources */, + 0072990B28AC455500DCD2E6 /* TalkPageCellViewModel.swift in Sources */, + 7A0FF2CC230343BA00E755D4 /* PageHistoryCollectionViewCell.swift in Sources */, + 67282FBD24855B7B00B73E20 /* ArticleContextMenuPresenting.swift in Sources */, + 6707C032237DBCEA0017E7B6 /* DiffRevisionTransition.swift in Sources */, + BAFCE8431F1D7FD30077D5E9 /* AppearanceSettingsViewController.swift in Sources */, + 0EF5BB6D1C110C2100DE75E1 /* AppDelegate.m in Sources */, + 830C0DD523D9AFBE006471C4 /* UIViewController+Push.swift in Sources */, + 83023C1120E6561900EC7592 /* ViewControllerTransitionsController.swift in Sources */, + D82972831E3950100061550A /* ArticlePlace.swift in Sources */, + 7ABE173B2239DEF0006BA309 /* WelcomeAnimationView.swift in Sources */, + B01CFC611E71069000B3546A /* String?+WMFExtras.swift in Sources */, + 83927D7B1F70570400051890 /* DisambiguationPagesViewController.swift in Sources */, + B0E803481C0CD7AA0065EBC0 /* WMFSearchResults.m in Sources */, + B0DE922B1D6E272B00EC76A7 /* WMFBarButtonItemPopoverMessageViewController.m in Sources */, + 6706A21922927D63004774E2 /* TalkPageHintViewController.swift in Sources */, + 7A0161B41FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift in Sources */, + B01490A31DB96E5F007F5391 /* WMFReferencePageViewController.swift in Sources */, + 67F73E6D2267B79E0079DEEF /* AccountViewController.swift in Sources */, + 8382F8CD20D9206000AE5250 /* ImageCollectionViewCell.swift in Sources */, + 676E813329380D8A00F15258 /* TalkPagesFunnel.swift in Sources */, + FF5555642771388F00925099 /* CollectionViewContextMenuShowing.swift in Sources */, + B0E8065C1C0CE84B0065EBC0 /* WikiTextSectionUploader.m in Sources */, + B02376B41D6ECCF9007DBA73 /* UIViewController+WMFDynamicHeightPopoverMessage.m in Sources */, + 83B1218427FC8750006B8CCC /* RemoteNotificationsFunnel.swift in Sources */, + 0072991A28AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift in Sources */, + D8C4D3D81FD5D9260089CEC2 /* TUSafariActivity.m in Sources */, + 7AF8CEED22653406000B7676 /* InsertMediaSelectedImageView.swift in Sources */, + 838790B32858009000067B1D /* TalkPageFetcher.swift in Sources */, + B08337611DB17DC5002860D2 /* WMFWelcomeAnalyticsViewController.swift in Sources */, + 6782DBAF2343B812003FA21B /* DiffHeaderCompareView.swift in Sources */, + B0D3E70C214AF776007578BA /* DescriptionEditViewController.swift in Sources */, + 00CF2EA027DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift in Sources */, + BC23E4E51C22429100B5AFDE /* WMFRevisionQueryResults.m in Sources */, + 67ADEE9623A2CFFB0000CAF7 /* ArticleWebMessagingController.swift in Sources */, + 0042813925E6E841004945B3 /* NYTScalingImageView.m in Sources */, + B0845E1120618DA400CDD98E /* SavedProgressViewController.swift in Sources */, + B0E802C11C0CD27F0065EBC0 /* WMFAppViewController.m in Sources */, + 7AFEB3F51FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift in Sources */, + B08624341F72EA1A00B770FD /* WMFWelcomeAnimationBackgroundView.swift in Sources */, + B0E805551C0CE0DC0065EBC0 /* UIView+IBExtras.swift in Sources */, + 00BCB71826DEE04D002C3F72 /* InsetLabelView.swift in Sources */, + 6782DB912343B6F9003FA21B /* DiffContainerViewController.swift in Sources */, + D8E6FF7C2405AAC400686272 /* ArticleViewController+Analytics.swift in Sources */, + D818D3861ED750E40076110D /* ArticleCollectionViewController.swift in Sources */, + B0E805961C0CE2C60065EBC0 /* WMFHamburgerMenuFunnel.m in Sources */, + 7A9524CB22665E6400C55CDC /* InsertMediaSettingsImageView.swift in Sources */, + B09B30D11DB817660012281F /* UIViewController+WMFWelcomeStoryboard.swift in Sources */, + 8320331B22B90528004A9EDA /* NavigationStateController.swift in Sources */, + D8421B53203CC8420040F50B /* DebugReadingListsViewController.swift in Sources */, + 7A71567922699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift in Sources */, + 7A48EA0E21B5C9B20083F3DC /* EditToolbarView.swift in Sources */, + 0042812525E6E841004945B3 /* NYTPhotoTransitionController.m in Sources */, + 0EF2249A1CC5536200FDF78E /* WMFLanguageCell.m in Sources */, + 6771298F24FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift in Sources */, + 7A1469BD220BBE44000A20F1 /* EditHintViewController.swift in Sources */, + 83DB0A5923EEDE2D00DA5F58 /* MWKDataStore+LegacyMobileview.swift in Sources */, + 0042813525E6E841004945B3 /* NYTPhotoDismissalInteractionController.m in Sources */, + 6782DBA92343B7FC003FA21B /* DiffHeaderEditorView.swift in Sources */, + 7A27E85221B19767001B2D21 /* TextStyleFormattingTableViewController.swift in Sources */, + 7A6ED50D20ADBF950001849F /* SessionsFunnel.swift in Sources */, + 6798332922C3F28A0073CE6F /* UITextView+Extensions.swift in Sources */, + B031032D1F677BEC00E2FCF6 /* WMFWelcomeExplorationViewController.swift in Sources */, + 7AE99B2E21CC53AB0092BE7F /* TextFontFormattingTableViewController.swift in Sources */, + 00B0B3D02978745400DD7893 /* EditNoticesFetcher.swift in Sources */, + 00E5B39F28EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift in Sources */, + 6782DBC12343FDCA003FA21B /* DiffListChangeCell.swift in Sources */, + 7A32078821E40193009E1677 /* SectionEditorMenuItemsController.swift in Sources */, + 00AA5AA7276BF29E005295B0 /* StatusTextBarButtonItem.swift in Sources */, + 67D9D1F02970D88E00BFCD4F /* DisclosureButton.swift in Sources */, + 6782DC0B23453D7D003FA21B /* DiffHeaderCompareItemView.swift in Sources */, + B04C444B1E56966B00C6DFB0 /* Array+WMFAllFieldsFilled.swift in Sources */, + D8940CEF1DB56C8A00E17F9E /* NewsViewController.swift in Sources */, + 6780CF3329676DE300D45927 /* ShiftingTopViewsStack.swift in Sources */, + B0E8071F1C0CEC8A0065EBC0 /* main.m in Sources */, + 0E8DC0951C74F0D700622CBD /* WMFDailyStatsLoggingFunnel.m in Sources */, + D84F2BFC1D2FEE6300963D42 /* WMFRandomDiceButton.m in Sources */, + 67985A862524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift in Sources */, + 67E5DA5C2761B0AB00CE827D /* NotificationsCenterFilterView.swift in Sources */, + 7A6ED51720ADBF950001849F /* UserHistoryFunnel.swift in Sources */, + 7A1C4995227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift in Sources */, + 7A71566E22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift in Sources */, + D858C7B6210B91CD0039E0C9 /* PassthroughView.swift in Sources */, + 83FBE9751F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift in Sources */, + D8A47C8A23D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift in Sources */, + D84F2C031D30162700963D42 /* WMFFirstRandomViewController.m in Sources */, + FF2B2110254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift in Sources */, + 7A28126320D3F84A009B42B5 /* FeedCardSettingsViewController.swift in Sources */, + 67E466FA241BED770014149B /* EditHistoryCompareFunnel.swift in Sources */, + 8386BDF62386D735007EE89D /* ViewController+URLHandling.swift in Sources */, + 8386BDFB2386D754007EE89D /* SinglePageWebViewController.swift in Sources */, + 00BCB72226DEEB1C002C3F72 /* RoundedImageView.swift in Sources */, + B0D4916F21F999A3002BBDD3 /* EditSaveViewController.swift in Sources */, + 0042813D25E6E841004945B3 /* NYTPhotosViewController.m in Sources */, + 83C06882292EC85700DF1403 /* TalkPageFormattingToolbarView.swift in Sources */, + D82972881E3A49980061550A /* ArticlePopoverViewController.swift in Sources */, + 007CCF0C26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift in Sources */, + B0E8073E1C0CED810065EBC0 /* WMFLogFormatter.m in Sources */, + 678D79E4235E592F006161FF /* DiffListChangeItemViewModel.swift in Sources */, + B0BCF0AB2023AC7700986F72 /* ScrollableEducationPanelViewController.swift in Sources */, + 7AB209F922FC67B4006FECB4 /* PageHistoryViewController.swift in Sources */, + B0E804DA1C0CE0B40065EBC0 /* NSString+FormattedAttributedString.m in Sources */, + B01CFC5F1E70B00100B3546A /* WMFDeleteBackwardReportingTextField.swift in Sources */, + B0267CED1E316AE3006B6D8D /* WMFForgotPasswordViewController.swift in Sources */, + 0E4A34721CBBFCD400A400F6 /* WMFImageGalleryViewController.m in Sources */, + 7AF49F80204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift in Sources */, + 6782DC112346920B003FA21B /* DiffContainerViewModel.swift in Sources */, + 7ADF853623516CF500500ADC /* PageHistoryHintController.swift in Sources */, + B0C6BE421E413B3F0033BD6E /* WMFAccountCreationViewController.swift in Sources */, + 7AB7DEC8227203A600DD61A2 /* InsertMediaViewController.swift in Sources */, + 67FBE33A29705FC200A2E4AD /* TalkPageArchivesItem.swift in Sources */, + 7A16C4E6212D941C00F0D5EC /* SubSettingsViewController.swift in Sources */, + 7A2FE55C20517BAE00F92F8F /* EraseSavedArticlesView.swift in Sources */, + B0E804C81C0CE0B40065EBC0 /* NSAttributedString+WMFModify.m in Sources */, + 83F1096423D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift in Sources */, + B0E806C41C0CEB380065EBC0 /* WMFSettingsViewController.m in Sources */, + 7A70797D223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift in Sources */, + B0E8054E1C0CE0DC0065EBC0 /* UIScrollView+WMFContentOffsetUtils.m in Sources */, + B0524AF12144D7BE00D8FD8D /* DescriptionHelpViewController.swift in Sources */, + 679A24082968E0D0008D7686 /* ShiftingScrollView.swift in Sources */, + 0E9B9E331CBF3225001E4C3C /* WMFImageGalleryDetailOverlayView.m in Sources */, + 7A998AC11FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift in Sources */, + D81E5F881E5F2C8400E1A80C /* UIApplication+SystemSettings.swift in Sources */, + 675175DC276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift in Sources */, + 83C06887292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift in Sources */, + 7ADF497B21B45CEE009EA338 /* TextFormattingPlainToolbarView.swift in Sources */, + 67E069062238A396008550AC /* FindAndReplaceKeyboardBar.swift in Sources */, + 6707C038237F0A6E0017E7B6 /* UIFont+Extensions.swift in Sources */, + 00DEE61928AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift in Sources */, + 674711832507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift in Sources */, + BC23E4E21C223FAE00B5AFDE /* WMFArticleRevision.m in Sources */, + 6780CF2D29676AB000D45927 /* ShiftingTopViewsContaining.swift in Sources */, + B01E3AFF21F98BFF0015B715 /* EditPreviewViewController.swift in Sources */, + D818D38B1ED765470076110D /* ArticleLocationCollectionViewController.swift in Sources */, + D8A6BAEF1E4C9C0700A981C8 /* ArticlePlaceView.swift in Sources */, + 7A196F5A21BF199500D9E4B5 /* SectionEditorWebView.swift in Sources */, + 6780CF232967683800D45927 /* TalkPageArchivesViewController.swift in Sources */, + 006ABEED2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift in Sources */, + 83836ECC1F615E5B007D1A05 /* ShareViewController.swift in Sources */, + 67E50B2B27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift in Sources */, + B0C6BE4A1E42D19D0033BD6E /* WMFCaptchaViewController.swift in Sources */, + 83DE45B92449C09B00671878 /* SplashScreenViewController.swift in Sources */, + 67DC5BEF23A1427D00B03A84 /* ActionHandlerScript.swift in Sources */, + 7A6ED51220ADBF950001849F /* ReadingListsFunnel.swift in Sources */, + 83B01F9523DB41D7001185F4 /* ArticleViewController+FindInPage.swift in Sources */, + B0E804BF1C0CE0B40065EBC0 /* DDLog+WMFLogger.m in Sources */, + 83FDE799293564AC006D55FE /* Link.swift in Sources */, + 0E281A331DC263DE00FA1AB1 /* WMFLegacyReference.swift in Sources */, + 6734115922735832005B31DA /* OldTalkPagesController.swift in Sources */, + 7ABAD6BF20349B91006A364C /* Collection.swift in Sources */, + 67BEFFD528AD9DF000606B38 /* TalkPageType.swift in Sources */, + 7A5A0543225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift in Sources */, + 6761AEE62704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift in Sources */, + 671DF9C125F2AE4E0011799E /* ArticleDescriptionControlling.swift in Sources */, + 67CEF26F2351113000D5CA6C /* DiffController.swift in Sources */, + 67D3C453228CB54E001D5741 /* OldTalkPageReplyComposeView.swift in Sources */, + 7A6F560521AF527A0076D184 /* TextFormattingInputViewController.swift in Sources */, + 7AC809C521DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift in Sources */, + B083371E1DADB251002860D2 /* WMFWelcomePageViewController.swift in Sources */, + 6782DBBB2343B861003FA21B /* DiffListViewController.swift in Sources */, + 7A29A5C81F6C405900E8F42B /* HistoryViewController.swift in Sources */, + D808DCEF1E438C5100A3E89C /* MKCoordinateRegion+Dimensions.swift in Sources */, + 7ADEAB031FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift in Sources */, + 0EBCA7481C176389004F1FD9 /* WMFAlertManager.swift in Sources */, + B083375F1DB17DA5002860D2 /* WMFWelcomeLanguageTableViewController.swift in Sources */, + B0408C552127F2C100AC76CE /* WMFImageGalleryGradientViews.swift in Sources */, + B0E805911C0CE2C60065EBC0 /* WMFLoginFunnel.m in Sources */, + 7AF56C3521DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift in Sources */, + 83C0656B23D23220001821BC /* TableOfContentsItem.swift in Sources */, + 672F0558222F24FB00FB1084 /* IconBarButtonItem.swift in Sources */, + 00D9276B29511E95004ECBEA /* PageHistoryCountsView.swift in Sources */, + D88C70181EE595E90022A26A /* MapView.swift in Sources */, + 7AFEB1BC1FA236A100B8DF32 /* UIViewController+Peekable.swift in Sources */, + 834CC34B21075B7600F62818 /* UITabBar+Theme.swift in Sources */, + D88FCAE11E4B776600505A9F /* MapUtilities.swift in Sources */, + 7AB20A0C22FC8432006FECB4 /* PageHistoryCountsViewController.swift in Sources */, + 678D29B3272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift in Sources */, + 6706A21722925FD2004774E2 /* InfoBannerView.swift in Sources */, + B068EDE0206B183500C827D1 /* Progress+ProgressUI.swift in Sources */, + 7A9F06192266432200856321 /* InsertMediaSettingsTextTableViewCell.swift in Sources */, + 83C06893292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift in Sources */, + FFD7B84624AEAB3F005C2471 /* ArticleScrolling.swift in Sources */, + 67D9D1F62970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift in Sources */, + 8367A27F20D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift in Sources */, + 7AEBAD452102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift in Sources */, + 83F1095F23D09F80003F3E9E /* ArticleViewController+WIconPopover.swift in Sources */, + 7A25367721B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift in Sources */, + 67FBE335297056EB00A2E4AD /* TalkPageArchivesFetcher.swift in Sources */, + D8E6FF6C24056AC300686272 /* ArticleViewController+ContextMenu.swift in Sources */, + 6734117022773122005B31DA /* OldTalkPageReplyCell.swift in Sources */, + 7A2432BE1FCF401900FB4BA5 /* CreateReadingListViewController.swift in Sources */, + 7A0DE50020CEEC760032AB57 /* ExploreFeedSettingsViewController.swift in Sources */, + B0E805611C0CE0DC0065EBC0 /* WKWebView+ElementLocation.m in Sources */, + B09B30CF1DB813760012281F /* UIViewController+WMFStoryboardUtilities.swift in Sources */, + 8330532923EF0B4200123141 /* ArticleViewController+Media.swift in Sources */, + B09705B4236B29D7006FDB5C /* DiffThanker.swift in Sources */, + 67DAEDD927E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift in Sources */, + B09B03F51CE0FB7700009083 /* ReadingThemesControlsViewController.swift in Sources */, + 00BCB71D26DEE1C7002C3F72 /* VerticalSpacerView.swift in Sources */, + B0E8036F1C0CD99A0065EBC0 /* TableOfContentsPresentationController.swift in Sources */, + 67C6F79C27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift in Sources */, + 67DC5BE323A017CA00B03A84 /* ArticleViewController.swift in Sources */, + B0C6BE401E4068C60033BD6E /* WMFLoginViewController.swift in Sources */, + 00FCB2C326D839A500F5A47A /* NotificationsCenterCellViewModel.swift in Sources */, + 7AE1D3391FCD10B900393471 /* SavedViewController.swift in Sources */, + 00D46DA52889B7F50015DE9B /* TalkPageView.swift in Sources */, + 83B01F9A23DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift in Sources */, + B0CD9DD71F70997300051843 /* WMFWelcomeAnimationExtensions.swift in Sources */, + 7A203F0B1FDEDCDD00A229EC /* ReadingListHintController.swift in Sources */, + B09B03F21CE0FB6300009083 /* PageHistoryFetcher.swift in Sources */, + 7ABE17182239B8EE006BA309 /* WelcomeContainerViewController.swift in Sources */, + 676C869326D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift in Sources */, + B04AE84C21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift in Sources */, + 836BF56E2869F9C200B98321 /* TalkPageViewController.swift in Sources */, + B0E8058D1C0CE2C60065EBC0 /* CreateAccountFunnel.m in Sources */, + 83927D811F705B7B00051890 /* SearchResultsViewController.swift in Sources */, + 67146036243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift in Sources */, + 7A82896821B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift in Sources */, + B0E8059D1C0CE2F50065EBC0 /* WMFShareFunnel.m in Sources */, + 6734F052227B634900BDDB94 /* ActionButton.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D844D9671D6CB2600042D692 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6779D45A2400822B002840CA /* MWKImageInfoFetcher.h in Sources */, + 0042807125E6E395004945B3 /* NSValueTransformer+MTLPredefinedTransformerAdditions.m in Sources */, + 0042809325E6E395004945B3 /* NSDictionary+MTLManipulationAdditions.m in Sources */, + D844D9C01D6CB7BA0042D692 /* MWKDataStore.m in Sources */, + D844482B1DDB632100425630 /* WMFArticle+CoreDataProperties.m in Sources */, + 837E619B2510E47400C67494 /* ArticleSummary.swift in Sources */, + 0042806D25E6E395004945B3 /* FLAnimatedImageView.m in Sources */, + 832A7A6023EAE03200D0A750 /* String+JavaScript.swift in Sources */, + D837B5A81F06E5C600DCB9CD /* DateFormatter+WikipediaLanguage.swift in Sources */, + D81EFDE41D775B6B0035F2EB /* SavedPageSpotlightManager.swift in Sources */, + D81EFDE21D775B140035F2EB /* NSUserActivity+WMFExtensions.m in Sources */, + 8320332122B90548004A9EDA /* NSManagedObjectContext+NavigationState.swift in Sources */, + 67D6C00C24058714005709B1 /* CacheItemMappingModel.xcmappingmodel in Sources */, + 67E9A11C25536B6F00C5ED31 /* ABTestsController.swift in Sources */, + 6798037224F99AB200D765AA /* SignificantEventsModels.swift in Sources */, + 7A9133AA22B162E8002AEBCF /* RemoteNotifications.xcdatamodeld in Sources */, + D8733C941ECA16940011E379 /* HasText.swift in Sources */, + B0016CBF2136105900FA1096 /* SetupButton.swift in Sources */, + 6798037324F99AB200D765AA /* SignificantEventsFetcher.swift in Sources */, + 7A65A5DC20ACFD98001170B8 /* WMFContentGroup+EventLogging.swift in Sources */, + B32536001EE87A6200372E93 /* EventRecord+CoreDataClass.swift in Sources */, + D8FA18BE1E1BD891009675C3 /* NSFileManager+WMFExtendedFileAttributes.m in Sources */, + D84B224E1DAFD0FC007C44AA /* WMFNotificationsController.m in Sources */, + 7A0312F92153DEB30095C953 /* RemoteNotificationsAPIController.swift in Sources */, + D826C51721766F1A0012F940 /* BackgroundFetcher.swift in Sources */, + D84C36401F3245A200895FA1 /* ArticleCollectionViewCell+Themeable.swift in Sources */, + 83D5EC871F755E1F003DE6F2 /* SwipeableCell.swift in Sources */, + B0B423481EF1FEE000D3DC4C /* WMFFeedOnThisDayEvent.m in Sources */, + 83EE477020D01A9A00A21F34 /* ExploreCardCollectionViewCell.swift in Sources */, + D84C36411F3245CD00895FA1 /* ArticleCollectionViewCell+WMFFeedContentDisplaying.swift in Sources */, + D84448591DDCE49D00425630 /* WMFContentGroup+CoreDataClass.m in Sources */, + 67A6F13A23BFEA0400736539 /* ImageFetcher.swift in Sources */, + 0042808825E6E395004945B3 /* NSArray+MTLManipulationAdditions.m in Sources */, + D8CC94DA217897FB007293E7 /* NSManagedObject+Extensions.swift in Sources */, + 831937E723E1CE80006A9FF3 /* String+LinkParsing.swift in Sources */, + 67DA31882720957B0035D40F /* RemoteNotificationsPagingOperation.swift in Sources */, + D8FA18B81E1BD891009675C3 /* NSDateFormatter+WMFExtensions.m in Sources */, + BA7FF0B61F618F5A0054CF02 /* CollectionViewEditController.swift in Sources */, + 672034E527A2600C007DC24F /* RemoteNotificationsProjectOperation.swift in Sources */, + 6761AEDF2704CF0000E47BAD /* WikimediaProject+RemoteNotifications.swift in Sources */, + D84C361F1F32404700895FA1 /* OnThisDayCollectionViewCell.swift in Sources */, + 702096B9256C3D5700E27041 /* SamplingController.swift in Sources */, + D826C51521766E570012F940 /* Collection+AsyncMap.swift in Sources */, + D8987E061E325D8A00E75DA6 /* QuadKey.swift in Sources */, + 67C9D58F28D3689F00629165 /* WMFLocalizedDateFormatStrings+Extensions.swift in Sources */, + D8FA18B11E1BD891009675C3 /* NSBundle+WMFInfoUtils.m in Sources */, + D8FA18FB1E1BDA4C009675C3 /* UIImage+WMFStyle.m in Sources */, + 8392E8681F55801B007E2EE2 /* NSTextAttachment+WMFExtras.swift in Sources */, + D8FA18F31E1BDA35009675C3 /* UIImageView+WMFContentOffset.m in Sources */, + D8FA18B61E1BD891009675C3 /* NSDictionary+WMFRequiredValueForKey.m in Sources */, + B0B423511EF32D2700D3DC4C /* WMFOnThisDayContentSource.m in Sources */, + D8FA18F81E1BDA4C009675C3 /* WMFArticlePreviewViewController.swift in Sources */, + 67146032243B885E008CE885 /* SurveyAnnouncementsController.swift in Sources */, + 67E2E4982504E2130070F12D /* TimelineView.swift in Sources */, + 7A0312F72153C4990095C953 /* RemoteNotificationsModelController.swift in Sources */, + 0042807A25E6E395004945B3 /* MTLJSONAdapter.m in Sources */, + D80A792A1F31E63C00EC06AB /* NSCharacterSet+WMFLinkParsing.m in Sources */, + D8543231218879D000E895B5 /* Configuration.swift in Sources */, + D8FA18AC1E1BD874009675C3 /* EXTScope.m in Sources */, + 67D6C01C2405A4FB005709B1 /* CacheItem+CoreDataClass.swift in Sources */, + 7A630F71217A400200FC93FC /* Array+Chunked.swift in Sources */, + 67F9AE4923AD2F38003D4F5E /* Array+SafeIndex.swift in Sources */, + D826C51B217741C50012F940 /* ReachabilityNotifier.swift in Sources */, + 0E728D391DAEEAD60074EB4B /* WMFLocationSearchFetcher.m in Sources */, + 8380753920DC7684000D222C /* ColumarCollectionViewLayoutSection.swift in Sources */, + B32536011EE87A6200372E93 /* EventRecord+CoreDataProperties.swift in Sources */, + D8BD63BF1EA7E28700BBC082 /* SummaryExtensions.swift in Sources */, + D84C36391F3241ED00895FA1 /* WMFGradientView.m in Sources */, + 83ACAA9E24E6D94C003B3035 /* MWKSearchResult+PageNamespace.swift in Sources */, + 0042809125E6E395004945B3 /* MTLTransformerErrorHandling.m in Sources */, + 67D6C0202405B3D2005709B1 /* CacheGroup+CoreDataClass.swift in Sources */, + 7A5357AB215552E7007998DC /* RemoteNotificationsOperation.swift in Sources */, + D84C361E1F32404700895FA1 /* NewsCollectionViewCell.swift in Sources */, + 00D4B1B4282996A2008C705C /* EchoModelVersion.swift in Sources */, + 00A988082829D92B006D800B /* PushNotificationContentIdentifier.swift in Sources */, + D844D9F61D6CC0440042D692 /* Cancellable.swift in Sources */, + 7A3AD05B20ADB1DF00C92E04 /* WMFCurrentlyLoggedInUserFetcher.swift in Sources */, + D89D44031D74D40100F7862E /* MWKSearchResult.m in Sources */, + 83D05189246EA70D00DA92C6 /* NSMutableAttributedString+Mutations.swift in Sources */, + 0042807825E6E395004945B3 /* MTLEXTScope.m in Sources */, + D8635AE8216E2BFC001A7C00 /* HTTPCookieStorage+Migration.swift in Sources */, + D8181FA52188DC1400FDEC59 /* String+Domains.swift in Sources */, + 8386BDED2386C269007EE89D /* WikipediaURLTranslations.swift in Sources */, + 7A79CCF2200C2C850099B01F /* BatchEditToolbarViewController.swift in Sources */, + D84448231DDB60FF00425630 /* WMFArticle+Extensions.m in Sources */, + D844DA071D6CC4D40042D692 /* MWKLanguageLinkController.m in Sources */, + 7A65A5DD20ACFDB6001170B8 /* EventLoggingStandardEventProviding.swift in Sources */, + 67F73383273C163700D7D713 /* TimeInterval+Extensions.swift in Sources */, + D844D9DC1D6CBBFA0042D692 /* NSString+WMFHTMLParsing.m in Sources */, + D8FA18D71E1BD899009675C3 /* NSURL+WMFExtras.m in Sources */, + D813FDA51EC34B2600FA4690 /* WMFArticle+Extensions.swift in Sources */, + D844DA011D6CC3C20042D692 /* MWKLanguageLink.m in Sources */, + 83ACAAAD24E6EED9003B3035 /* WikipediaSiteInfoLookup.swift in Sources */, + 6761AEEB270613B400E47BAD /* SharedContainerCache.swift in Sources */, + D844D9F71D6CC05F0042D692 /* ImageDownload.swift in Sources */, + 0E728D361DAEE8FF0074EB4B /* WMFRandomContentSource.m in Sources */, + 83CCB28A209CA4E600D31565 /* NSRegularExpression+HTML.m in Sources */, + 7A3AD05A20ADB1CD00C92E04 /* WMFAccountLogin.swift in Sources */, + D8733C921ECA16580011E379 /* UIView+SemanticContent.swift in Sources */, + 7A52C01B2150389D00A3A4A1 /* RemoteNotificationsController.swift in Sources */, + 0E728D451DAEEE880074EB4B /* CLLocation+WMFBearing.m in Sources */, + 832B2B8423D9F9420087EB5F /* NSRegularExpression+Utilities.swift in Sources */, + D8AC391E1D6F2328007E3C14 /* UIScreen+WMFImageWidth.m in Sources */, + D84C35F51F323CE800895FA1 /* SaveButton.swift in Sources */, + 678C7C2E23BE705C001AC4D5 /* CacheDBWriting.swift in Sources */, + D84C36171F32402000895FA1 /* AnnouncementCollectionViewCell.swift in Sources */, + D85F56A2219C45C900AF3E13 /* URLComponents+Extensions.swift in Sources */, + D84448291DDB632100425630 /* WMFArticle+CoreDataClass.m in Sources */, + 7A2432ED1FCF469100FB4BA5 /* SetupView.swift in Sources */, + 833D4FFB20A9E20800B44E7C /* String+HTML.swift in Sources */, + 67146034243B8B4F008CE885 /* AnnouncementType.swift in Sources */, + 6761AEDA2704BA3800E47BAD /* RemoteNotification+CoreDataClass.swift in Sources */, + B019FED02029347300BDE9C9 /* UIStackView+SubviewVerification.swift in Sources */, + 6779618D29245BF300C2A65F /* PageIDToURLFetcher.swift in Sources */, + 83C643582239508600FC16BF /* RandomArticleFetcher.swift in Sources */, + D87647481F1F9C2500D02CA4 /* CommonStrings.swift in Sources */, + D84B22541DAFD1E1007C44AA /* CIDetector+WMFFaceDetection.m in Sources */, + D8FA18D91E1BD899009675C3 /* NSURL+WMFLinkParsing.m in Sources */, + D84C363D1F32438B00895FA1 /* SizeThatFitsView.swift in Sources */, + 7A5A2777206D288C004CC837 /* NSFileManager+DirectorySize.swift in Sources */, + D84C35F41F323CD100895FA1 /* ArticleRightAlignedImageCollectionViewCell.swift in Sources */, + 0E728D381DAEEAD60074EB4B /* WMFLocationSearchResults.m in Sources */, + 0E728D341DAEE8FF0074EB4B /* WMFFeedContentSource.m in Sources */, + D84B22521DAFD1E1007C44AA /* CIContext+WMFImageProcessing.m in Sources */, + 672034E327A2531F007DC24F /* RemoteNotificationsReauthenticateOperation.swift in Sources */, + D81A28BE231E8F4C001CC77D /* ExtensionViewController.swift in Sources */, + 8330531F23EF051900123141 /* NSArray+WMFMapping.m in Sources */, + 006694FC265D9F2900E23AE4 /* WidgetSettings.swift in Sources */, + 0E728D1D1DAEE2B50074EB4B /* WMFFeedTopReadResponse.m in Sources */, + 670AF1CF26CD74A6005F76D0 /* EchoSubscriptionFetcher.swift in Sources */, + 7A0312FF215422960095C953 /* RemoteNotificationsMarkReadOrUnreadOperation.swift in Sources */, + B0B4237A1F0211AB00D3DC4C /* WMFFeedArticlePreview+DescriptionOrSnippet.swift in Sources */, + D8FA18BC1E1BD891009675C3 /* NSFileManager+WMFGroup.m in Sources */, + D844D97D1D6CB29B0042D692 /* MWKDataObject.m in Sources */, + 678C7C3623BE7779001AC4D5 /* FileManager+CacheExtensions.swift in Sources */, + 0E728D471DAEEE880074EB4B /* CLLocation+WMFComparison.m in Sources */, + 670AF1CE26CA188B005F76D0 /* RemoteNotificationLinks.swift in Sources */, + 678C7C3423BE75F9001AC4D5 /* CacheFileWriterHelper.swift in Sources */, + 8338AF8E21F7B33E000C4055 /* WMFLegacyFetcher.m in Sources */, + 6771299D24FF8CC000E89CA5 /* ArticleAsLivingDocViewModels.swift in Sources */, + 8330532223EF05D000123141 /* WMFBlocksKit.swift in Sources */, + D844D9B91D6CB7980042D692 /* MWKRecentSearchList.m in Sources */, + D837B5A61F06AA8C00DCB9CD /* Theme.swift in Sources */, + D8619BA51FBB10240045C8BC /* ReadingList+CoreDataProperties.swift in Sources */, + 7A96EBA922CFDA4B0037C8A8 /* PageNamespace.swift in Sources */, + 7A5AB82D2294121D00B91C9C /* WMFHTMLElement.m in Sources */, + D8EBD1BC1FBB177D00AA7DA9 /* ReadingListEntry+JSON.swift in Sources */, + 0E728D321DAEE8FF0074EB4B /* WMFContinueReadingContentSource.m in Sources */, + D8FA18F91E1BDA4C009675C3 /* UIView+WMFDefaultNib.m in Sources */, + D84C361D1F32404700895FA1 /* SideScrollingCollectionViewCell.swift in Sources */, + 0042809225E6E395004945B3 /* MTLReflection.m in Sources */, + 67D6C0212405B3D2005709B1 /* CacheGroup+CoreDataProperties.swift in Sources */, + 535F16D625CE11A300875AAD /* MWKDataStore+LanguageVariantMigration.swift in Sources */, + 83ACAAA224E6E38A003B3035 /* Wikipedia.swift in Sources */, + 6779D45323F6EC2D002840CA /* CacheFetching.swift in Sources */, + 678D29AC2729EAD20036C5D9 /* RemoteNotification+CoreDataProperties.swift in Sources */, + D844D9C21D6CB7D20042D692 /* MWKImageInfo.m in Sources */, + 67D6C01D2405A4FB005709B1 /* CacheItem+CoreDataProperties.swift in Sources */, + D82CA32F2020E87D005C2D5C /* ReadingListsOperation.swift in Sources */, + 0042809025E6E395004945B3 /* NSValueTransformer+MTLInversionAdditions.m in Sources */, + 8359BAC721E4C9C1009B5E6C /* Fetcher.swift in Sources */, + 0E728D211DAEE2B50074EB4B /* WMFFeedImage.m in Sources */, + D8FA18D31E1BD891009675C3 /* WMFTaskGroup.m in Sources */, + D801C93D1EB9404A001FA294 /* WMFLocalization.m in Sources */, + 0E728D301DAEE8FF0074EB4B /* WMFNearbyContentSource.m in Sources */, + 67A6F14023BFF62300736539 /* ImageCacheController.swift in Sources */, + D84B22501DAFD15A007C44AA /* WMFFaceDetectionCache.m in Sources */, + 0E728D2C1DAEE8FF0074EB4B /* WMFRelatedPagesContentSource.m in Sources */, + 6761AEF327065DE400E47BAD /* WMFNotificationsController+Extensions.swift in Sources */, + D8619BA41FBB10240045C8BC /* ReadingList+CoreDataClass.swift in Sources */, + D8E892252176124F00587F61 /* PeriodicWorker.swift in Sources */, + 0E8768371DDE002C00B8CACD /* WMFAnnouncementsContentSource.m in Sources */, + 83A6D44325100BEE00F9F909 /* Bundle+IsAppExtension.swift in Sources */, + 7AE5248D21383D9C00CDC817 /* WikidataFetcher.swift in Sources */, + A4C558BD2403D74100AFBFDC /* LocationManagerProtocol.swift in Sources */, + 8383446C1F62EBD000BD5A37 /* UIView+Constraints.swift in Sources */, + 83E880E823EB19270087223F /* MediaList.swift in Sources */, + D89845221ECB3F6C00849DA4 /* CGRect+Layout.swift in Sources */, + 83E9C45B2419193C006BDBC2 /* WikipediaSiteInfo.swift in Sources */, + D844485B1DDCE49D00425630 /* WMFContentGroup+CoreDataProperties.m in Sources */, + D8FA18F51E1BDA3C009675C3 /* WMFSparklineView.swift in Sources */, + B0B4234D1EF2055200D3DC4C /* WMFOnThisDayEventsFetcher.m in Sources */, + 83B87ECC1F71431F00F342F1 /* ArticleCollectionViewCell+ListDisplay.swift in Sources */, + BA7FF0B41F6188C70054CF02 /* CollectionViewCellActionsView.swift in Sources */, + D8D365151E953C7100593A38 /* ImageControllerCompletionManager.swift in Sources */, + D8FA18FD1E1BDA4C009675C3 /* UIImageView+WMFImageFetching.m in Sources */, + D8FA18FE1E1BDA4C009675C3 /* UIColor+WMFStyle.m in Sources */, + D84C36161F32401B00895FA1 /* RankedArticleCollectionViewCell.swift in Sources */, + D8AAF6B81FE93DE9005760E6 /* UIScrollView+Limits.swift in Sources */, + D8FA18AE1E1BD891009675C3 /* NSDictionary+WMFPageViewsSortedByDate.m in Sources */, + 0062597324DE0A2500C95037 /* WidgetController.swift in Sources */, + 8321FCCA23871D8F0079F3C7 /* Router.swift in Sources */, + D81930DB1E9F97B200554B19 /* WMFExploreFeedContentController.m in Sources */, + D8FA18F71E1BDA4C009675C3 /* GroupedAccessibilityView.swift in Sources */, + D8E78FA41FB4C8250094B968 /* ReadingListsController.swift in Sources */, + 982800D624D302BF004B1850 /* EventPlatformClient.swift in Sources */, + 83A1561420DBE08C0052487B /* ColumnarCollectionViewLayout.swift in Sources */, + 6779618F29246BC900C2A65F /* NSUserActivity+Extensions.swift in Sources */, + 831937E923E1CEAC006A9FF3 /* CharacterSet+LinkParsing.swift in Sources */, + D8FA18DB1E1BD899009675C3 /* NSURLComponents+WMFLinkParsing.m in Sources */, + D8FA18C21E1BD891009675C3 /* NSURL+WMFQueryParameters.m in Sources */, + D8FA18C71E1BD891009675C3 /* NSIndexSet+BKReduce.m in Sources */, + D8CE9B031FDEBB1900AE7D49 /* NavigationBar.swift in Sources */, + 67A7CA7528665CEF008D4BF6 /* HTTPStatusCode.swift in Sources */, + 6773B1FE240F02E40022A70E /* PermanentlyPersistableURLCache.swift in Sources */, + 70B7983625758EB800C10BCA /* EPEventRecord+CoreDataProperties.swift in Sources */, + 678C7C3023BE7319001AC4D5 /* CacheDBWriterHelper.swift in Sources */, + 67F73388273C26A000D7D713 /* NotificationServiceHelper.swift in Sources */, + D84C363E1F32441800895FA1 /* WMFDynamicTypeExtentions.swift in Sources */, + 6761AEEF2706249300E47BAD /* PushNotificationsCache.swift in Sources */, + D8F36F031EEEBA130087D4DD /* Licenses.swift in Sources */, + 0042807925E6E395004945B3 /* MTLEXTRuntimeExtensions.m in Sources */, + 0E728D231DAEE2B50074EB4B /* WMFFeedNewsStory.m in Sources */, + D844D9981D6CB5CD0042D692 /* WikipediaAppUtils.m in Sources */, + 7A45AB8020AB2A4C006A92F5 /* Dictionary+Equality.swift in Sources */, + D84C363C1F32428A00895FA1 /* CircledRankView.swift in Sources */, + 678F512A23A7EE5100CE5357 /* ArticleCacheDBWriter.swift in Sources */, + D82972941E4361BF0061550A /* WMFKeyValue+CoreDataClass.m in Sources */, + 83A933472514C491006EB48A /* WMFCrossProcessCoreDataSynchronizer.m in Sources */, + D84C361B1F32403D00895FA1 /* OnThisDayCollectionViewCell+WMFFeedContentDisplaying.swift in Sources */, + D8FA18C91E1BD891009675C3 /* WMFGeometry.c in Sources */, + D8EBD1B81FBB13EE00AA7DA9 /* ReadingList+JSON.swift in Sources */, + D84C36201F32404700895FA1 /* OnThisDayExploreCollectionViewCell.swift in Sources */, + D84C361C1F32403D00895FA1 /* OnThisDayExploreCollectionViewCell+WMFFeedContentDisplaying.swift in Sources */, + 0066BE30265EC4A900512BE8 /* WidgetFeaturedContent.swift in Sources */, + 7A0312FB215402FD0095C953 /* RemoteNotificationsImportOperation.swift in Sources */, + D82C3A99213451100073EEAC /* DeviceInfo.swift in Sources */, + 6773B2022411D8600022A70E /* ArticleCacheDBWriter+SyncResources.swift in Sources */, + D80ACD291EA0DD0000DC3F20 /* FLAnimatedImage+SafeForSwift.m in Sources */, + D8FA18F21E1BDA35009675C3 /* UIImageView+WMFImageFetchingInternal.m in Sources */, + 0E728D251DAEE2B50074EB4B /* WMFFeedContentFetcher.m in Sources */, + 83F1095B23D07E5D003F3E9E /* APIURLComponentsBuilder.swift in Sources */, + D84C35F11F323CCA00895FA1 /* CollectionViewCell.swift in Sources */, + 67A6F13823BFB75300736539 /* ImageCacheDBWriter.swift in Sources */, + A4C558BF2403D7E300AFBFDC /* LocationManager.swift in Sources */, + D8FA18F41E1BDA35009675C3 /* UIImage+WMFNormalization.m in Sources */, + D8CD97651E83FAB400ECCA9D /* Cache.xcdatamodeld in Sources */, + 6779D45924007AF0002840CA /* MWKImageInfoFetcher.m in Sources */, + 70B79820257577B800C10BCA /* StorageManager.swift in Sources */, + 831C15C62099EB3A001B04BF /* WMFArticle+Errors.swift in Sources */, + 6739A182273061220063E0E0 /* RemoteNotificationsMarkAllAsReadOperation.swift in Sources */, + 7A0F2589217221D10028871B /* RepeatingTimer.swift in Sources */, + D844D9B51D6CB77D0042D692 /* MWKSavedPageList.m in Sources */, + D84C35F31F323CD100895FA1 /* ArticleFullWidthImageCollectionViewCell.swift in Sources */, + 83222DB41F8E554800338BE5 /* WMFContent+CoreDataProperties.m in Sources */, + 006694FE265D9F3A00E23AE4 /* WidgetCache.swift in Sources */, + 0042808F25E6E395004945B3 /* NSDictionary+MTLJSONKeyPath.m in Sources */, + 67FF9C6B28076ADA000963D1 /* NSError+Utilities.swift in Sources */, + 83E9A2121F56FE5E006EB091 /* FakeProgressController.swift in Sources */, + D84C36441F3245E600895FA1 /* WMFContentGroup+WMFFeedContentDisplaying.m in Sources */, + D8FA18F11E1BDA35009675C3 /* UIImage+WMFImageProcessing.m in Sources */, + D8FA18E71E1BD8AF009675C3 /* NSProcessInfo+WMFOperatingSystemVersionChecks.m in Sources */, + 7A3159CF206458B000143119 /* ReadingListAlertType.swift in Sources */, + 67E5DA6B276416A600CE827D /* RemoteNotificationsRefreshCrossWikiOperation.swift in Sources */, + 7A3AD05820ADB1A900C92E04 /* WMFAuthenticationManager.swift in Sources */, + D87F1D3D1EC0ACC400575CF8 /* AsyncOperation.swift in Sources */, + D8650B7C20350FEE0044DFFA /* NSString+SHA256.m in Sources */, + D8FA18BA1E1BD891009675C3 /* NSDate+WMFRelativeDate.m in Sources */, + D844D99D1D6CB61B0042D692 /* NSString+WMFExtras.m in Sources */, + D837B5B21F0D68B800DCB9CD /* URL+LinkParsing.swift in Sources */, + 8387CE8824C8C70A00439D93 /* WMFSecureUnarchiveFromDataTransformer.swift in Sources */, + 67A6F13E23BFEF4200736539 /* ArticleCacheController.swift in Sources */, + 67D6C00A240581ED005709B1 /* CacheItemMigrationPolicy.swift in Sources */, + D80ED2591EE178A800CE8C50 /* Gradient.swift in Sources */, + 7004A5BA268CEE680029C46B /* MetricsClientBridge.swift in Sources */, + D844DA0A1D6CC5240042D692 /* NSLocale+WMFExtras.swift in Sources */, + 0042806C25E6E395004945B3 /* FLAnimatedImage.m in Sources */, + 830177FA1FBF3E490005681C /* ReadingListsAPIController.swift in Sources */, + D84C35F61F323CF000895FA1 /* AlignedImageButton.swift in Sources */, + D84C361A1F32403D00895FA1 /* NewsCollectionViewCell+WMFFeedContentDisplaying.swift in Sources */, + 7AEF527120ADD74D00DDF791 /* WMFCaptcha.swift in Sources */, + 678C7C2A23BE67F0001AC4D5 /* CacheController.swift in Sources */, + D8619BA61FBB10240045C8BC /* ReadingListEntry+CoreDataClass.swift in Sources */, + 834400B020B3368A005F087D /* NSCharacterSet+WMFExtras.m in Sources */, + D837B5AA1F0D0D1600DCB9CD /* WMFFeedOnThisDayEvent+LocalizedDates.swift in Sources */, + D844480F1DDA33D900425630 /* Wikipedia.xcdatamodeld in Sources */, + D84C35F21F323CD100895FA1 /* ArticleCollectionViewCell.swift in Sources */, + 83CDC7D425122A1700A2F8A1 /* PermanentCacheController.swift in Sources */, + 0E87683B1DDE00D600B8CACD /* WMFAnnouncementsFetcher.m in Sources */, + 67DAEDA123CD1BC9003AA208 /* CacheGatekeeper.swift in Sources */, + D881B1131E32874500D33F62 /* WMFArticle+QuadKey.swift in Sources */, + 00669500265DA01000E23AE4 /* WidgetContentFetcher.swift in Sources */, + 0015712C27D92F6B00F1EB26 /* RetryBlockTask.swift in Sources */, + D8E78FA61FB4C8740094B968 /* Session.swift in Sources */, + D8FA18D51E1BD891009675C3 /* NSError+WMFExtensions.m in Sources */, + 8320332322B906A0004A9EDA /* NavigationState.swift in Sources */, + 83222DB31F8E554800338BE5 /* WMFContent+CoreDataClass.m in Sources */, + 7A06020E20EAAF5A00FBB71D /* ExploreFeedPreferencesUpdateCoordinator.swift in Sources */, + 83DB0A5723EEDE2100DA5F58 /* MobileviewToMobileHTMLConverter.swift in Sources */, + 0042808A25E6E395004945B3 /* NSDictionary+MTLMappingAdditions.m in Sources */, + D8726D431EBA052900A107D0 /* Localization.swift in Sources */, + 0E8768401DDE012300B8CACD /* WMFAnnouncement.m in Sources */, + 70B798142575714100C10BCA /* EventPlatformEvents.xcdatamodeld in Sources */, + D8DC16F31D6F6F2C00D6D9FB /* NSUserDefaults+WMFExtensions.swift in Sources */, + 0E728D1B1DAEE2B50074EB4B /* WMFFeedDayResponse.m in Sources */, + 0E728D461DAEEE880074EB4B /* NSString+WMFDistance.m in Sources */, + 8380753720DC7481000D222C /* ColumnarCollectionViewLayoutInfo.swift in Sources */, + D88E0E1D1EBB5A97005B8E9E /* Bundle.swift in Sources */, + 7AEF527320ADF07100DDF791 /* KeychainCredentialsManager.swift in Sources */, + 6779D45123F60903002840CA /* CacheFileWriter.swift in Sources */, + 83ACAAAB24E6E745003B3035 /* WikipediaLookup.swift in Sources */, + 6761AEED2706247800E47BAD /* PushNotificationsSettings.swift in Sources */, + B3632E7F1EE5F98C007A2464 /* EventLoggingService.swift in Sources */, + D8FA18F61E1BDA3F009675C3 /* UIFont+WMFDynamicType.swift in Sources */, + 7A3AD05920ADB1BD00C92E04 /* WMFAuthLoginInfoFetcher.swift in Sources */, + 67BEFFDE28AEDF5200606B38 /* WikimediaProject.swift in Sources */, + 0042808E25E6E395004945B3 /* NSObject+MTLComparisonAdditions.m in Sources */, + 6713519D277285B7006C07D9 /* RemoteNotificationsRefreshDeadlineController.swift in Sources */, + 830177FC1FBF3EF70005681C /* NSManagedObjectContext+WMFUtilities.swift in Sources */, + 8380754520DE627E000D222C /* WMFContentGroup+Display.swift in Sources */, + D844485F1DDCE4E500425630 /* WMFContentGroup+Extensions.m in Sources */, + 7A3AD05C20ADB1F500C92E04 /* WMFKeychainCredentials.swift in Sources */, + D880652F218C732800BF7B91 /* WorkerController.swift in Sources */, + 6773B2042411DCF50022A70E /* ArticleCacheResourceDBWriting.swift in Sources */, + 0E728D1F1DAEE2B50074EB4B /* WMFFeedArticlePreview.m in Sources */, + D83FA6B61D74CDE6008CAB00 /* EventLoggingFunnel.m in Sources */, + D8FA18D11E1BD891009675C3 /* WMFMath.m in Sources */, + 678F512B23A7EE6600CE5357 /* ArticleFetcher.swift in Sources */, + 0042808925E6E395004945B3 /* MTLModel.m in Sources */, + 0042807B25E6E395004945B3 /* MTLModel+NSCoding.m in Sources */, + D8FA18B41E1BD891009675C3 /* NSCalendar+WMFCommonCalendars.m in Sources */, + B085536C2399E368002100F8 /* UIAccessibility+Grouping.swift in Sources */, + 83A8E34221A431F100B3FF82 /* WMFLegacySerializer.m in Sources */, + 83DB0A5E23EEDE4400DA5F58 /* LegacyArticle.swift in Sources */, + D844D9801D6CB3310042D692 /* MWKSiteDataObject.m in Sources */, + D8733C8B1ECA10930011E379 /* LabelGroupAccessibilityElement.swift in Sources */, + D8E2B0F31D6CC5DE006FFB24 /* WMFImageURLParsing.m in Sources */, + D8619BA71FBB10240045C8BC /* ReadingListEntry+CoreDataProperties.swift in Sources */, + 7ADB2A0E1FD1E96300B84818 /* BatchEditSelectView.swift in Sources */, + D8CE9B041FDEBB2C00AE7D49 /* NavigationBarHider.swift in Sources */, + B32535F11EE856FF00372E93 /* EventLogging.xcdatamodeld in Sources */, + 70B7982B25758E6D00C10BCA /* EPEventRecord+CoreDataClass.swift in Sources */, + D844D9A71D6CB7280042D692 /* MWKList.m in Sources */, + 8380753B20DC7D04000D222C /* ColumnarCollectionViewLayoutMetrics.swift in Sources */, + D82CA3332020E8D8005C2D5C /* ReadingListsSyncOperation.swift in Sources */, + 8387CE9024C99C2600439D93 /* WMFMTLModel.m in Sources */, + 007B5FC526FA40F100180FF8 /* RemoteNotificationType.swift in Sources */, + 6761AEF52707BE4200E47BAD /* RemoteNotificationsRefreshOperation.swift in Sources */, + 7AD5D453223874F600C01164 /* RelatedSearchFetcher.swift in Sources */, + 8386BDF12386D3E1007EE89D /* RequestError.swift in Sources */, + 67540CA924D221E3008B2894 /* LocationManagerFactory.swift in Sources */, + D844D9F31D6CC0220042D692 /* MWKLicense.m in Sources */, + 0042808D25E6E395004945B3 /* MTLValueTransformer.m in Sources */, + D85BD2471F8F9D6900D0D478 /* NSManagedObjectContext+WMFKeyValue.m in Sources */, + 836944DC1F572452007BD6DA /* ThemeableTextField.swift in Sources */, + D82972951E4361C60061550A /* WMFKeyValue+CoreDataProperties.m in Sources */, + D8C41DDB23FC09EE00353DCE /* NSManagedObjectContext+History.swift in Sources */, + 67F1375E23C986CD00512B61 /* CacheTaskTracking.swift in Sources */, + 67C9D59128D36BDD00629165 /* WMFFeedNewsStory+LocalizedStrings.swift in Sources */, + 834F47F42833D91F00F86C80 /* RemoteNotificationFilterType.swift in Sources */, + 835A042D223AD63000D4D758 /* ArticleSummaryController.swift in Sources */, + D844D9B81D6CB7980042D692 /* MWKRecentSearchEntry.m in Sources */, + 67B7E77E2988777A00708A81 /* MediaWikiApiErrors.swift in Sources */, + D8FA18AF1E1BD891009675C3 /* NSNumberFormatter+WMFExtras.swift in Sources */, + D844DA091D6CC4D40042D692 /* MWKLanguageFilter.m in Sources */, + 67F1A180286F34A5000D0F74 /* FeatureFlags.swift in Sources */, + 0042807225E6E395004945B3 /* NSError+MTLModelException.m in Sources */, + 7A03130321542F5C0095C953 /* RemoteNotificationsOperationsController.swift in Sources */, + 0E728D371DAEEAD60074EB4B /* MWKLocationSearchResult.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D870215C1EBA63EE000D02D6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83A72BBF24E70BB200732493 /* localization.swift in Sources */, + 83ACAAA724E6E655003B3035 /* main.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8A42A561E815A9C00D8E281 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B0524B2C214854E900D8FD8D /* DescriptionWelcomePanelViewController.swift in Sources */, + 7A1C4992227254EC00230ED2 /* InsertMediaSearchViewController.swift in Sources */, + D8A42A571E815A9C00D8E281 /* UserLocationAnnotationView.swift in Sources */, + 672D69A7273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift in Sources */, + 6798331D22C174F00073CE6F /* LinkOnlyTextView.swift in Sources */, + 00E75B6027EB874A00A45B78 /* NotificationsCenterDetailViewController.swift in Sources */, + D8A42A581E815A9C00D8E281 /* WMFSearchFunnel.m in Sources */, + 8368BB8724129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift in Sources */, + 00A7946E245CA4E60063BA18 /* ArticleSurveyTimerController.swift in Sources */, + 41FCAA3921C844CB001D8411 /* ReadingListEntryCollectionViewController.swift in Sources */, + 672C35EE22D8E7D2007B8D46 /* EmptyViewController.swift in Sources */, + 7AB6F10222AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift in Sources */, + 6771299724FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift in Sources */, + 0010F93C27A49C7700D77848 /* HorizontalSpacerView.swift in Sources */, + D8A42A591E815A9C00D8E281 /* WMFImageURLActivitySource.swift in Sources */, + 7A27EDA52279F5270010CB24 /* InsertLinkViewController.swift in Sources */, + 671DF9C825F2AE4F0011799E /* ShortDescriptionController.swift in Sources */, + 00FCCBD22900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift in Sources */, + B0CD9DF01F70997500051843 /* WMFWelcomeLanguagesAnimationView.swift in Sources */, + 67B64D5A2507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift in Sources */, + 6782DBA02343B7DB003FA21B /* DiffHeaderTitleView.swift in Sources */, + 6730FD1128998EFD000E5F40 /* TalkPageReplyComposeContentView.swift in Sources */, + 830D71D21F704DD40080078B /* ArticleFetchedResultsViewController.swift in Sources */, + 83AE1C831F34BB5A004B62E0 /* ImageDimmingExampleViewController.swift in Sources */, + 67DC5BEC23A03FE700B03A84 /* ArticleToolbarController.swift in Sources */, + B0EFCD701EBF12E5008F36E5 /* LibrariesUsed.swift in Sources */, + B0CD9DEB1F70997500051843 /* WMFWelcomeAnimationView.swift in Sources */, + B0524B72214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift in Sources */, + 830D71C61F703C980080078B /* ArticleURLListViewController.swift in Sources */, + 7A7AC84921B6B89B003B849B /* SectionEditorViewController.swift in Sources */, + B01E54B2206479CC00374FEE /* ProgressContainer.swift in Sources */, + 83987AD320E4FB2C00C92C60 /* UISearchBar+Theme.swift in Sources */, + D8A42A5E1E815A9C00D8E281 /* WMFCaptchaResetter.swift in Sources */, + 006ABEEB2901E8F600722DF8 /* VanishAccountWarningView.swift in Sources */, + 67146039243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift in Sources */, + 678D79FF235E59B2006161FF /* DiffListUneditedViewModel.swift in Sources */, + 00474A3228DD1B13002E3C09 /* TalkPageCoffeeRollView.swift in Sources */, + 00E75B7927EB946D00A45B78 /* ReusableCell.swift in Sources */, + 00E2EA9126E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift in Sources */, + B0CD9DF11F70997500051843 /* WMFWelcomeAnalyticsAnimationView.swift in Sources */, + 00AA5AAF276BF2AE005295B0 /* TextBarButtonItem.swift in Sources */, + 67C6F79527E8C03A00B9C864 /* NotificationsCenterAction.swift in Sources */, + 7A420DB722A029780005689B /* EditFunnel.swift in Sources */, + 00E75B6527EB87DC00A45B78 /* NotificationsCenterDetailView.swift in Sources */, + B0B4236B1EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift in Sources */, + D8A42A621E815A9C00D8E281 /* PlacesViewController.swift in Sources */, + B0ACB13621265B9D0078C136 /* WMFImageGalleryDescriptionTextView.swift in Sources */, + 8356116028D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift in Sources */, + 67C9FC0228C77F350065A530 /* TalkPageTopicComposeViewController.swift in Sources */, + 8382F8D620D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift in Sources */, + B0C7A0811F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift in Sources */, + 67DDD18A250C1A28006C0F93 /* ThreeLineHeaderView.swift in Sources */, + 53A575FD2602C845009835E6 /* WMFAppViewController+Extensions.swift in Sources */, + 678D79ED235E595A006161FF /* DiffListChangeItemViewModel.swift in Sources */, + 00E75B6F27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift in Sources */, + D8A42A641E815A9C00D8E281 /* WMFAuthButton.swift in Sources */, + 679A24062968DBFC008D7686 /* ShiftingNavigationBarView.swift in Sources */, + B0432347210680A900A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift in Sources */, + 7A20AE0B2057F39C005FB5DF /* UIView+Identifier.swift in Sources */, + D8A42A651E815A9C00D8E281 /* WMFReferencePanelViewController.swift in Sources */, + 676F392B2745FB2000F4D33D /* NotificationsCenterFiltersViewModel.swift in Sources */, + 83F1097123D0E787003F3E9E /* RandomArticleViewController.swift in Sources */, + 7A715664226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift in Sources */, + 7A29A5D11F6C49C600E8F42B /* CollectionViewUpdater.swift in Sources */, + D8A42A681E815A9C00D8E281 /* WikidataFetcher+Places.swift in Sources */, + D8A42A691E815A9C00D8E281 /* UIButton+WMFButton.m in Sources */, + D8A42A6A1E815A9C00D8E281 /* TableOfContentsAnimator.swift in Sources */, + D8A42A6D1E815A9C00D8E281 /* WMFReferencePageBackgroundView.swift in Sources */, + 7A13A89C2028BB3600F28254 /* ReadingListsAlertController.swift in Sources */, + 0042811C25E6E841004945B3 /* NYTPhotosOverlayView.m in Sources */, + 7A610CC0220A582A00C266AE /* HintController.swift in Sources */, + 8321FCCF2387231E0079F3C7 /* ViewControllerRouter.swift in Sources */, + D850A53D1F8686DE006FD295 /* WMFThemeableNavigationController.m in Sources */, + FF59DF502555E0CB0048E66C /* InternalLinkPreviewing.swift in Sources */, + 83EDC4C428B424B6007D0192 /* VanishAccountPopUpAlertView.swift in Sources */, + D8A42A6F1E815A9C00D8E281 /* UIApplication+RTL.swift in Sources */, + 672285732540B56D0038E332 /* ReferenceBackLinksViewControllerDelegate.swift in Sources */, + 6780CF2B2967690200D45927 /* TalkPageArchivesView.swift in Sources */, + 67C6F7AE27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift in Sources */, + 83510B0A28F4CF6400B6235B /* TalkPageErrorStateView.swift in Sources */, + D8A42A721E815A9C00D8E281 /* WMFWelcomePanelViewController.swift in Sources */, + 7A1469C8220BC223000A20F1 /* EditHintController.swift in Sources */, + 67C6F7A927E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift in Sources */, + 83023C2220E6584F00EC7592 /* SearchTransition.swift in Sources */, + 007CCF0426D5A10200D5EA7C /* NotificationsCenterViewController.swift in Sources */, + 7A610CBA220A30C900C266AE /* HintViewController.swift in Sources */, + 7A23CED3211A24FF00441A79 /* FeedFunnel.swift in Sources */, + 677129A324FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift in Sources */, + 83DAA9B323FEB611002D5716 /* ReferenceBackLinksViewController.swift in Sources */, + D8A42A751E815A9C00D8E281 /* UIViewController+WMFAlerts.swift in Sources */, + 679A24012968DAB9008D7686 /* ShiftingTopView.swift in Sources */, + 7A49A20421231510005C574C /* CollectionViewFooter.swift in Sources */, + 67CEF26C2351111D00D5CA6C /* DiffNetworkModels.swift in Sources */, + 83B4CDC220E3DCD6007D5A6E /* SearchViewController.swift in Sources */, + 67D3C456228CB54E001D5741 /* OldTalkPageReplyComposeView.swift in Sources */, + 7AC19E352301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift in Sources */, + 41CCB67721CC1F9700206B47 /* SavedArticlesCollectionViewController.swift in Sources */, + 0072991828AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift in Sources */, + 6780D5BD237AF8A10087A5D1 /* DiffToolbarView.swift in Sources */, + 8350FC4F20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift in Sources */, + 679A23FC2968D865008D7686 /* ShiftingTopViewsData.swift in Sources */, + D818D3AE1ED87E8F0076110D /* ArticleLocationCellUpdating.swift in Sources */, + D8A42A761E815A9C00D8E281 /* NSDate+WMFPOTDTitle.m in Sources */, + 67C6F78127E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift in Sources */, + 83E3E7282440F1FE00AA2E9A /* LoadingAnimationViewController.swift in Sources */, + 007CCF0A26D5A17200D5EA7C /* NotificationsCenterView.swift in Sources */, + 8382F8CA20D844C800AE5250 /* ArticleLocationCollectionViewCell.swift in Sources */, + 67112E40275E603B007A9850 /* NotificationsCenterInboxViewModel.swift in Sources */, + B0B423641EF9D6A400D3DC4C /* OnThisDayViewController.swift in Sources */, + D8A42A771E815A9C00D8E281 /* WMFTableHeaderFooterLabelView.m in Sources */, + 7A6CA2912289AF2200C7FD47 /* EditLinkViewController.swift in Sources */, + 7AEC985C219F529000BEF62B /* DefaultEditToolbarView.swift in Sources */, + B0524B4A214854E900D8FD8D /* DescriptionWelcomePageViewController.swift in Sources */, + 7A6ED51620ADBF950001849F /* UserHistoryFunnel.swift in Sources */, + 009C8EC529071E720056A3AC /* NSString+Range.swift in Sources */, + 6761AEE42704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift in Sources */, + 67C78F7428B6DA1400AC207A /* SwiftUITextView.swift in Sources */, + 7A6ED52020ADBF950001849F /* LoginFunnel.swift in Sources */, + D8A42A7C1E815A9C00D8E281 /* WMFDatabaseHousekeeper.swift in Sources */, + 7A393284236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift in Sources */, + B3F21D121EB0EBB4000ED0BB /* PlaceSearchService.swift in Sources */, + 7A84224A2268BBE70074648E /* InsertMediaImageInfoView.swift in Sources */, + 7AFC79FB21B0367700BB0C50 /* TextFormattingTableViewController.swift in Sources */, + D8A42A7E1E815A9C00D8E281 /* PlaceSearch.swift in Sources */, + 6782DBA62343B7EE003FA21B /* DiffHeaderSummaryView.swift in Sources */, + D8A42A801E815A9C00D8E281 /* PlaceSearchSuggestionController.swift in Sources */, + 830378432940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift in Sources */, + 7AE99B2B21CC4F420092BE7F /* TextSizeFormattingTableViewController.swift in Sources */, + 7A71566A226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift in Sources */, + D8A42A821E815A9C00D8E281 /* WMFPageHistoryRevision.m in Sources */, + 00FCCBC8290082C200C9ECD2 /* TalkPageFindInPageState.swift in Sources */, + D8A42A891E815A9C00D8E281 /* WMFEmptyView.m in Sources */, + 678E7E8426432F060005439C /* NavigationEventsFunnel.swift in Sources */, + 830ECAD21FBDD8C00080B1EF /* ReadingListsViewController.swift in Sources */, + 83ACF8E828E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift in Sources */, + D8A42A8C1E815A9C00D8E281 /* WMFSettingsTableViewCell.m in Sources */, + D8A42A8D1E815A9C00D8E281 /* UIView+WMFSubviews.swift in Sources */, + D8A42A8F1E815A9C00D8E281 /* WMFReferencePopoverMessageViewController.m in Sources */, + 8351CE7B20D4424100E32FC1 /* CollectionViewHeader.swift in Sources */, + 6734EE7922976AED00F00B05 /* ActionButton.swift in Sources */, + 7A0CD24321DFA34100066F68 /* TextFormattingToolbarView.swift in Sources */, + 003AD7312979C512005BDB90 /* EditNoticesViewModel.swift in Sources */, + 005E004428DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift in Sources */, + 67B5334428416C0F00C33E13 /* UserDataExportCache.swift in Sources */, + 83B01F7F23DB0BA2001185F4 /* ArticleViewController+Editing.swift in Sources */, + D8A42A901E815A9C00D8E281 /* WMFSearchFetcher.m in Sources */, + 7A741DCD207FB9CC00CBAAE2 /* SearchBarExtendedViewController.swift in Sources */, + 00D46DAD2889B9250015DE9B /* TalkPageCell.swift in Sources */, + D8A42A921E815A9C00D8E281 /* LoggingDefaults.swift in Sources */, + 83E776A620FFA4D700E26A47 /* DetailTransition.swift in Sources */, + 6782DBFF234537D0003FA21B /* DiffHeaderExtendedView.swift in Sources */, + D8A42A971E815A9C00D8E281 /* MWKTitleLanguageController.m in Sources */, + D8A42A981E815A9C00D8E281 /* UIView+WMFSnapshotting.m in Sources */, + 009B8360298091CD00AABEA3 /* EditNoticesView.swift in Sources */, + 6724289A2362113A00490629 /* DiffFetcher.swift in Sources */, + 6747118B25072D1500287951 /* IconTitleBadge.swift in Sources */, + 832BD3BF28996B68002623CA /* VanishAccountContentView.swift in Sources */, + 671DF9CC25F2AE4F0011799E /* WikidataDescriptionController.swift in Sources */, + 00474A2D28DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift in Sources */, + 7A4D228021B1CD8600D889BD /* TextFormattingTableViewCell.swift in Sources */, + B0524B78214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift in Sources */, + D8A42A9A1E815A9C00D8E281 /* UIViewController+WMFStoryboardUtilities.m in Sources */, + 67985A892524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift in Sources */, + 7A6ED50C20ADBF950001849F /* SessionsFunnel.swift in Sources */, + 6741245327E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift in Sources */, + 00E2EA8C26E28A9700B1A741 /* NotificationsCenterCellStyle.swift in Sources */, + D8A42A9B1E815A9C00D8E281 /* UIVIewController+WMFCommonRotationSupport.swift in Sources */, + 673411572273578A005B31DA /* OldTalkPageFetcher.swift in Sources */, + D8A42A9D1E815A9C00D8E281 /* WMFTitleInsetRespectingButton.m in Sources */, + B0F4761E21F921D300C4E254 /* EditSummaryViewController.swift in Sources */, + B0421AA5206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift in Sources */, + 7A2BB1D721F27AC5004C0FDF /* WKWebView+OffsetHack.swift in Sources */, + B01E3AFC21F986750015B715 /* PreviewWebViewContainer.swift in Sources */, + 00E75B7427EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift in Sources */, + 7A9524DA22669A8B00C55CDC /* InsertMediaSettingsButtonView.swift in Sources */, + 67861A1A223C13940044F69D /* FocusNavigationView.swift in Sources */, + D8A42A9E1E815A9C00D8E281 /* WMFChangePasswordViewController.swift in Sources */, + 00EACEC928E39D470054DDB4 /* TalkPageEmptyView.swift in Sources */, + 7AF0265922985CB9000E0A06 /* BeKindInputAccessoryView.swift in Sources */, + 83B01F7523DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift in Sources */, + D8A42AA11E815A9C00D8E281 /* WMFMapsActivity.swift in Sources */, + 83DB4413244A57590046FABE /* RootNavigationController.swift in Sources */, + 0042812C25E6E841004945B3 /* NYTPhotoViewController.m in Sources */, + 67E0690A22399D1C008550AC /* ReadingThemesControlsViewController.swift in Sources */, + 00E75B6A27EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift in Sources */, + 670F766222B0C49000D87545 /* FakeProgressLoading.swift in Sources */, + B0016CC621362DB300FA1096 /* SetupGradientView.swift in Sources */, + 67B64D5F2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift in Sources */, + 83C06891292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift in Sources */, + D818FEBE21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift in Sources */, + 00097D5F29660FF4000B3514 /* View+Extensions.swift in Sources */, + D8A42AA61E815A9C00D8E281 /* WMFArticleRevisionFetcher.m in Sources */, + 7ABE17382239DCF6006BA309 /* WelcomeAnimationViewController.swift in Sources */, + 7A73B48521E54B4200249E09 /* SectionEditorNavigationItemController.swift in Sources */, + D837CC3A231FE9CC00BA6130 /* ThemeableViewController.swift in Sources */, + B0FFFB2D21C9BED1001E787E /* TextFormattingButton.swift in Sources */, + 00F5AED327C6C80C006390A8 /* PushNotificationsSettingsViewController.swift in Sources */, + 7AF6F76922395BEC00949393 /* EditingWelcomeViewController.swift in Sources */, + 0042813425E6E841004945B3 /* NYTPhotosDataSource.m in Sources */, + 672286282540DB330038E332 /* AppTabBarDelegate.swift in Sources */, + D8A42AA71E815A9C00D8E281 /* WMFArticleLanguagesSectionHeader.m in Sources */, + D818D3891ED750E40076110D /* ArticleCollectionViewController.swift in Sources */, + B0524B54214854E900D8FD8D /* DescriptionWelcomeContainerViewController.swift in Sources */, + 7AF56C3221DDEC1C00563A9C /* TextFormattingProviding.swift in Sources */, + 83CA612D20D1675800EF0C4A /* ExploreCardViewController.swift in Sources */, + D8A42AAD1E815A9C00D8E281 /* WMFTwoFactorPasswordViewController.swift in Sources */, + B0CD9DEF1F70997500051843 /* WMFWelcomeExplorationAnimationView.swift in Sources */, + B0016CBC21354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift in Sources */, + D8A42AAF1E815A9C00D8E281 /* SavedPagesFunnel.m in Sources */, + D8A42AB11E815A9C00D8E281 /* WMFChange.m in Sources */, + 00CB689B288B0CD3002EBB0A /* TalkPageHeaderView.swift in Sources */, + 7A4B333F2136EDED00C6C820 /* UnderlineButton.swift in Sources */, + 67DAEDA623CE24DA003AA208 /* SavedArticlesFetcher.swift in Sources */, + D8A42AB31E815A9C00D8E281 /* MWKSearchRedirectMapping.m in Sources */, + D8A42AB41E815A9C00D8E281 /* MWKImageInfoFetcher+PicOfTheDayInfo.m in Sources */, + 67E8B082226A57E500537BC9 /* OldTalkPageTopicCell.swift in Sources */, + D8A42AB61E815A9C00D8E281 /* RoundedCornerView.swift in Sources */, + 7AE1FE3421B4A9790068BE9F /* TextFormattingButtonView.swift in Sources */, + 83E52BC21F682E3E0045E776 /* LicenseView.swift in Sources */, + D88C701B1EE595E90022A26A /* MapView.swift in Sources */, + 7AF8B7452102297A009772CC /* SearchSettingsViewController.swift in Sources */, + 83B01F7A23DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift in Sources */, + 67C6F79A27E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift in Sources */, + D8A42ABA1E815A9C00D8E281 /* WMFScrollViewController.swift in Sources */, + B0524B68214854E900D8FD8D /* DescriptionWelcomeImageViewController.swift in Sources */, + 7ABE17032239B346006BA309 /* WelcomeViewController.swift in Sources */, + 0072990928AC44F100DCD2E6 /* TalkPageCellTopicView.swift in Sources */, + 832A7A5E23EA138C00D0A750 /* ArticleViewController+References.swift in Sources */, + D8A42AC01E815A9C00D8E281 /* PageHistorySection.swift in Sources */, + 7ABE170F2239B5A0006BA309 /* WelcomePageViewController.swift in Sources */, + B0F9299E1F84789D002A0788 /* WMFWelcomeInitialViewController.swift in Sources */, + 0072992228AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift in Sources */, + D8B166881FD97A0500097D8B /* ViewController.swift in Sources */, + D8A47C9223D7338C002AA823 /* ArticleViewController+TableOfContents.swift in Sources */, + 6782DBD02343FDF2003FA21B /* DiffListUneditedCell.swift in Sources */, + 7AB809D322675B2300BFAB7C /* ThemeableTextView.swift in Sources */, + D8A42AC41E815A9C00D8E281 /* TableOfContentsViewController.swift in Sources */, + 67D9D1FE29711CA700BFCD4F /* Loadable.swift in Sources */, + 673FC3D3273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift in Sources */, + 678D29B12729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift in Sources */, + 83A171D92819B6A80029FB89 /* UNAuthorizationStatus+String.swift in Sources */, + 83A642782226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift in Sources */, + 672D69AC273ACAA200B123B3 /* UITabBarAppearance+Extensions.swift in Sources */, + 7A82765C226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift in Sources */, + D8A42AC61E815A9C00D8E281 /* ProtectedEditAttemptFunnel.m in Sources */, + 83ED2E27289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift in Sources */, + 6780D7702832908F00265F10 /* Notification+NotificationsCenter.swift in Sources */, + B0C7A07B1F710E75008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift in Sources */, + 67EA9E12228F0359008D9EFD /* OldTalkPageHeaderView.swift in Sources */, + 67C1757928AD4D6000C5ABA4 /* TalkPageDataController.swift in Sources */, + 7A9F2779225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift in Sources */, + D8A42AC71E815A9C00D8E281 /* WKWebView+WMFWebViewControllerJavascript.m in Sources */, + D8A42ACB1E815A9C00D8E281 /* WMFArticleTextActivitySource.m in Sources */, + D8A42ACC1E815A9C00D8E281 /* WMFAuthLinkLabel.swift in Sources */, + D82E956D1F156F77007BD960 /* UIView+SubviewEnumeration.swift in Sources */, + 833D6B4B229EE872003CB650 /* TalkPageTopic+Extensions.swift in Sources */, + 67471181250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift in Sources */, + D818D3841ED7254D0076110D /* ColumnarCollectionViewController.swift in Sources */, + D8A42AD11E815A9C00D8E281 /* WMFPasswordResetter.swift in Sources */, + 7A0161E31FE8B4CA00AEDC3D /* TagCollectionViewCell.swift in Sources */, + D8A42AD41E815A9C00D8E281 /* UIBarButtonItem+WMFButtonConvenience.m in Sources */, + 83E52BB71F681F940045E776 /* ShareAFactViewController.swift in Sources */, + D8A42AD51E815A9C00D8E281 /* UIViewController+WMFEmptyView.m in Sources */, + BAA0D91F1F4F165A00091284 /* PageIssuesTableViewController.swift in Sources */, + 009B835B298091BC00AABEA3 /* EditNoticesViewController.swift in Sources */, + 003CD3EC28EF7C77000158E4 /* TalkPageFindInPageSearchController.swift in Sources */, + B0524B22214854E900D8FD8D /* DescriptionWelcomeInitialViewController.swift in Sources */, + D8A42ADD1E815A9C00D8E281 /* NSUserDefaults+WMFApplicationDefaults.swift in Sources */, + 8382F8DC20D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift in Sources */, + 834C26A1240D49F400245BE7 /* ReferenceViewController.swift in Sources */, + 8330533123EF107D00123141 /* MediaListGalleryViewController.swift in Sources */, + D80BF0A62347735E00B3B522 /* AppSearchButton.swift in Sources */, + D8A42ADF1E815A9C00D8E281 /* AboutViewController.m in Sources */, + 00FCCBCD2900848300C9ECD2 /* TalkPageViewController+FindInPage.swift in Sources */, + 7A4FE5421FA00AF1009FA199 /* ArticlePeekPreviewViewController.swift in Sources */, + 8361474E24223689003E49D3 /* ArticleViewController+Announcements.swift in Sources */, + 00EBB7CF27D6A86A002025AC /* SettingsPresentationDelegate.swift in Sources */, + D8B1668F1FD97FE000097D8B /* WMFViewController.m in Sources */, + D82E95881F16502E007BD960 /* WMFLanguagesViewController.m in Sources */, + 67E0690E22399D2E008550AC /* ReadingThemesControlsProtocols.swift in Sources */, + 0022DD2C25829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift in Sources */, + 7A82898F21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift in Sources */, + D8A42AE31E815A9C00D8E281 /* WMFArticleLanguagesSectionFooter.m in Sources */, + 67E2E492250452E60070F12D /* ArticleAsLivingDocHeaderView.swift in Sources */, + 7A71565E226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift in Sources */, + 7A6ED51120ADBF950001849F /* ReadingListsFunnel.swift in Sources */, + 67E069142239B33E008550AC /* FindAndReplaceKeyboardBar.swift in Sources */, + 83B01F8423DB1235001185F4 /* SectionFetcher.swift in Sources */, + 6782DBDC2344EC86003FA21B /* DiffHeaderViewModels.swift in Sources */, + D8B3D7691EC34F5B00930C21 /* SaveButtonsController.swift in Sources */, + 7AFA21BE20110D7900E957E7 /* ReadingListHintViewController.swift in Sources */, + D8A42AE51E815A9C00D8E281 /* WMFWelcomeContainerViewController.swift in Sources */, + D87676A221E7B73C00491039 /* ToolbarSeparatorView.swift in Sources */, + 00FCB2C126D8398700F5A47A /* NotificationsCenterCell.swift in Sources */, + D8A42AE61E815A9C00D8E281 /* WMFAccountCreator.swift in Sources */, + 67134A1A28A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift in Sources */, + 83F1097623D0F115003F3E9E /* HelpViewController.swift in Sources */, + D8A42AE71E815A9C00D8E281 /* UIViewController+WMFHideKeyboard.swift in Sources */, + 6789FA3122E7790900E43842 /* TalkPage+Extensions.swift in Sources */, + D8A47C8823D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift in Sources */, + D8A42AE91E815A9C00D8E281 /* WeakScriptMessageDelegate.swift in Sources */, + 67EA9E16228F035E008D9EFD /* TalkPageReplyFooterView.swift in Sources */, + D8E27BA41F82B38200F9D2B3 /* RMessageView.m in Sources */, + 83FBE9721F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift in Sources */, + 6771299224FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift in Sources */, + 00D280FA247EFFFE006BEE23 /* Date+Extensions.swift in Sources */, + 67CCB34A299155250032439D /* WMFItemSourceExcludingActivityTypes.swift in Sources */, + 7ABAD6B720338CFB006A364C /* ReadingListDetailUnderBarViewController.swift in Sources */, + 7A19C64B20DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift in Sources */, + 832289DE1F7291BA0081A5FB /* SizeThatFitsReusableView.swift in Sources */, + 6782DBD62343FE03003FA21B /* DiffListGroupViewModel.swift in Sources */, + 67059DB82260D61A009811AA /* SchemeHandler.swift in Sources */, + D8A42AEF1E815A9C00D8E281 /* UIView+Animations.swift in Sources */, + 0042813025E6E841004945B3 /* NYTPhotoCaptionView.m in Sources */, + 7A9A612121124D0F00403154 /* CreateNewReadingListButtonView.swift in Sources */, + 7A35CB8A1FD82B6300AAF3B7 /* ReadingListDetailViewController.swift in Sources */, + D8A42AF11E815A9C00D8E281 /* MWKLanguageLinkFetcher.m in Sources */, + 830AD2BC24D1D615003EEFE6 /* WebPageUserScript.swift in Sources */, + 7ABE17272239BB54006BA309 /* WelcomePanelViewController.swift in Sources */, + 67CE5D23222F70C0007B0A2C /* IconBarButtonItem.swift in Sources */, + 7AC19E482301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift in Sources */, + BA6972591F2BA2D700E35F78 /* SettingsTableViewSection.swift in Sources */, + 007CCF1426D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift in Sources */, + D8A42AF51E815A9C00D8E281 /* WMFImageTextActivitySource.swift in Sources */, + 83EE476D20D019A100A21F34 /* ExploreViewController.swift in Sources */, + D8A42AF91E815A9C00D8E281 /* ToCInteractionFunnel.m in Sources */, + D8E6FF6F24056AC300686272 /* ArticleViewController+ContextMenu.swift in Sources */, + 7A8422562268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift in Sources */, + D8A42AFC1E815A9C00D8E281 /* WMFCompassView.m in Sources */, + D8A42AFD1E815A9C00D8E281 /* WMFWelcomeIntroductionViewController.swift in Sources */, + 67C6F7A427E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift in Sources */, + 678D79F3235E5979006161FF /* DiffListChangeViewModel.swift in Sources */, + B0BCF0BC202537D800986F72 /* Panels.swift in Sources */, + 6782DBCA2343FDE4003FA21B /* DiffListContextCell.swift in Sources */, + 6734EE7522976AE400F00B05 /* InfoBannerView.swift in Sources */, + D8E27BA91F82B38700F9D2B3 /* RMessage.m in Sources */, + D8A42B031E815A9C00D8E281 /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m in Sources */, + FFD7B85C24B3CAA0005C2471 /* ReferenceShowing.swift in Sources */, + 00EBB7CA27D6878E002025AC /* BarButtonImageStyle.swift in Sources */, + D8A42B051E815A9C00D8E281 /* UIViewController+WMFChildViewController.swift in Sources */, + D8A42B091E815A9C00D8E281 /* UIView+WMFFrameUtils.m in Sources */, + 83023C0920E51DDF00EC7592 /* SearchLanguagesBarViewController.swift in Sources */, + D8A42B0A1E815A9C00D8E281 /* WMFWelcomeAnimationViewControllers.swift in Sources */, + 67C78F7928B7407100AC207A /* VanishAccountFooterView.swift in Sources */, + D8A42B0B1E815A9C00D8E281 /* TableOfContentsCell.swift in Sources */, + 0030592927DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift in Sources */, + 7AB809DF22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift in Sources */, + B0C7A0871F710EB1008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift in Sources */, + 67C6F78627E8BC2F00B9C864 /* NotificationsCenterIconType.swift in Sources */, + D8A42B0D1E815A9C00D8E281 /* WMFSettingsMenuItem.m in Sources */, + B0CD9DEE1F70997500051843 /* WMFWelcomeIntroductionAnimationView.swift in Sources */, + D8A42B0F1E815A9C00D8E281 /* UIScrollView+ScrollSubviewToLocation.m in Sources */, + FFA0641C25A943EB00B9460B /* BasicLogger.swift in Sources */, + 678D79F9235E599B006161FF /* DiffListContextViewModel.swift in Sources */, + 674E8ABC2382DF030053D206 /* DiffTransformer.swift in Sources */, + D8A42B101E815A9C00D8E281 /* MTLValueTransformer+WMFNumericValueTransformer.m in Sources */, + 671F5E022367EDC600111116 /* GlobalUserInfoFetcher.swift in Sources */, + 67985A572523D80100EBF353 /* ArticleAsLivingDocController.swift in Sources */, + 83B01F9323DB41BE001185F4 /* ArticleViewController+Sharing.swift in Sources */, + 7ADF498A21B45E42009EA338 /* TextFormattingGroupedToolbarView.swift in Sources */, + 0042812425E6E841004945B3 /* NSBundle+NYTPhotoViewer.m in Sources */, + 679471DD275F245000621071 /* NotificationsCenterInboxView.swift in Sources */, + D8A42B161E815A9C00D8E281 /* TableOfContentsHeader.swift in Sources */, + D8A42B191E815A9C00D8E281 /* UIViewController+WMFScrollToTop.swift in Sources */, + 6734116722739CCC005B31DA /* TalkPageLocalHandler.swift in Sources */, + 6761AEFA2707E34F00E47BAD /* NotificationsCenterModelController.swift in Sources */, + 7A9F06102266425700856321 /* InsertMediaSettingsViewController.swift in Sources */, + 00D1F59228885BA300127169 /* TalkPageViewModel.swift in Sources */, + 00E5B3A728EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift in Sources */, + 007F5C70275AA74200E4B02C /* StackedImageLabelView.swift in Sources */, + D8A42B1F1E815A9C00D8E281 /* UIApplicationShortcutItem+WMFShortcutItem.m in Sources */, + 0042812025E6E841004945B3 /* NYTPhotoTransitionAnimator.m in Sources */, + 83F26B2D220B62EC002D87A4 /* SectionEditorButton.swift in Sources */, + 7AA96D5F21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift in Sources */, + D8A42B231E815A9C00D8E281 /* WMFAuthAccountCreationInfoFetcher.swift in Sources */, + 83F1096C23D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift in Sources */, + 6707C035237DBCEE0017E7B6 /* DiffRevisionTransition.swift in Sources */, + 837A15F628DA591E00AAC3FC /* TalkPageCache.swift in Sources */, + 0072990E28AC455500DCD2E6 /* TalkPageCellViewModel.swift in Sources */, + 7A0FF2CF230343BA00E755D4 /* PageHistoryCollectionViewCell.swift in Sources */, + 67282FC024855B7B00B73E20 /* ArticleContextMenuPresenting.swift in Sources */, + D8A42B251E815A9C00D8E281 /* AppDelegate.m in Sources */, + B09CE59D222F623900067D2A /* WKWebView+EditSelectionJavascript.swift in Sources */, + 830C0DD823D9AFBE006471C4 /* UIViewController+Push.swift in Sources */, + 83DE45BC2449C09B00671878 /* SplashScreenViewController.swift in Sources */, + D8A42B271E815A9C00D8E281 /* ArticlePlace.swift in Sources */, + D87B13A81F276B1000B27227 /* ShareActivityController.swift in Sources */, + D8E6FF7F2405AAC400686272 /* ArticleViewController+Analytics.swift in Sources */, + 83023C1420E6561900EC7592 /* ViewControllerTransitionsController.swift in Sources */, + D8A42B281E815A9C00D8E281 /* String?+WMFExtras.swift in Sources */, + D82117FF1EE58C080076C040 /* MapAnnotation.swift in Sources */, + 7ABE173E2239DEF0006BA309 /* WelcomeAnimationView.swift in Sources */, + D8A42B2A1E815A9C00D8E281 /* WMFSearchResults.m in Sources */, + 6734EE7D22976BA300F00B05 /* TalkPageHintViewController.swift in Sources */, + D8A42B2C1E815A9C00D8E281 /* WMFBarButtonItemPopoverMessageViewController.m in Sources */, + 67E8B07D226A57DF00537BC9 /* AccountViewController.swift in Sources */, + FF555567277287F500925099 /* CollectionViewContextMenuShowing.swift in Sources */, + 676E813629380D8A00F15258 /* TalkPagesFunnel.swift in Sources */, + D8A42B2E1E815A9C00D8E281 /* WMFReferencePageViewController.swift in Sources */, + BA4524271F32500C00439C42 /* TextSizeChangeExampleViewController.swift in Sources */, + 83927D7E1F70570400051890 /* DisambiguationPagesViewController.swift in Sources */, + 7A0161B71FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift in Sources */, + 833B8C8B281AE2120021C12C /* RemoteNotificationsFunnel.swift in Sources */, + 0072991D28AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift in Sources */, + 8382F8D020D9206000AE5250 /* ImageCollectionViewCell.swift in Sources */, + D8A42B371E815A9C00D8E281 /* WikiTextSectionUploader.m in Sources */, + 8334EC4E286A443C00929DF2 /* TalkPageFetcher.swift in Sources */, + 7A6ED51B20ADBF950001849F /* SettingsFunnel.swift in Sources */, + 7AF8CEF022653406000B7676 /* InsertMediaSelectedImageView.swift in Sources */, + 00CF2EA327DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift in Sources */, + D8A42B3B1E815A9C00D8E281 /* UIViewController+WMFDynamicHeightPopoverMessage.m in Sources */, + 0042813C25E6E841004945B3 /* NYTScalingImageView.m in Sources */, + 6782DBB22343B812003FA21B /* DiffHeaderCompareView.swift in Sources */, + 67ADEE9923A2CFFB0000CAF7 /* ArticleWebMessagingController.swift in Sources */, + 67E8B094226A57EA00537BC9 /* TalkPageTopicNewViewController.swift in Sources */, + D8A42B411E815A9C00D8E281 /* WMFWelcomeAnalyticsViewController.swift in Sources */, + B0D3E70F214AF776007578BA /* DescriptionEditViewController.swift in Sources */, + 00BCB71B26DEE11C002C3F72 /* InsetLabelView.swift in Sources */, + D8A42B421E815A9C00D8E281 /* WMFRevisionQueryResults.m in Sources */, + D8A42B431E815A9C00D8E281 /* WMFAppViewController.m in Sources */, + D8C4D3DB1FD5D9260089CEC2 /* TUSafariActivity.m in Sources */, + B0845E1420618DA400CDD98E /* SavedProgressViewController.swift in Sources */, + 6782DB942343B6F9003FA21B /* DiffContainerViewController.swift in Sources */, + D818D38E1ED765470076110D /* ArticleLocationCollectionViewController.swift in Sources */, + 67E8B096226A57EA00537BC9 /* TalkPageReplyListViewController.swift in Sources */, + 7AFEB3F81FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift in Sources */, + 7A9524CE22665E6400C55CDC /* InsertMediaSettingsImageView.swift in Sources */, + 8320331E22B90529004A9EDA /* NavigationStateController.swift in Sources */, + 0042812825E6E841004945B3 /* NYTPhotoTransitionController.m in Sources */, + D8A42B471E815A9C00D8E281 /* UIView+IBExtras.swift in Sources */, + D8A42B491E815A9C00D8E281 /* WMFHamburgerMenuFunnel.m in Sources */, + 7A71567C22699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift in Sources */, + D8A42B4A1E815A9C00D8E281 /* UIViewController+WMFWelcomeStoryboard.swift in Sources */, + 0042813825E6E841004945B3 /* NYTPhotoDismissalInteractionController.m in Sources */, + 83DB0A5C23EEDE2E00DA5F58 /* MWKDataStore+LegacyMobileview.swift in Sources */, + B08624331F72EA1A00B770FD /* WMFWelcomeAnimationBackgroundView.swift in Sources */, + 67E466FD241BED810014149B /* EditHistoryCompareFunnel.swift in Sources */, + 7A48EA1121B5C9B20083F3DC /* EditToolbarView.swift in Sources */, + BA7683C71F30D87D00A487AA /* ProminentSwitch.swift in Sources */, + 6782DBAC2343B7FC003FA21B /* DiffHeaderEditorView.swift in Sources */, + D8A42B4B1E815A9C00D8E281 /* WMFLanguageCell.m in Sources */, + 00B0B3D32978745400DD7893 /* EditNoticesFetcher.swift in Sources */, + 00E5B3A228EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift in Sources */, + 7A1469C0220BBE44000A20F1 /* EditHintViewController.swift in Sources */, + 6798332C22C3F2950073CE6F /* UITextView+Extensions.swift in Sources */, + 00AA5AAA276BF29E005295B0 /* StatusTextBarButtonItem.swift in Sources */, + 67D9D1F32970D88E00BFCD4F /* DisclosureButton.swift in Sources */, + 7A27E85521B19767001B2D21 /* TextStyleFormattingTableViewController.swift in Sources */, + D8421B56203CC8420040F50B /* DebugReadingListsViewController.swift in Sources */, + 6782DBC42343FDCA003FA21B /* DiffListChangeCell.swift in Sources */, + 6780CF3629676DE300D45927 /* ShiftingTopViewsStack.swift in Sources */, + 7AE99B3121CC53AB0092BE7F /* TextFontFormattingTableViewController.swift in Sources */, + 6782DC0E23453D7D003FA21B /* DiffHeaderCompareItemView.swift in Sources */, + D8A42B4E1E815A9C00D8E281 /* Array+WMFAllFieldsFilled.swift in Sources */, + 7A32078B21E40193009E1677 /* SectionEditorMenuItemsController.swift in Sources */, + 67E5DA5F2761B0AB00CE827D /* NotificationsCenterFilterView.swift in Sources */, + D8A42B4F1E815A9C00D8E281 /* NewsViewController.swift in Sources */, + D8A42B501E815A9C00D8E281 /* main.m in Sources */, + B03103301F677BED00E2FCF6 /* WMFWelcomeExplorationViewController.swift in Sources */, + D8A42B531E815A9C00D8E281 /* WMFDailyStatsLoggingFunnel.m in Sources */, + D8A42B541E815A9C00D8E281 /* WMFRandomDiceButton.m in Sources */, + 7A1C4998227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift in Sources */, + D8A47C8D23D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift in Sources */, + FF2B2113254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift in Sources */, + 7A71567122697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift in Sources */, + D8A42B5B1E815A9C00D8E281 /* WMFFirstRandomViewController.m in Sources */, + D8A42B5D1E815A9C00D8E281 /* ArticlePopoverViewController.swift in Sources */, + 8386BDF82386D736007EE89D /* ViewController+URLHandling.swift in Sources */, + 00BCB72526DEEB1C002C3F72 /* RoundedImageView.swift in Sources */, + 8386BDFE2386D754007EE89D /* SinglePageWebViewController.swift in Sources */, + 0042814025E6E841004945B3 /* NYTPhotosViewController.m in Sources */, + 83C06885292EC85700DF1403 /* TalkPageFormattingToolbarView.swift in Sources */, + D8E6FF7A24058AC600686272 /* WMFWebView.m in Sources */, + 007CCF0F26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift in Sources */, + D858C7B9210B91CE0039E0C9 /* PassthroughView.swift in Sources */, + D8A42B5E1E815A9C00D8E281 /* WMFLogFormatter.m in Sources */, + 7A28126620D3F84A009B42B5 /* FeedCardSettingsViewController.swift in Sources */, + 7AB209FC22FC67B4006FECB4 /* PageHistoryViewController.swift in Sources */, + B0D4917221F999A3002BBDD3 /* EditSaveViewController.swift in Sources */, + D8A42B611E815A9C00D8E281 /* NSString+FormattedAttributedString.m in Sources */, + 83FBE9781F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift in Sources */, + B0BCF0AE2023AC7700986F72 /* ScrollableEducationPanelViewController.swift in Sources */, + D8A42B651E815A9C00D8E281 /* WMFDeleteBackwardReportingTextField.swift in Sources */, + D8A42B671E815A9C00D8E281 /* WMFForgotPasswordViewController.swift in Sources */, + FF921888252F7EA500C39A8F /* ThanksGiving.swift in Sources */, + D8A42B691E815A9C00D8E281 /* WMFImageGalleryViewController.m in Sources */, + 6782DC142346920B003FA21B /* DiffContainerViewModel.swift in Sources */, + 67FBE33D29705FC200A2E4AD /* TalkPageArchivesItem.swift in Sources */, + 7ADF853923516CF500500ADC /* PageHistoryHintController.swift in Sources */, + D80ED25F1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift in Sources */, + D8A42B6D1E815A9C00D8E281 /* WMFAccountCreationViewController.swift in Sources */, + D8E6FF6A24054FA100686272 /* ArticleViewController+LinkPreviewing.swift in Sources */, + 7AB7DECB227203A600DD61A2 /* InsertMediaViewController.swift in Sources */, + 7A16C4E9212D941C00F0D5EC /* SubSettingsViewController.swift in Sources */, + 83F1096723D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift in Sources */, + 7AF49F83204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift in Sources */, + 679A240B2968E0D0008D7686 /* ShiftingScrollView.swift in Sources */, + D8A42B701E815A9C00D8E281 /* NSAttributedString+WMFModify.m in Sources */, + 7A2FE55F20517BAE00F92F8F /* EraseSavedArticlesView.swift in Sources */, + BA45241B1F324C3100439C42 /* FontSizeSliderViewController.swift in Sources */, + 675175DF276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift in Sources */, + 83C0688A292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift in Sources */, + 7A707980223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift in Sources */, + D84DAA191EEEF527008E4B18 /* SWStepSlider.swift in Sources */, + B0524AF42144D7BE00D8FD8D /* DescriptionHelpViewController.swift in Sources */, + 00DEE61C28AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift in Sources */, + D8A42B711E815A9C00D8E281 /* WMFSettingsViewController.m in Sources */, + D8A42B731E815A9C00D8E281 /* UIScrollView+WMFContentOffsetUtils.m in Sources */, + 6780CF3029676AB000D45927 /* ShiftingTopViewsContaining.swift in Sources */, + D8A42B751E815A9C00D8E281 /* WMFImageGalleryDetailOverlayView.m in Sources */, + 6707C03B237F0A6E0017E7B6 /* UIFont+Extensions.swift in Sources */, + 7ADF497E21B45CEE009EA338 /* TextFormattingPlainToolbarView.swift in Sources */, + 7A998AC41FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift in Sources */, + 6780CF262967683800D45927 /* TalkPageArchivesViewController.swift in Sources */, + 006ABEF02901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift in Sources */, + D8A42B781E815A9C00D8E281 /* UIApplication+SystemSettings.swift in Sources */, + 67E50B2E27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift in Sources */, + B01E3B0221F98BFF0015B715 /* EditPreviewViewController.swift in Sources */, + D8A42B7B1E815A9C00D8E281 /* WMFArticleRevision.m in Sources */, + 7A196F5D21BF199500D9E4B5 /* SectionEditorWebView.swift in Sources */, + D8A42B7C1E815A9C00D8E281 /* ArticlePlaceView.swift in Sources */, + 67DC5BF223A1427D00B03A84 /* ActionHandlerScript.swift in Sources */, + 83B01F9823DB41D7001185F4 /* ArticleViewController+FindInPage.swift in Sources */, + 83FDE79C293564AC006D55FE /* Link.swift in Sources */, + D8A42B7D1E815A9C00D8E281 /* WMFCaptchaViewController.swift in Sources */, + D8A42B7E1E815A9C00D8E281 /* DDLog+WMFLogger.m in Sources */, + 674711862507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift in Sources */, + 67BEFFD828AD9DF000606B38 /* TalkPageType.swift in Sources */, + 67E8B092226A57EA00537BC9 /* TalkPageContainerViewController.swift in Sources */, + 6761AEE92704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift in Sources */, + 671DF9C425F2AE4E0011799E /* ArticleDescriptionControlling.swift in Sources */, + D8A42B801E815A9C00D8E281 /* WMFLegacyReference.swift in Sources */, + D8A42B811E815A9C00D8E281 /* WMFWelcomePageViewController.swift in Sources */, + 6734115C22735833005B31DA /* OldTalkPagesController.swift in Sources */, + 67CEF2722351113000D5CA6C /* DiffController.swift in Sources */, + 7A5A0546225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift in Sources */, + 83836ECF1F615E5B007D1A05 /* ShareViewController.swift in Sources */, + 7A6F560821AF527A0076D184 /* TextFormattingInputViewController.swift in Sources */, + 7AC809C821DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift in Sources */, + 6782DBBE2343B861003FA21B /* DiffListViewController.swift in Sources */, + 7ABAD6C220349B91006A364C /* Collection.swift in Sources */, + D8A42B821E815A9C00D8E281 /* MKCoordinateRegion+Dimensions.swift in Sources */, + D8A42B841E815A9C00D8E281 /* WMFAlertManager.swift in Sources */, + D8A42B851E815A9C00D8E281 /* WMFWelcomeLanguageTableViewController.swift in Sources */, + 7ADEAB061FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift in Sources */, + D8A42B871E815A9C00D8E281 /* WMFLoginFunnel.m in Sources */, + B0408C582127F2C100AC76CE /* WMFImageGalleryGradientViews.swift in Sources */, + 00D9276E29511E95004ECBEA /* PageHistoryCountsView.swift in Sources */, + 83C0656E23D23220001821BC /* TableOfContentsItem.swift in Sources */, + 7A29A5CB1F6C405900E8F42B /* HistoryViewController.swift in Sources */, + 7AF56C3821DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift in Sources */, + D8A42B891E815A9C00D8E281 /* MapUtilities.swift in Sources */, + 834CC34E21075B7600F62818 /* UITabBar+Theme.swift in Sources */, + 678D29B6272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift in Sources */, + 7AFEB1BF1FA236A100B8DF32 /* UIViewController+Peekable.swift in Sources */, + D8A42B8B1E815A9C00D8E281 /* WKWebView+ElementLocation.m in Sources */, + 7AB20A0F22FC8432006FECB4 /* PageHistoryCountsViewController.swift in Sources */, + 83C06896292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift in Sources */, + FFD7B84924AEB04A005C2471 /* ArticleScrolling.swift in Sources */, + 67D9D1F92970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift in Sources */, + 8367A28220D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift in Sources */, + 7AEBAD482102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift in Sources */, + 7A9F061C2266432200856321 /* InsertMediaSettingsTextTableViewCell.swift in Sources */, + B068EDE3206B183500C827D1 /* Progress+ProgressUI.swift in Sources */, + 67FBE338297056EB00A2E4AD /* TalkPageArchivesFetcher.swift in Sources */, + 83F1096223D09F80003F3E9E /* ArticleViewController+WIconPopover.swift in Sources */, + 7A25367A21B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift in Sources */, + 6754E44B22773588005EEAD1 /* OldTalkPageReplyCell.swift in Sources */, + 7A0DE50320CEEC760032AB57 /* ExploreFeedSettingsViewController.swift in Sources */, + 7A2432C11FCF401900FB4BA5 /* CreateReadingListViewController.swift in Sources */, + D8A42B8D1E815A9C00D8E281 /* UIViewController+WMFStoryboardUtilities.swift in Sources */, + D8A42B8F1E815A9C00D8E281 /* TableOfContentsPresentationController.swift in Sources */, + 8330532C23EF0B4200123141 /* ArticleViewController+Media.swift in Sources */, + 67DAEDDC27E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift in Sources */, + B09705B7236B29D7006FDB5C /* DiffThanker.swift in Sources */, + 00BCB72026DEE1C7002C3F72 /* VerticalSpacerView.swift in Sources */, + D8A42B921E815A9C00D8E281 /* WMFLoginViewController.swift in Sources */, + 67C6F79F27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift in Sources */, + 67DC5BE623A017CA00B03A84 /* ArticleViewController.swift in Sources */, + D8533ED81ECF581600E44F86 /* NewsCollectionViewHeader.swift in Sources */, + 00FCB2C626D839A500F5A47A /* NotificationsCenterCellViewModel.swift in Sources */, + 7AE1D33C1FCD10B900393471 /* SavedViewController.swift in Sources */, + 00D46DA82889B7F50015DE9B /* TalkPageView.swift in Sources */, + B0CD9DEC1F70997500051843 /* WMFWelcomeAnimationExtensions.swift in Sources */, + 83B01F9D23DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift in Sources */, + 7A203F0E1FDEDCDD00A229EC /* ReadingListHintController.swift in Sources */, + D8A42B961E815A9C00D8E281 /* PageHistoryFetcher.swift in Sources */, + 7ABE171B2239B8EE006BA309 /* WelcomeContainerViewController.swift in Sources */, + 676C869626D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift in Sources */, + D8A42B981E815A9C00D8E281 /* CreateAccountFunnel.m in Sources */, + 836BF5712869F9C200B98321 /* TalkPageViewController.swift in Sources */, + B04AE84F21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift in Sources */, + BAC6EECA1F1E519B00228AD0 /* AppearanceSettingsViewController.swift in Sources */, + 83927D841F705B7B00051890 /* SearchResultsViewController.swift in Sources */, + 7A82896B21B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift in Sources */, + D8A42B9A1E815A9C00D8E281 /* WMFShareFunnel.m in Sources */, + 67E8B093226A57EA00537BC9 /* TalkPageTopicListViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8B589A121CD05070027083A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83B019D024F6ACAA0014B5EF /* WikipediaLanguageCommandLineUtility.swift in Sources */, + 83B019D224F6ACAA0014B5EF /* main.swift in Sources */, + 83B019D124F6ACAA0014B5EF /* WikipediaLanguageCommandLineUtilityAPI.swift in Sources */, + 83ACAAA524E6E47D003B3035 /* Wikipedia.swift in Sources */, + 83ACAA9C24E6D8F8003B3035 /* PageNamespace.swift in Sources */, + 83ACAAAE24E6EF0B003B3035 /* WikipediaSiteInfoLookup.swift in Sources */, + 83ACAA9924E6D112003B3035 /* Collection+AsyncMap.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8CE24E01E698E2400DAE2E0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D8CE24E11E698E2400DAE2E0 /* UserLocationAnnotationView.swift in Sources */, + 7ABAD6B520338CFB006A364C /* ReadingListDetailUnderBarViewController.swift in Sources */, + D8CE24E21E698E2400DAE2E0 /* WMFSearchFunnel.m in Sources */, + D8CE24E31E698E2400DAE2E0 /* WMFImageURLActivitySource.swift in Sources */, + D8B3D7671EC34F5B00930C21 /* SaveButtonsController.swift in Sources */, + 8368BB8524129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift in Sources */, + B04AE84D21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift in Sources */, + 83F1097423D0F115003F3E9E /* HelpViewController.swift in Sources */, + 7A27EDA32279F5270010CB24 /* InsertLinkViewController.swift in Sources */, + 6782DBCE2343FDF2003FA21B /* DiffListUneditedCell.swift in Sources */, + 007CCF0326D5A10200D5EA7C /* NotificationsCenterViewController.swift in Sources */, + B0524B76214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift in Sources */, + B0D3E70D214AF776007578BA /* DescriptionEditViewController.swift in Sources */, + 67D3C454228CB54E001D5741 /* OldTalkPageReplyComposeView.swift in Sources */, + B0F4761C21F921D300C4E254 /* EditSummaryViewController.swift in Sources */, + 00AA5AA9276BF29E005295B0 /* StatusTextBarButtonItem.swift in Sources */, + 0042812F25E6E841004945B3 /* NYTPhotoCaptionView.m in Sources */, + 7A48EA0F21B5C9B20083F3DC /* EditToolbarView.swift in Sources */, + 7A82896921B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift in Sources */, + 67471180250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift in Sources */, + B0EFCD6E1EBF12E5008F36E5 /* LibrariesUsed.swift in Sources */, + D8CE24E81E698E2400DAE2E0 /* WMFCaptchaResetter.swift in Sources */, + 00E2EA8B26E28A9700B1A741 /* NotificationsCenterCellStyle.swift in Sources */, + 00D1F59128885BA300127169 /* TalkPageViewModel.swift in Sources */, + B0CD9DDE1F70997400051843 /* WMFWelcomeAnimationExtensions.swift in Sources */, + 67E2E491250452E60070F12D /* ArticleAsLivingDocHeaderView.swift in Sources */, + 676C869526D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift in Sources */, + D87676A021E7B73C00491039 /* ToolbarSeparatorView.swift in Sources */, + 00D46DA72889B7F50015DE9B /* TalkPageView.swift in Sources */, + 7ABE17192239B8EE006BA309 /* WelcomeContainerViewController.swift in Sources */, + D858C7B7210B91CE0039E0C9 /* PassthroughView.swift in Sources */, + 83F26B2B220B62EC002D87A4 /* SectionEditorButton.swift in Sources */, + D8CE24EC1E698E2400DAE2E0 /* PlacesViewController.swift in Sources */, + 67C78F7828B7407000AC207A /* VanishAccountFooterView.swift in Sources */, + 00474A2C28DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift in Sources */, + 67146037243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift in Sources */, + B0C7A07D1F710E75008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift in Sources */, + D8CE24EE1E698E2400DAE2E0 /* WMFReferencePanelViewController.swift in Sources */, + 7AC19E332301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift in Sources */, + 7A29A5CF1F6C49C600E8F42B /* CollectionViewUpdater.swift in Sources */, + 83510B0928F4CF6400B6235B /* TalkPageErrorStateView.swift in Sources */, + 7A715662226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift in Sources */, + D818D38C1ED765470076110D /* ArticleLocationCollectionViewController.swift in Sources */, + 7A71567A22699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift in Sources */, + D8CE24F11E698E2400DAE2E0 /* WikidataFetcher+Places.swift in Sources */, + 67C9FC0128C77F350065A530 /* TalkPageTopicComposeViewController.swift in Sources */, + D8CE24F21E698E2400DAE2E0 /* UIButton+WMFButton.m in Sources */, + 679A24002968DAB9008D7686 /* ShiftingTopView.swift in Sources */, + D8CE24F31E698E2400DAE2E0 /* TableOfContentsAnimator.swift in Sources */, + 7AEC985A219F529000BEF62B /* DefaultEditToolbarView.swift in Sources */, + D82E956B1F156F77007BD960 /* UIView+SubviewEnumeration.swift in Sources */, + D8CE24F61E698E2400DAE2E0 /* WMFReferencePageBackgroundView.swift in Sources */, + 833D6B49229EE872003CB650 /* TalkPageTopic+Extensions.swift in Sources */, + FFD7B85B24B3CAA0005C2471 /* ReferenceShowing.swift in Sources */, + 67E8B086226A57E900537BC9 /* TalkPageTopicNewViewController.swift in Sources */, + 83E52BB51F681F940045E776 /* ShareAFactViewController.swift in Sources */, + D8CE24F81E698E2400DAE2E0 /* UIApplication+RTL.swift in Sources */, + B0524B48214854E900D8FD8D /* DescriptionWelcomePageViewController.swift in Sources */, + 7A19C64920DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift in Sources */, + FFD7B84824AEB04A005C2471 /* ArticleScrolling.swift in Sources */, + D8CE24FB1E698E2400DAE2E0 /* WMFWelcomePanelViewController.swift in Sources */, + 83F1096523D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift in Sources */, + 6780CF3529676DE300D45927 /* ShiftingTopViewsStack.swift in Sources */, + 83B01F9123DB41BE001185F4 /* ArticleViewController+Sharing.swift in Sources */, + 00BCB71A26DEE11C002C3F72 /* InsetLabelView.swift in Sources */, + 7A1C4996227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift in Sources */, + D8533ED61ECF581600E44F86 /* NewsCollectionViewHeader.swift in Sources */, + 0072990D28AC455500DCD2E6 /* TalkPageCellViewModel.swift in Sources */, + D8CE24FE1E698E2400DAE2E0 /* UIViewController+WMFAlerts.swift in Sources */, + 7A741DCB207FB9CC00CBAAE2 /* SearchBarExtendedViewController.swift in Sources */, + 7ADF853723516CF500500ADC /* PageHistoryHintController.swift in Sources */, + D8CE24FF1E698E2400DAE2E0 /* NSDate+WMFPOTDTitle.m in Sources */, + D8CE25001E698E2400DAE2E0 /* WMFTableHeaderFooterLabelView.m in Sources */, + 6782DBB02343B812003FA21B /* DiffHeaderCompareView.swift in Sources */, + 83DAA9B123FEB611002D5716 /* ReferenceBackLinksViewController.swift in Sources */, + 678D29B5272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift in Sources */, + 7A9524D822669A8B00C55CDC /* InsertMediaSettingsButtonView.swift in Sources */, + 67BEFFD728AD9DF000606B38 /* TalkPageType.swift in Sources */, + 678E7E8326432F060005439C /* NavigationEventsFunnel.swift in Sources */, + FF2B2112254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift in Sources */, + 8351CE7920D4424100E32FC1 /* CollectionViewHeader.swift in Sources */, + 7A20AE092057F39C005FB5DF /* UIView+Identifier.swift in Sources */, + D8E27BA21F82B38100F9D2B3 /* RMessageView.m in Sources */, + D8B1668D1FD97FE000097D8B /* WMFViewController.m in Sources */, + 7AE99B2F21CC53AB0092BE7F /* TextFontFormattingTableViewController.swift in Sources */, + 832BD3BE28996B68002623CA /* VanishAccountContentView.swift in Sources */, + 83DB0A5A23EEDE2D00DA5F58 /* MWKDataStore+LegacyMobileview.swift in Sources */, + 83FDE79B293564AC006D55FE /* Link.swift in Sources */, + 67EA9E14228F035D008D9EFD /* TalkPageReplyFooterView.swift in Sources */, + D8CE25051E698E2400DAE2E0 /* WMFDatabaseHousekeeper.swift in Sources */, + D8CE25071E698E2400DAE2E0 /* PlaceSearch.swift in Sources */, + 7A0FF2CD230343BA00E755D4 /* PageHistoryCollectionViewCell.swift in Sources */, + 00BCB72426DEEB1C002C3F72 /* RoundedImageView.swift in Sources */, + 7A610CB8220A30C900C266AE /* HintViewController.swift in Sources */, + 83E3E7262440F1FE00AA2E9A /* LoadingAnimationViewController.swift in Sources */, + FF555565277287F300925099 /* CollectionViewContextMenuShowing.swift in Sources */, + 67D9D1F82970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift in Sources */, + 00BCB71F26DEE1C7002C3F72 /* VerticalSpacerView.swift in Sources */, + D8CE25091E698E2400DAE2E0 /* PlaceSearchSuggestionController.swift in Sources */, + 7AF8CEEE22653406000B7676 /* InsertMediaSelectedImageView.swift in Sources */, + 83F1096A23D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift in Sources */, + 6771299624FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift in Sources */, + D8CE250B1E698E2400DAE2E0 /* WMFPageHistoryRevision.m in Sources */, + 83A171D82819B6A70029FB89 /* UNAuthorizationStatus+String.swift in Sources */, + 67DC5BEA23A03FE700B03A84 /* ArticleToolbarController.swift in Sources */, + D8CE25121E698E2400DAE2E0 /* WMFEmptyView.m in Sources */, + 009B835A298091BC00AABEA3 /* EditNoticesViewController.swift in Sources */, + 7AE1FE3221B4A9790068BE9F /* TextFormattingButtonView.swift in Sources */, + D8CE25151E698E2400DAE2E0 /* WMFSettingsTableViewCell.m in Sources */, + 7A35CB881FD82B6300AAF3B7 /* ReadingListDetailViewController.swift in Sources */, + 00E75B6E27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift in Sources */, + D8CE25161E698E2400DAE2E0 /* UIView+WMFSubviews.swift in Sources */, + 830C0DD623D9AFBE006471C4 /* UIViewController+Push.swift in Sources */, + 67E8B085226A57E900537BC9 /* TalkPageTopicListViewController.swift in Sources */, + 83ED2E26289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift in Sources */, + D87B13A61F276B0F00B27227 /* ShareActivityController.swift in Sources */, + D8CE25181E698E2400DAE2E0 /* WMFReferencePopoverMessageViewController.m in Sources */, + D8CE25191E698E2400DAE2E0 /* WMFSearchFetcher.m in Sources */, + 6782DBD42343FE03003FA21B /* DiffListGroupViewModel.swift in Sources */, + 67E0690C22399D1D008550AC /* ReadingThemesControlsViewController.swift in Sources */, + 7A9A611F21124D0F00403154 /* CreateNewReadingListButtonView.swift in Sources */, + 67E069132239B33D008550AC /* FindAndReplaceKeyboardBar.swift in Sources */, + 67DC5BF023A1427D00B03A84 /* ActionHandlerScript.swift in Sources */, + D8CE251B1E698E2400DAE2E0 /* LoggingDefaults.swift in Sources */, + D8CE25201E698E2400DAE2E0 /* MWKTitleLanguageController.m in Sources */, + 678D79F7235E599B006161FF /* DiffListContextViewModel.swift in Sources */, + 830378422940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift in Sources */, + D8CE25211E698E2400DAE2E0 /* UIView+WMFSnapshotting.m in Sources */, + D8CE25221E698E2400DAE2E0 /* UIViewController+WMFStoryboardUtilities.m in Sources */, + 00CF2EA227DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift in Sources */, + B0524AF22144D7BE00D8FD8D /* DescriptionHelpViewController.swift in Sources */, + 7A715668226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift in Sources */, + D8CE25231E698E2400DAE2E0 /* UIVIewController+WMFCommonRotationSupport.swift in Sources */, + 00E75B6427EB87DC00A45B78 /* NotificationsCenterDetailView.swift in Sources */, + 0072992128AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift in Sources */, + 7ADF498821B45E42009EA338 /* TextFormattingGroupedToolbarView.swift in Sources */, + D8CE25241E698E2400DAE2E0 /* (null) in Sources */, + D818D3821ED7254D0076110D /* ColumnarCollectionViewController.swift in Sources */, + 6734EE7B22976BA300F00B05 /* TalkPageHintViewController.swift in Sources */, + 67B64D5E2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift in Sources */, + B0524B66214854E900D8FD8D /* DescriptionWelcomeImageViewController.swift in Sources */, + 6782DB922343B6F9003FA21B /* DiffContainerViewController.swift in Sources */, + 6780D5BB237AF8A10087A5D1 /* DiffToolbarView.swift in Sources */, + 6782DC0C23453D7D003FA21B /* DiffHeaderCompareItemView.swift in Sources */, + 0042811B25E6E841004945B3 /* NYTPhotosOverlayView.m in Sources */, + B0016CC421362DB300FA1096 /* SetupGradientView.swift in Sources */, + 6707C039237F0A6E0017E7B6 /* UIFont+Extensions.swift in Sources */, + D8CE25261E698E2400DAE2E0 /* WMFTitleInsetRespectingButton.m in Sources */, + B0C7A0891F710EB1008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift in Sources */, + 00841DE524477805003CF74A /* AppTabBarDelegate.swift in Sources */, + D8CE25271E698E2400DAE2E0 /* WMFChangePasswordViewController.swift in Sources */, + D8B166861FD97A0500097D8B /* ViewController.swift in Sources */, + 00097D5E29660FF3000B3514 /* View+Extensions.swift in Sources */, + B0421AA3206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift in Sources */, + 7A6ED52220ADBF950001849F /* LoginFunnel.swift in Sources */, + 83CA612B20D1675800EF0C4A /* ExploreCardViewController.swift in Sources */, + 8356115F28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift in Sources */, + 6798332A22C3F2940073CE6F /* UITextView+Extensions.swift in Sources */, + 7A5A0544225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift in Sources */, + D8CE252E1E698E2400DAE2E0 /* WMFArticleRevisionFetcher.m in Sources */, + 00E75B5F27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift in Sources */, + D8CE252F1E698E2400DAE2E0 /* WMFArticleLanguagesSectionHeader.m in Sources */, + 83C0656C23D23220001821BC /* TableOfContentsItem.swift in Sources */, + 7A0CD24121DFA34100066F68 /* TextFormattingToolbarView.swift in Sources */, + 7A29A5C91F6C405900E8F42B /* HistoryViewController.swift in Sources */, + D8CE25351E698E2400DAE2E0 /* WMFTwoFactorPasswordViewController.swift in Sources */, + 679A23FB2968D865008D7686 /* ShiftingTopViewsData.swift in Sources */, + 7AA96D5D21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift in Sources */, + B0B423621EF9D6A300D3DC4C /* OnThisDayViewController.swift in Sources */, + D818D3871ED750E40076110D /* ArticleCollectionViewController.swift in Sources */, + 00CB689A288B0CD3002EBB0A /* TalkPageHeaderView.swift in Sources */, + 7A203F0C1FDEDCDD00A229EC /* ReadingListHintController.swift in Sources */, + D8CE25371E698E2400DAE2E0 /* SavedPagesFunnel.m in Sources */, + 6798331B22C174F00073CE6F /* LinkOnlyTextView.swift in Sources */, + 67C6F79427E8C03A00B9C864 /* NotificationsCenterAction.swift in Sources */, + D8CE25391E698E2400DAE2E0 /* WMFChange.m in Sources */, + D8CE253B1E698E2400DAE2E0 /* MWKSearchRedirectMapping.m in Sources */, + 7A25367821B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift in Sources */, + 7A16C4E7212D941C00F0D5EC /* SubSettingsViewController.swift in Sources */, + 8382F8CE20D9206000AE5250 /* ImageCollectionViewCell.swift in Sources */, + 672428982362113900490629 /* DiffFetcher.swift in Sources */, + D8CE253C1E698E2400DAE2E0 /* MWKImageInfoFetcher+PicOfTheDayInfo.m in Sources */, + 67C6F7A327E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift in Sources */, + D8CE25411E698E2400DAE2E0 /* WMFScrollViewController.swift in Sources */, + 7A7AC84721B6B89B003B849B /* SectionEditorViewController.swift in Sources */, + 7AFEB3F61FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift in Sources */, + 7ADEAB041FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift in Sources */, + 6747118A25072D1500287951 /* IconTitleBadge.swift in Sources */, + D8CE25471E698E2400DAE2E0 /* PageHistorySection.swift in Sources */, + 83E52BC01F682E3E0045E776 /* LicenseView.swift in Sources */, + 83AE1C811F34BB59004B62E0 /* ImageDimmingExampleViewController.swift in Sources */, + 67E0691022399D2F008550AC /* ReadingThemesControlsProtocols.swift in Sources */, + D8CE254B1E698E2400DAE2E0 /* TableOfContentsViewController.swift in Sources */, + 6734115A22735832005B31DA /* OldTalkPagesController.swift in Sources */, + D8CE254D1E698E2400DAE2E0 /* ProtectedEditAttemptFunnel.m in Sources */, + 7A1469BE220BBE44000A20F1 /* EditHintViewController.swift in Sources */, + 6780CF252967683800D45927 /* TalkPageArchivesViewController.swift in Sources */, + D8CE254E1E698E2400DAE2E0 /* WKWebView+WMFWebViewControllerJavascript.m in Sources */, + 6780CF2A2967690200D45927 /* TalkPageArchivesView.swift in Sources */, + 830AD2BB24D1D615003EEFE6 /* WebPageUserScript.swift in Sources */, + 7A71565C226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift in Sources */, + 67CEF2702351113000D5CA6C /* DiffController.swift in Sources */, + 836BF5702869F9C200B98321 /* TalkPageViewController.swift in Sources */, + D8CE25521E698E2400DAE2E0 /* WMFArticleTextActivitySource.m in Sources */, + 8321FCCD2387231E0079F3C7 /* ViewControllerRouter.swift in Sources */, + D8BDA8BF1E71B8D10031F4BF /* String?+WMFExtras.swift in Sources */, + 67C6F78527E8BC2F00B9C864 /* NotificationsCenterIconType.swift in Sources */, + D8CE25571E698E2400DAE2E0 /* WMFPasswordResetter.swift in Sources */, + D8CE255A1E698E2400DAE2E0 /* UIBarButtonItem+WMFButtonConvenience.m in Sources */, + 67EA9E10228F0358008D9EFD /* OldTalkPageHeaderView.swift in Sources */, + D8CE255B1E698E2400DAE2E0 /* UIViewController+WMFEmptyView.m in Sources */, + B0408C562127F2C100AC76CE /* WMFImageGalleryGradientViews.swift in Sources */, + BA69725B1F2BA2D800E35F78 /* SettingsTableViewSection.swift in Sources */, + 6782DBDA2344EC86003FA21B /* DiffHeaderViewModels.swift in Sources */, + D8CE25631E698E2400DAE2E0 /* NSUserDefaults+WMFApplicationDefaults.swift in Sources */, + 83FBE9701F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift in Sources */, + 83B4CDC020E3DCD6007D5A6E /* SearchViewController.swift in Sources */, + 67112E3F275E603B007A9850 /* NotificationsCenterInboxViewModel.swift in Sources */, + 6734115522735789005B31DA /* OldTalkPageFetcher.swift in Sources */, + 67CE5D20222F70C0007B0A2C /* IconBarButtonItem.swift in Sources */, + D8CE25651E698E2400DAE2E0 /* AboutViewController.m in Sources */, + 679A24052968DBFC008D7686 /* ShiftingNavigationBarView.swift in Sources */, + 00AA5AAE276BF2AE005295B0 /* TextBarButtonItem.swift in Sources */, + B0CD9DE11F70997400051843 /* WMFWelcomeExplorationAnimationView.swift in Sources */, + 7AFA21BC20110D7900E957E7 /* ReadingListHintViewController.swift in Sources */, + 67CEF26A2351111D00D5CA6C /* DiffNetworkModels.swift in Sources */, + D8CE25691E698E2400DAE2E0 /* WMFArticleLanguagesSectionFooter.m in Sources */, + D8CE256B1E698E2400DAE2E0 /* WMFWelcomeContainerViewController.swift in Sources */, + D8CE256C1E698E2400DAE2E0 /* WMFAccountCreator.swift in Sources */, + 0072990828AC44F100DCD2E6 /* TalkPageCellTopicView.swift in Sources */, + 0072991728AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift in Sources */, + 7AB6F10022AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift in Sources */, + 00FCB2C026D8398700F5A47A /* NotificationsCenterCell.swift in Sources */, + 7A393282236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift in Sources */, + 7A9524CC22665E6400C55CDC /* InsertMediaSettingsImageView.swift in Sources */, + B0524B70214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift in Sources */, + 7A2FE55D20517BAE00F92F8F /* EraseSavedArticlesView.swift in Sources */, + 7A6F560621AF527A0076D184 /* TextFormattingInputViewController.swift in Sources */, + 6730FD1028998EFD000E5F40 /* TalkPageReplyComposeContentView.swift in Sources */, + D8CE256D1E698E2400DAE2E0 /* UIViewController+WMFHideKeyboard.swift in Sources */, + BA4524191F324C3100439C42 /* FontSizeSliderViewController.swift in Sources */, + FF921887252F7EA500C39A8F /* ThanksGiving.swift in Sources */, + 0072991C28AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift in Sources */, + 83023C1220E6561900EC7592 /* ViewControllerTransitionsController.swift in Sources */, + 0042813B25E6E841004945B3 /* NYTScalingImageView.m in Sources */, + 6782DBAA2343B7FC003FA21B /* DiffHeaderEditorView.swift in Sources */, + 7A6ED51D20ADBF950001849F /* SettingsFunnel.swift in Sources */, + 00EBB7C927D6878E002025AC /* BarButtonImageStyle.swift in Sources */, + FFA0641B25A943EB00B9460B /* BasicLogger.swift in Sources */, + 673FC3D2273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift in Sources */, + 67282FBF24855B7B00B73E20 /* ArticleContextMenuPresenting.swift in Sources */, + D8CE256F1E698E2400DAE2E0 /* WeakScriptMessageDelegate.swift in Sources */, + 6782DBF9234537D0003FA21B /* DiffHeaderExtendedView.swift in Sources */, + 83B01F9623DB41D7001185F4 /* ArticleViewController+FindInPage.swift in Sources */, + 00FCCBCC2900848300C9ECD2 /* TalkPageViewController+FindInPage.swift in Sources */, + 00E75B7827EB946D00A45B78 /* ReusableCell.swift in Sources */, + 67CCB349299155250032439D /* WMFItemSourceExcludingActivityTypes.swift in Sources */, + 834C269F240D49F400245BE7 /* ReferenceViewController.swift in Sources */, + 7AB809DD22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift in Sources */, + 7AC19E462301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift in Sources */, + B0C7A0831F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift in Sources */, + 7AFC79F921B0367700BB0C50 /* TextFormattingTableViewController.swift in Sources */, + 67B5334328416C0E00C33E13 /* UserDataExportCache.swift in Sources */, + 8361474C24223689003E49D3 /* ArticleViewController+Announcements.swift in Sources */, + 83023C2020E6584F00EC7592 /* SearchTransition.swift in Sources */, + 003AD7302979C512005BDB90 /* EditNoticesViewModel.swift in Sources */, + 677129A224FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift in Sources */, + 676F392A2745FB2000F4D33D /* NotificationsCenterFiltersViewModel.swift in Sources */, + 0042813725E6E841004945B3 /* NYTPhotoDismissalInteractionController.m in Sources */, + 6782DBC22343FDCA003FA21B /* DiffListChangeCell.swift in Sources */, + D8CE25751E698E2400DAE2E0 /* UIView+Animations.swift in Sources */, + 67861A18223C13940044F69D /* FocusNavigationView.swift in Sources */, + 6734EE7322976AE300F00B05 /* InfoBannerView.swift in Sources */, + 00FCB2C526D839A500F5A47A /* NotificationsCenterCellViewModel.swift in Sources */, + 00B0B3D22978745400DD7893 /* EditNoticesFetcher.swift in Sources */, + D8BDA8BE1E71B8C90031F4BF /* WMFDeleteBackwardReportingTextField.swift in Sources */, + 83023C0720E51DDF00EC7592 /* SearchLanguagesBarViewController.swift in Sources */, + D8CE25771E698E2400DAE2E0 /* MWKLanguageLinkFetcher.m in Sources */, + 7ABE173C2239DEF0006BA309 /* WelcomeAnimationView.swift in Sources */, + 7A196F5B21BF199500D9E4B5 /* SectionEditorWebView.swift in Sources */, + 67E50B2D27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift in Sources */, + D8CE257B1E698E2400DAE2E0 /* WMFImageTextActivitySource.swift in Sources */, + 830D71C41F703C980080078B /* ArticleURLListViewController.swift in Sources */, + D8CE257F1E698E2400DAE2E0 /* ToCInteractionFunnel.m in Sources */, + B0524B2A214854E900D8FD8D /* DescriptionWelcomePanelViewController.swift in Sources */, + 7ABE17362239DCF6006BA309 /* WelcomeAnimationViewController.swift in Sources */, + D8A47C9023D7338C002AA823 /* ArticleViewController+TableOfContents.swift in Sources */, + 00D46DAC2889B9250015DE9B /* TalkPageCell.swift in Sources */, + 83B01F8223DB1235001185F4 /* SectionFetcher.swift in Sources */, + 67DC5BE423A017CA00B03A84 /* ArticleViewController.swift in Sources */, + 0030592827DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift in Sources */, + D81445FF1E7093870078D71E /* UIViewController+WMFChildViewController.swift in Sources */, + 6782DB9E2343B7DB003FA21B /* DiffHeaderTitleView.swift in Sources */, + D8CE25821E698E2400DAE2E0 /* WMFCompassView.m in Sources */, + D818D3AC1ED87E8F0076110D /* ArticleLocationCellUpdating.swift in Sources */, + 53A575FC2602C845009835E6 /* WMFAppViewController+Extensions.swift in Sources */, + 67E8B084226A57E900537BC9 /* TalkPageContainerViewController.swift in Sources */, + 7A71566F22697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift in Sources */, + D82117FD1EE58C080076C040 /* MapAnnotation.swift in Sources */, + B0CD9DE01F70997400051843 /* WMFWelcomeIntroductionAnimationView.swift in Sources */, + 7A82898D21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift in Sources */, + 67E8B07B226A57DE00537BC9 /* AccountViewController.swift in Sources */, + 7A32078921E40193009E1677 /* SectionEditorMenuItemsController.swift in Sources */, + 6707C033237DBCEE0017E7B6 /* DiffRevisionTransition.swift in Sources */, + 8382F8D420D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift in Sources */, + 67985A562523D80000EBF353 /* ArticleAsLivingDocController.swift in Sources */, + 00FCCBD12900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift in Sources */, + D8CE25831E698E2400DAE2E0 /* WMFWelcomeIntroductionViewController.swift in Sources */, + 832A7A5C23EA138C00D0A750 /* ArticleViewController+References.swift in Sources */, + 7A49A20221231510005C574C /* CollectionViewFooter.swift in Sources */, + 7A28126420D3F84A009B42B5 /* FeedCardSettingsViewController.swift in Sources */, + D80ED25D1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift in Sources */, + BA4524251F32500C00439C42 /* TextSizeChangeExampleViewController.swift in Sources */, + D8CE25891E698E2400DAE2E0 /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m in Sources */, + D8CE258E1E698E2400DAE2E0 /* UIView+WMFFrameUtils.m in Sources */, + 009B835F298091CD00AABEA3 /* EditNoticesView.swift in Sources */, + D8E6FF6D24056AC300686272 /* ArticleViewController+ContextMenu.swift in Sources */, + 8386BDFC2386D754007EE89D /* SinglePageWebViewController.swift in Sources */, + D8CE258F1E698E2400DAE2E0 /* WMFWelcomeAnimationViewControllers.swift in Sources */, + D8CE25901E698E2400DAE2E0 /* TableOfContentsCell.swift in Sources */, + D8CE25931E698E2400DAE2E0 /* WMFSettingsMenuItem.m in Sources */, + 674E8ABA2382DF020053D206 /* DiffTransformer.swift in Sources */, + 83C06889292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift in Sources */, + B0432345210680A900A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift in Sources */, + 83987AD120E4FB2C00C92C60 /* UISearchBar+Theme.swift in Sources */, + D8CE25951E698E2400DAE2E0 /* UIScrollView+ScrollSubviewToLocation.m in Sources */, + 007CCF1326D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift in Sources */, + 00D280F9247EFFFE006BEE23 /* Date+Extensions.swift in Sources */, + 67B64D592507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift in Sources */, + 832289DC1F7291BA0081A5FB /* SizeThatFitsReusableView.swift in Sources */, + 83C06890292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift in Sources */, + 7AB209FA22FC67B4006FECB4 /* PageHistoryViewController.swift in Sources */, + 6782DBBC2343B861003FA21B /* DiffListViewController.swift in Sources */, + D8CE25961E698E2400DAE2E0 /* MTLValueTransformer+WMFNumericValueTransformer.m in Sources */, + 7AF6F76722395BEC00949393 /* EditingWelcomeViewController.swift in Sources */, + 83C06895292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift in Sources */, + 6734116522739CCB005B31DA /* TalkPageLocalHandler.swift in Sources */, + 837A15F528DA591E00AAC3FC /* TalkPageCache.swift in Sources */, + BA7683C91F30D87F00A487AA /* ProminentSwitch.swift in Sources */, + 7A4B333D2136EDED00C6C820 /* UnderlineButton.swift in Sources */, + B0D4917021F999A3002BBDD3 /* EditSaveViewController.swift in Sources */, + D8C4D3D91FD5D9260089CEC2 /* TUSafariActivity.m in Sources */, + D8CE259C1E698E2400DAE2E0 /* TableOfContentsHeader.swift in Sources */, + B0FFFB2B21C9BED1001E787E /* TextFormattingButton.swift in Sources */, + 7A420DB522A029780005689B /* EditFunnel.swift in Sources */, + 0042811F25E6E841004945B3 /* NYTPhotoTransitionAnimator.m in Sources */, + 00E75B7327EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift in Sources */, + 6761AEE82704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift in Sources */, + D8CE259F1E698E2400DAE2E0 /* UIViewController+WMFScrollToTop.swift in Sources */, + B0524B52214854E900D8FD8D /* DescriptionWelcomeContainerViewController.swift in Sources */, + D81446021E7094290078D71E /* WMFAuthButton.swift in Sources */, + 0042813325E6E841004945B3 /* NYTPhotosDataSource.m in Sources */, + 41CCB67521CC1F9700206B47 /* SavedArticlesCollectionViewController.swift in Sources */, + 8350FC4D20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift in Sources */, + 67C6F7AD27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift in Sources */, + 67C1757828AD4D6000C5ABA4 /* TalkPageDataController.swift in Sources */, + D8CE25A51E698E2400DAE2E0 /* UIApplicationShortcutItem+WMFShortcutItem.m in Sources */, + 67ADEE9723A2CFFB0000CAF7 /* ArticleWebMessagingController.swift in Sources */, + 67C78F7328B6DA1400AC207A /* SwiftUITextView.swift in Sources */, + 83A642762226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift in Sources */, + 7AF8B7432102297A009772CC /* SearchSettingsViewController.swift in Sources */, + 6780CF2F29676AB000D45927 /* ShiftingTopViewsContaining.swift in Sources */, + D81446041E70C2430078D71E /* WMFMapsActivity.swift in Sources */, + 671DF9C725F2AE4F0011799E /* ShortDescriptionController.swift in Sources */, + D8CE25A91E698E2400DAE2E0 /* WMFAuthAccountCreationInfoFetcher.swift in Sources */, + 00E5B3A128EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift in Sources */, + 8367A28020D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift in Sources */, + 7AE99B2921CC4F420092BE7F /* TextSizeFormattingTableViewController.swift in Sources */, + 83927D7C1F70570400051890 /* DisambiguationPagesViewController.swift in Sources */, + D8CE25AB1E698E2400DAE2E0 /* AppDelegate.m in Sources */, + 00DEE61B28AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift in Sources */, + 83E776A420FFA4D700E26A47 /* DetailTransition.swift in Sources */, + 675175DE276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift in Sources */, + D8CE25AD1E698E2400DAE2E0 /* ArticlePlace.swift in Sources */, + D8CE25AF1E698E2400DAE2E0 /* WMFSearchResults.m in Sources */, + 83DE45BA2449C09B00671878 /* SplashScreenViewController.swift in Sources */, + 67D9D1FD29711CA700BFCD4F /* Loadable.swift in Sources */, + 00E2EA9026E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift in Sources */, + 6761AEE32704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift in Sources */, + 7A9F2777225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift in Sources */, + 0042812B25E6E841004945B3 /* NYTPhotoViewController.m in Sources */, + BAC6EEC81F1E519A00228AD0 /* AppearanceSettingsViewController.swift in Sources */, + D8E6FF7D2405AAC400686272 /* ArticleViewController+Analytics.swift in Sources */, + D82E95861F16502E007BD960 /* WMFLanguagesViewController.m in Sources */, + 6761AEF92707E34F00E47BAD /* NotificationsCenterModelController.swift in Sources */, + 672C35EC22D8E7D2007B8D46 /* EmptyViewController.swift in Sources */, + 7A4FE5401FA00AEF009FA199 /* ArticlePeekPreviewViewController.swift in Sources */, + D8CE25B11E698E2400DAE2E0 /* WMFBarButtonItemPopoverMessageViewController.m in Sources */, + 83EDC4C328B424B6007D0192 /* VanishAccountPopUpAlertView.swift in Sources */, + B01E54B0206479CC00374FEE /* ProgressContainer.swift in Sources */, + D8CE25B31E698E2400DAE2E0 /* WMFReferencePageViewController.swift in Sources */, + 6782DBC82343FDE4003FA21B /* DiffListContextCell.swift in Sources */, + D850A53B1F8686DE006FD295 /* WMFThemeableNavigationController.m in Sources */, + B3F21D101EB0EBB4000ED0BB /* PlaceSearchService.swift in Sources */, + D8CE25BC1E698E2400DAE2E0 /* WikiTextSectionUploader.m in Sources */, + 83ACF8E728E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift in Sources */, + 7A0DE50120CEEC760032AB57 /* ExploreFeedSettingsViewController.swift in Sources */, + 7A9F060E2266425700856321 /* InsertMediaSettingsViewController.swift in Sources */, + 7A27E85321B19767001B2D21 /* TextStyleFormattingTableViewController.swift in Sources */, + B010E1A91E723E3600CFE1CD /* WMFAuthLinkLabel.swift in Sources */, + 7AF49F81204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift in Sources */, + B01E3AFA21F986750015B715 /* PreviewWebViewContainer.swift in Sources */, + D8421B54203CC8420040F50B /* DebugReadingListsViewController.swift in Sources */, + D8CE25C01E698E2400DAE2E0 /* UIViewController+WMFDynamicHeightPopoverMessage.m in Sources */, + BAA0D91D1F4F165A00091284 /* PageIssuesTableViewController.swift in Sources */, + D8CE25C61E698E2400DAE2E0 /* WMFWelcomeAnalyticsViewController.swift in Sources */, + 6780D76F2832908F00265F10 /* Notification+NotificationsCenter.swift in Sources */, + 7ADF497C21B45CEE009EA338 /* TextFormattingPlainToolbarView.swift in Sources */, + 830ECAD01FBDD8C00080B1EF /* ReadingListsViewController.swift in Sources */, + 7A6CA28F2289AF2200C7FD47 /* EditLinkViewController.swift in Sources */, + D8CE25C71E698E2400DAE2E0 /* WMFRevisionQueryResults.m in Sources */, + 007CCF0926D5A17200D5EA7C /* NotificationsCenterView.swift in Sources */, + B0CD9DE21F70997400051843 /* WMFWelcomeLanguagesAnimationView.swift in Sources */, + 7A998AC21FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift in Sources */, + D8CE25C81E698E2400DAE2E0 /* WMFAppViewController.m in Sources */, + 7A0161B51FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift in Sources */, + B08624311F72EA1900B770FD /* WMFWelcomeAnimationBackgroundView.swift in Sources */, + 8382F8C820D844C700AE5250 /* ArticleLocationCollectionViewCell.swift in Sources */, + 6741245227E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift in Sources */, + 67E5DA5E2761B0AB00CE827D /* NotificationsCenterFilterView.swift in Sources */, + 7AFEB1BD1FA236A100B8DF32 /* UIViewController+Peekable.swift in Sources */, + 003CD3EB28EF7C77000158E4 /* TalkPageFindInPageSearchController.swift in Sources */, + B0ACB13421265B9C0078C136 /* WMFImageGalleryDescriptionTextView.swift in Sources */, + 7A1469C6220BC223000A20F1 /* EditHintController.swift in Sources */, + D8CE25CC1E698E2400DAE2E0 /* UIView+IBExtras.swift in Sources */, + 007F5C6F275AA74200E4B02C /* StackedImageLabelView.swift in Sources */, + B0524B20214854E900D8FD8D /* DescriptionWelcomeInitialViewController.swift in Sources */, + 6771299124FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift in Sources */, + 67C6F78027E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift in Sources */, + 7A4D227E21B1CD8600D889BD /* TextFormattingTableViewCell.swift in Sources */, + 67C6F7A827E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift in Sources */, + 671DF9CB25F2AE4F0011799E /* WikidataDescriptionController.swift in Sources */, + B09CE59B222F623900067D2A /* WKWebView+EditSelectionJavascript.swift in Sources */, + 8382F8DA20D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift in Sources */, + 67E466FB241BED800014149B /* EditHistoryCompareFunnel.swift in Sources */, + D8CE25CE1E698E2400DAE2E0 /* WMFHamburgerMenuFunnel.m in Sources */, + 8386BDF52386D735007EE89D /* ViewController+URLHandling.swift in Sources */, + D8CE25CF1E698E2400DAE2E0 /* UIViewController+WMFWelcomeStoryboard.swift in Sources */, + 67FBE337297056EB00A2E4AD /* TalkPageArchivesFetcher.swift in Sources */, + 678D29B02729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift in Sources */, + D8E27BA71F82B38500F9D2B3 /* RMessage.m in Sources */, + 00EBB7CE27D6A86A002025AC /* SettingsPresentationDelegate.swift in Sources */, + 006ABEEF2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift in Sources */, + B01E3B0021F98BFF0015B715 /* EditPreviewViewController.swift in Sources */, + D8CE25D01E698E2400DAE2E0 /* WMFLanguageCell.m in Sources */, + 6782DC122346920B003FA21B /* DiffContainerViewModel.swift in Sources */, + 7A6ED50E20ADBF950001849F /* SessionsFunnel.swift in Sources */, + 7A8422542268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift in Sources */, + 6789FA2F22E7790900E43842 /* TalkPage+Extensions.swift in Sources */, + 7AF56C3021DDEC1C00563A9C /* TextFormattingProviding.swift in Sources */, + D8CE25D31E698E2400DAE2E0 /* Array+WMFAllFieldsFilled.swift in Sources */, + 00FCCBC7290082C200C9ECD2 /* TalkPageFindInPageState.swift in Sources */, + D8CE25D41E698E2400DAE2E0 /* NewsViewController.swift in Sources */, + D8CE25D51E698E2400DAE2E0 /* main.m in Sources */, + 67985A882524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift in Sources */, + 7AB809D122675B2300BFAB7C /* ThemeableTextView.swift in Sources */, + D8CE25D71E698E2400DAE2E0 /* WMFDailyStatsLoggingFunnel.m in Sources */, + 83927D821F705B7B00051890 /* SearchResultsViewController.swift in Sources */, + 41FCAA3721C844CB001D8411 /* ReadingListEntryCollectionViewController.swift in Sources */, + 7A1C4990227254EC00230ED2 /* InsertMediaSearchViewController.swift in Sources */, + 672285722540B56D0038E332 /* ReferenceBackLinksViewControllerDelegate.swift in Sources */, + D8CE25D81E698E2400DAE2E0 /* WMFRandomDiceButton.m in Sources */, + 678D79EB235E5959006161FF /* DiffListChangeItemViewModel.swift in Sources */, + 6734EE7722976AED00F00B05 /* ActionButton.swift in Sources */, + 7A6ED51820ADBF950001849F /* UserHistoryFunnel.swift in Sources */, + D8A47C8B23D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift in Sources */, + D8CE25DF1E698E2400DAE2E0 /* WMFFirstRandomViewController.m in Sources */, + D8E6FF7824058AC600686272 /* WMFWebView.m in Sources */, + D8CE25E11E698E2400DAE2E0 /* ArticlePopoverViewController.swift in Sources */, + 7ABE170D2239B5A0006BA309 /* WelcomePageViewController.swift in Sources */, + D8CE25E21E698E2400DAE2E0 /* WMFLogFormatter.m in Sources */, + 67FBE33C29705FC200A2E4AD /* TalkPageArchivesItem.swift in Sources */, + B031032E1F677BEC00E2FCF6 /* WMFWelcomeExplorationViewController.swift in Sources */, + 009C8EC429071E720056A3AC /* NSString+Range.swift in Sources */, + 7A2432BF1FCF401900FB4BA5 /* CreateReadingListViewController.swift in Sources */, + 0042812325E6E841004945B3 /* NSBundle+NYTPhotoViewer.m in Sources */, + D818FEBC21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift in Sources */, + D8CB32AE1E79D8A0008A0966 /* RoundedCornerView.swift in Sources */, + 7AE1D33A1FCD10B900393471 /* SavedViewController.swift in Sources */, + D8CE25E51E698E2400DAE2E0 /* NSString+FormattedAttributedString.m in Sources */, + 67D9D1F22970D88E00BFCD4F /* DisclosureButton.swift in Sources */, + 67E8B088226A57E900537BC9 /* TalkPageReplyListViewController.swift in Sources */, + 7AF56C3621DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift in Sources */, + 7A610CBE220A582A00C266AE /* HintController.swift in Sources */, + 7A2BB1D521F27AC5004C0FDF /* WKWebView+OffsetHack.swift in Sources */, + 83C06884292EC85700DF1403 /* TalkPageFormattingToolbarView.swift in Sources */, + 00E5B3A628EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift in Sources */, + 7A9F061A2266432200856321 /* InsertMediaSettingsTextTableViewCell.swift in Sources */, + D8CE25EA1E698E2400DAE2E0 /* WMFForgotPasswordViewController.swift in Sources */, + D8E6FF6824054FA100686272 /* ArticleViewController+LinkPreviewing.swift in Sources */, + 830D71D01F704DD40080078B /* ArticleFetchedResultsViewController.swift in Sources */, + B0845E1220618DA400CDD98E /* SavedProgressViewController.swift in Sources */, + 00474A3128DD1B13002E3C09 /* TalkPageCoffeeRollView.swift in Sources */, + 00EACEC828E39D470054DDB4 /* TalkPageEmptyView.swift in Sources */, + D8CE25EC1E698E2400DAE2E0 /* WMFImageGalleryViewController.m in Sources */, + 7AF0265722985CB9000E0A06 /* BeKindInputAccessoryView.swift in Sources */, + 00E75B6927EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift in Sources */, + 67DAEDA423CE24DA003AA208 /* SavedArticlesFetcher.swift in Sources */, + 7AB7DEC9227203A600DD61A2 /* InsertMediaViewController.swift in Sources */, + B0CD9DDD1F70997400051843 /* WMFWelcomeAnimationView.swift in Sources */, + B0F929A01F84789D002A0788 /* WMFWelcomeInitialViewController.swift in Sources */, + 00A7946D245CA4E60063BA18 /* ArticleSurveyTimerController.swift in Sources */, + B09705B5236B29D7006FDB5C /* DiffThanker.swift in Sources */, + D8CE25F01E698E2400DAE2E0 /* WMFAccountCreationViewController.swift in Sources */, + 674711852507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift in Sources */, + D8CE25F31E698E2400DAE2E0 /* NSAttributedString+WMFModify.m in Sources */, + D8CE25F41E698E2400DAE2E0 /* WMFSettingsViewController.m in Sources */, + 83B01F9B23DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift in Sources */, + D8A47C8623D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift in Sources */, + D8CE25F61E698E2400DAE2E0 /* UIScrollView+WMFContentOffsetUtils.m in Sources */, + 00F5AED227C6C80C006390A8 /* PushNotificationsSettingsViewController.swift in Sources */, + 83FBE9761F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift in Sources */, + 0042813F25E6E841004945B3 /* NYTPhotosViewController.m in Sources */, + 679471DC275F245000621071 /* NotificationsCenterInboxView.swift in Sources */, + 7A8422482268BBE70074648E /* InsertMediaImageInfoView.swift in Sources */, + 672D69AB273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift in Sources */, + 83836ECD1F615E5B007D1A05 /* ShareViewController.swift in Sources */, + D84DAA171EEEF527008E4B18 /* SWStepSlider.swift in Sources */, + D8CE25F81E698E2400DAE2E0 /* WMFImageGalleryDetailOverlayView.m in Sources */, + B0CD9DE31F70997400051843 /* WMFWelcomeAnalyticsAnimationView.swift in Sources */, + 7A82765A226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift in Sources */, + B068EDE1206B183500C827D1 /* Progress+ProgressUI.swift in Sources */, + 83EE476B20D019A100A21F34 /* ExploreViewController.swift in Sources */, + B0BCF0BA202537D800986F72 /* Panels.swift in Sources */, + 83B01F7D23DB0BA2001185F4 /* ArticleViewController+Editing.swift in Sources */, + 007CCF0E26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift in Sources */, + 7A6ED51320ADBF950001849F /* ReadingListsFunnel.swift in Sources */, + D8CE25FB1E698E2400DAE2E0 /* UIApplication+SystemSettings.swift in Sources */, + B0BCF0AC2023AC7700986F72 /* ScrollableEducationPanelViewController.swift in Sources */, + B0B423691EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift in Sources */, + D8CE25FE1E698E2400DAE2E0 /* WMFArticleRevision.m in Sources */, + 6754E44A22773587005EEAD1 /* OldTalkPageReplyCell.swift in Sources */, + 83F1096023D09F80003F3E9E /* ArticleViewController+WIconPopover.swift in Sources */, + 6782DBA42343B7EE003FA21B /* DiffHeaderSummaryView.swift in Sources */, + D8CE25FF1E698E2400DAE2E0 /* ArticlePlaceView.swift in Sources */, + 7A70797E223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift in Sources */, + 005E004328DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift in Sources */, + D8CE26001E698E2400DAE2E0 /* WMFCaptchaViewController.swift in Sources */, + 7A23CED2211A24FE00441A79 /* FeedFunnel.swift in Sources */, + D8CE26011E698E2400DAE2E0 /* DDLog+WMFLogger.m in Sources */, + 83B01F7823DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift in Sources */, + D8CE26031E698E2400DAE2E0 /* WMFLegacyReference.swift in Sources */, + 672D69A6273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift in Sources */, + 7A13A89A2028BB3600F28254 /* ReadingListsAlertController.swift in Sources */, + FF59DF4F2555E0CB0048E66C /* InternalLinkPreviewing.swift in Sources */, + 0022DD2B25829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift in Sources */, + D8CE26041E698E2400DAE2E0 /* WMFWelcomePageViewController.swift in Sources */, + 0042812725E6E841004945B3 /* NYTPhotoTransitionController.m in Sources */, + 67059DB62260D619009811AA /* SchemeHandler.swift in Sources */, + D8CE26051E698E2400DAE2E0 /* MKCoordinateRegion+Dimensions.swift in Sources */, + D8CE26071E698E2400DAE2E0 /* WMFAlertManager.swift in Sources */, + 67DDD189250C1A27006C0F93 /* ThreeLineHeaderView.swift in Sources */, + D8CE26081E698E2400DAE2E0 /* WMFWelcomeLanguageTableViewController.swift in Sources */, + D8CE260A1E698E2400DAE2E0 /* WMFLoginFunnel.m in Sources */, + D88C70191EE595E90022A26A /* MapView.swift in Sources */, + 679A240A2968E0D0008D7686 /* ShiftingScrollView.swift in Sources */, + 67DAEDDB27E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift in Sources */, + 670F766022B0C48E00D87545 /* FakeProgressLoading.swift in Sources */, + 834CC34C21075B7600F62818 /* UITabBar+Theme.swift in Sources */, + 83B01F7323DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift in Sources */, + 00D9276D29511E95004ECBEA /* PageHistoryCountsView.swift in Sources */, + 0010F93B27A49C7700D77848 /* HorizontalSpacerView.swift in Sources */, + 8320331C22B90528004A9EDA /* NavigationStateController.swift in Sources */, + D8CE260C1E698E2400DAE2E0 /* MapUtilities.swift in Sources */, + D8CE260E1E698E2400DAE2E0 /* WKWebView+ElementLocation.m in Sources */, + 678D79FD235E59B2006161FF /* DiffListUneditedViewModel.swift in Sources */, + 67134A1928A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift in Sources */, + D8CE26101E698E2400DAE2E0 /* UIViewController+WMFStoryboardUtilities.swift in Sources */, + B0016CBA21354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift in Sources */, + 83DB4412244A57590046FABE /* RootNavigationController.swift in Sources */, + 7A73B48321E54B4200249E09 /* SectionEditorNavigationItemController.swift in Sources */, + 006ABEEA2901E8F600722DF8 /* VanishAccountWarningView.swift in Sources */, + 671DF9C325F2AE4E0011799E /* ArticleDescriptionControlling.swift in Sources */, + D8CE26121E698E2400DAE2E0 /* TableOfContentsPresentationController.swift in Sources */, + 7ABE17012239B346006BA309 /* WelcomeViewController.swift in Sources */, + 7ABAD6C020349B91006A364C /* Collection.swift in Sources */, + 67C6F79927E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift in Sources */, + 7AB20A0D22FC8432006FECB4 /* PageHistoryCountsViewController.swift in Sources */, + 671F5E002367EDC600111116 /* GlobalUserInfoFetcher.swift in Sources */, + D80BF0A42347735E00B3B522 /* AppSearchButton.swift in Sources */, + 833B8C8A281AE2110021C12C /* RemoteNotificationsFunnel.swift in Sources */, + 7AC809C621DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift in Sources */, + 676E813529380D8A00F15258 /* TalkPagesFunnel.swift in Sources */, + D8CE26151E698E2400DAE2E0 /* WMFLoginViewController.swift in Sources */, + 67C6F79E27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift in Sources */, + 8330532A23EF0B4200123141 /* ArticleViewController+Media.swift in Sources */, + 8334EC4D286A443B00929DF2 /* TalkPageFetcher.swift in Sources */, + 7A0161E11FE8B4CA00AEDC3D /* TagCollectionViewCell.swift in Sources */, + 8330532F23EF107D00123141 /* MediaListGalleryViewController.swift in Sources */, + D837CC38231FE9CC00BA6130 /* ThemeableViewController.swift in Sources */, + 7AEBAD462102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift in Sources */, + D8CE26191E698E2400DAE2E0 /* PageHistoryFetcher.swift in Sources */, + 83F1096F23D0E787003F3E9E /* RandomArticleViewController.swift in Sources */, + 7ABE17252239BB54006BA309 /* WelcomePanelViewController.swift in Sources */, + 67E8B07F226A57E400537BC9 /* OldTalkPageTopicCell.swift in Sources */, + 678D79F1235E5979006161FF /* DiffListChangeViewModel.swift in Sources */, + D8CE261B1E698E2400DAE2E0 /* CreateAccountFunnel.m in Sources */, + D8CE261D1E698E2400DAE2E0 /* WMFShareFunnel.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D8EC3DD71E9BDA35006712EB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E1CFD6E6210C103900D8E37C /* ExploreCardViewController.swift in Sources */, + D8EC3DD81E9BDA35006712EB /* UserLocationAnnotationView.swift in Sources */, + 7ABAD6B620338CFB006A364C /* ReadingListDetailUnderBarViewController.swift in Sources */, + D8EC3DD91E9BDA35006712EB /* WMFSearchFunnel.m in Sources */, + D8EC3DDA1E9BDA35006712EB /* WMFImageURLActivitySource.swift in Sources */, + 8368BB8624129F3D00BC88BA /* ViewController+ArticleErrorHandling.swift in Sources */, + D8B3D7681EC34F5B00930C21 /* SaveButtonsController.swift in Sources */, + 83F1097523D0F115003F3E9E /* HelpViewController.swift in Sources */, + 7A27EDA42279F5270010CB24 /* InsertLinkViewController.swift in Sources */, + 6782DBCF2343FDF2003FA21B /* DiffListUneditedCell.swift in Sources */, + 007CCF0226D5A10200D5EA7C /* NotificationsCenterViewController.swift in Sources */, + B04AE84E21C6475C00CE51D8 /* WKWebViewWithSettableInputViews.swift in Sources */, + FFD7B85A24B3CA9F005C2471 /* ReferenceShowing.swift in Sources */, + B0524B77214856A400D8FD8D /* UIViewController+DescriptionWelcomeStoryboard.swift in Sources */, + 67D3C455228CB54E001D5741 /* OldTalkPageReplyComposeView.swift in Sources */, + 00AA5AA8276BF29E005295B0 /* StatusTextBarButtonItem.swift in Sources */, + 0042812E25E6E841004945B3 /* NYTPhotoCaptionView.m in Sources */, + B0D3E70E214AF776007578BA /* DescriptionEditViewController.swift in Sources */, + B0F4761D21F921D300C4E254 /* EditSummaryViewController.swift in Sources */, + 7A48EA1021B5C9B20083F3DC /* EditToolbarView.swift in Sources */, + 7A82896A21B3467D005D7EC1 /* TextFormattingDetailTableViewCell.swift in Sources */, + B0EFCD6F1EBF12E5008F36E5 /* LibrariesUsed.swift in Sources */, + 00E2EA8A26E28A9700B1A741 /* NotificationsCenterCellStyle.swift in Sources */, + 00D1F59028885BA300127169 /* TalkPageViewModel.swift in Sources */, + D8EC3DDF1E9BDA35006712EB /* WMFCaptchaResetter.swift in Sources */, + D87676A121E7B73C00491039 /* ToolbarSeparatorView.swift in Sources */, + 676C869426D98D8D00A704C1 /* UIApplication+WindowWorkarounds.swift in Sources */, + 7ABE171A2239B8EE006BA309 /* WelcomeContainerViewController.swift in Sources */, + 00D46DA62889B7F50015DE9B /* TalkPageView.swift in Sources */, + B0CD9DE51F70997400051843 /* WMFWelcomeAnimationExtensions.swift in Sources */, + 83F26B2C220B62EC002D87A4 /* SectionEditorButton.swift in Sources */, + D8EC3DE31E9BDA35006712EB /* PlacesViewController.swift in Sources */, + 67146038243BCE51008CE885 /* ArticleViewController+SurveyAnnouncements.swift in Sources */, + 67C78F7728B7407000AC207A /* VanishAccountFooterView.swift in Sources */, + 00474A2B28DD1AE2002E3C09 /* TalkPageCoffeeRollViewController.swift in Sources */, + 83987AD220E4FB2C00C92C60 /* UISearchBar+Theme.swift in Sources */, + B0C7A0791F710E75008415E7 /* WMFWelcomeExplorationAnimationBackgroundView.swift in Sources */, + 7AC19E342301EF7D00E25B83 /* PageHistoryFilterCountsViewController.swift in Sources */, + 7A29A5D01F6C49C600E8F42B /* CollectionViewUpdater.swift in Sources */, + 7A715663226972DF0066FEC4 /* InsertMediaImageTypeSettingsViewController.swift in Sources */, + 83510B0828F4CF6400B6235B /* TalkPageErrorStateView.swift in Sources */, + D8EC3DE51E9BDA35006712EB /* WMFReferencePanelViewController.swift in Sources */, + 7A71567B22699D5B0066FEC4 /* InsertMediaLabelTableFooterView.swift in Sources */, + D8EC3DE81E9BDA35006712EB /* WikidataFetcher+Places.swift in Sources */, + D8EC3DE91E9BDA35006712EB /* UIButton+WMFButton.m in Sources */, + 67C9FC0028C77F350065A530 /* TalkPageTopicComposeViewController.swift in Sources */, + 67B64D582507DE3E00FA27F3 /* ArticleAsLivingDocSectionHeaderView.swift in Sources */, + 679A23FF2968DAB9008D7686 /* ShiftingTopView.swift in Sources */, + D82E956C1F156F77007BD960 /* UIView+SubviewEnumeration.swift in Sources */, + 7AEC985B219F529000BEF62B /* DefaultEditToolbarView.swift in Sources */, + D8EC3DEA1E9BDA35006712EB /* TableOfContentsAnimator.swift in Sources */, + B0432346210680A900A9A6B6 /* WMFContentGroupKind+FeedCustomization.swift in Sources */, + BA45241A1F324C3100439C42 /* FontSizeSliderViewController.swift in Sources */, + 833D6B4A229EE872003CB650 /* TalkPageTopic+Extensions.swift in Sources */, + 67E8B08D226A57E900537BC9 /* TalkPageTopicNewViewController.swift in Sources */, + D8EC3DED1E9BDA35006712EB /* WMFReferencePageBackgroundView.swift in Sources */, + 83E52BB61F681F940045E776 /* ShareAFactViewController.swift in Sources */, + B0524B49214854E900D8FD8D /* DescriptionWelcomePageViewController.swift in Sources */, + FFD7B84724AEB049005C2471 /* ArticleScrolling.swift in Sources */, + 7A19C64A20DD3440000EF7F7 /* BaseExploreFeedSettingsViewController.swift in Sources */, + 83F1096623D0D4F6003F3E9E /* ArticlePreviewingDelegate.swift in Sources */, + 83B01F9223DB41BE001185F4 /* ArticleViewController+Sharing.swift in Sources */, + 6780CF3429676DE300D45927 /* ShiftingTopViewsStack.swift in Sources */, + 7A1C4997227265CD00230ED2 /* InsertMediaSelectedImageViewController.swift in Sources */, + 00BCB71926DEE11B002C3F72 /* InsetLabelView.swift in Sources */, + D8EC3DEF1E9BDA35006712EB /* UIApplication+RTL.swift in Sources */, + D8EC3DF21E9BDA35006712EB /* WMFWelcomePanelViewController.swift in Sources */, + 0072990C28AC455500DCD2E6 /* TalkPageCellViewModel.swift in Sources */, + 67DDD188250C1A27006C0F93 /* ThreeLineHeaderView.swift in Sources */, + D8533ED71ECF581600E44F86 /* NewsCollectionViewHeader.swift in Sources */, + 7ADF853823516CF500500ADC /* PageHistoryHintController.swift in Sources */, + 7A741DCC207FB9CC00CBAAE2 /* SearchBarExtendedViewController.swift in Sources */, + D8EC3DF51E9BDA35006712EB /* UIViewController+WMFAlerts.swift in Sources */, + 6782DBB12343B812003FA21B /* DiffHeaderCompareView.swift in Sources */, + 83DAA9B223FEB611002D5716 /* ReferenceBackLinksViewController.swift in Sources */, + 678D29B4272AF1DA0036C5D9 /* NotificationsCenterCellViewModel+SheetActionExtensions.swift in Sources */, + 7A9524D922669A8B00C55CDC /* InsertMediaSettingsButtonView.swift in Sources */, + 67BEFFD628AD9DF000606B38 /* TalkPageType.swift in Sources */, + 678E7E8226432F060005439C /* NavigationEventsFunnel.swift in Sources */, + FF2B2111254B7D6A0009E61A /* ActivityIndicatorCollectionViewFooter.swift in Sources */, + 83023C2120E6584F00EC7592 /* SearchTransition.swift in Sources */, + D8EC3DF61E9BDA35006712EB /* NSDate+WMFPOTDTitle.m in Sources */, + D8EC3DF71E9BDA35006712EB /* WMFTableHeaderFooterLabelView.m in Sources */, + 83B4CDC120E3DCD6007D5A6E /* SearchViewController.swift in Sources */, + 8351CE7A20D4424100E32FC1 /* CollectionViewHeader.swift in Sources */, + 832BD3BD28996B68002623CA /* VanishAccountContentView.swift in Sources */, + 83DB0A5B23EEDE2D00DA5F58 /* MWKDataStore+LegacyMobileview.swift in Sources */, + 83FDE79A293564AC006D55FE /* Link.swift in Sources */, + 7A20AE0A2057F39C005FB5DF /* UIView+Identifier.swift in Sources */, + 67EA9E15228F035E008D9EFD /* TalkPageReplyFooterView.swift in Sources */, + 7AE99B3021CC53AB0092BE7F /* TextFontFormattingTableViewController.swift in Sources */, + 7A0FF2CE230343BA00E755D4 /* PageHistoryCollectionViewCell.swift in Sources */, + 00BCB72326DEEB1C002C3F72 /* RoundedImageView.swift in Sources */, + 7A610CB9220A30C900C266AE /* HintViewController.swift in Sources */, + 83E3E7272440F1FE00AA2E9A /* LoadingAnimationViewController.swift in Sources */, + D8E27BA31F82B38100F9D2B3 /* RMessageView.m in Sources */, + 67D9D1F72970D8BE00BFCD4F /* BackgroundHighlightingButtonStyle.swift in Sources */, + FF555566277287F400925099 /* CollectionViewContextMenuShowing.swift in Sources */, + 00BCB71E26DEE1C7002C3F72 /* VerticalSpacerView.swift in Sources */, + D8B1668E1FD97FE000097D8B /* WMFViewController.m in Sources */, + 7AF8CEEF22653406000B7676 /* InsertMediaSelectedImageView.swift in Sources */, + 83F1096B23D0DB0F003F3E9E /* ViewController+ArticlePreviewing.swift in Sources */, + D8EC3DFC1E9BDA35006712EB /* WMFDatabaseHousekeeper.swift in Sources */, + 83A171D72819B6A60029FB89 /* UNAuthorizationStatus+String.swift in Sources */, + 67DC5BEB23A03FE700B03A84 /* ArticleToolbarController.swift in Sources */, + D8EC3DFE1E9BDA35006712EB /* PlaceSearch.swift in Sources */, + 009B8359298091BC00AABEA3 /* EditNoticesViewController.swift in Sources */, + 7AE1FE3321B4A9790068BE9F /* TextFormattingButtonView.swift in Sources */, + D8EC3E001E9BDA35006712EB /* PlaceSearchSuggestionController.swift in Sources */, + B0B423631EF9D6A300D3DC4C /* OnThisDayViewController.swift in Sources */, + 00E75B6D27EB92DE00A45B78 /* NotificationsCenterDetailHeaderCell.swift in Sources */, + D8EC3E021E9BDA35006712EB /* WMFPageHistoryRevision.m in Sources */, + 830C0DD723D9AFBE006471C4 /* UIViewController+Push.swift in Sources */, + 67E8B08C226A57E900537BC9 /* TalkPageTopicListViewController.swift in Sources */, + 83ED2E25289ACB1100462C65 /* VanishAccountCustomUIHostingController.swift in Sources */, + D8EC3E091E9BDA35006712EB /* WMFEmptyView.m in Sources */, + 7A35CB891FD82B6300AAF3B7 /* ReadingListDetailViewController.swift in Sources */, + 7A9A612021124D0F00403154 /* CreateNewReadingListButtonView.swift in Sources */, + 6782DBD52343FE03003FA21B /* DiffListGroupViewModel.swift in Sources */, + 67985A872524E05F00EBF353 /* ArticleAsLivingDocHintViewController.swift in Sources */, + 67E0690B22399D1C008550AC /* ReadingThemesControlsViewController.swift in Sources */, + 67E069122239B33D008550AC /* FindAndReplaceKeyboardBar.swift in Sources */, + 67DC5BF123A1427D00B03A84 /* ActionHandlerScript.swift in Sources */, + D8EC3E0C1E9BDA35006712EB /* WMFSettingsTableViewCell.m in Sources */, + D8EC3E0D1E9BDA35006712EB /* UIView+WMFSubviews.swift in Sources */, + 678D79F8235E599B006161FF /* DiffListContextViewModel.swift in Sources */, + 830378412940E41B00D20E01 /* UITextView+FormattingToolbarExtension.swift in Sources */, + D8EC3E0F1E9BDA35006712EB /* WMFReferencePopoverMessageViewController.m in Sources */, + D8EC3E101E9BDA35006712EB /* WMFSearchFetcher.m in Sources */, + 00CF2EA127DABCA8006EFDDC /* NotificationsCenterOnboardingView.swift in Sources */, + B0524AF32144D7BE00D8FD8D /* DescriptionHelpViewController.swift in Sources */, + 7A715669226974D10066FEC4 /* InsertMediaImageSizeSettingsViewController.swift in Sources */, + D8EC3E121E9BDA35006712EB /* LoggingDefaults.swift in Sources */, + 00E75B6327EB87DC00A45B78 /* NotificationsCenterDetailView.swift in Sources */, + 0072992028AC4D5300DCD2E6 /* TalkPageCellReplyDepthIndicator.swift in Sources */, + 7ADF498921B45E42009EA338 /* TextFormattingGroupedToolbarView.swift in Sources */, + D8EC3E171E9BDA35006712EB /* MWKTitleLanguageController.m in Sources */, + B0016CC521362DB300FA1096 /* SetupGradientView.swift in Sources */, + 6734EE7C22976BA300F00B05 /* TalkPageHintViewController.swift in Sources */, + B0524B67214854E900D8FD8D /* DescriptionWelcomeImageViewController.swift in Sources */, + 6782DB932343B6F9003FA21B /* DiffContainerViewController.swift in Sources */, + 6780D5BC237AF8A10087A5D1 /* DiffToolbarView.swift in Sources */, + 6782DC0D23453D7D003FA21B /* DiffHeaderCompareItemView.swift in Sources */, + D8EC3E181E9BDA35006712EB /* UIView+WMFSnapshotting.m in Sources */, + 0042811A25E6E841004945B3 /* NYTPhotosOverlayView.m in Sources */, + 6707C03A237F0A6E0017E7B6 /* UIFont+Extensions.swift in Sources */, + D8EC3E191E9BDA35006712EB /* UIViewController+WMFStoryboardUtilities.m in Sources */, + D84DAA181EEEF527008E4B18 /* SWStepSlider.swift in Sources */, + 00841DE724477806003CF74A /* AppTabBarDelegate.swift in Sources */, + D8EC3E1A1E9BDA35006712EB /* UIVIewController+WMFCommonRotationSupport.swift in Sources */, + 83E776A520FFA4D700E26A47 /* DetailTransition.swift in Sources */, + 6771299524FF775E00E89CA5 /* ArticleAsLivingDocLargeEventCollectionViewCell.swift in Sources */, + 00097D5D29660FF3000B3514 /* View+Extensions.swift in Sources */, + D8EC3E1B1E9BDA35006712EB /* (null) in Sources */, + B0C7A0851F710EB1008415E7 /* WMFWelcomeAnalyticsAnimationBackgroundView.swift in Sources */, + D8EC3E1D1E9BDA35006712EB /* WMFTitleInsetRespectingButton.m in Sources */, + 8356115E28D4FCEC00E95E6E /* NSMutableAttributedString+RemoveNewLine.swift in Sources */, + 6798332B22C3F2950073CE6F /* UITextView+Extensions.swift in Sources */, + 7A5A0545225FBE0500BBEAC1 /* InsertMediaSearchResultCollectionViewCell.swift in Sources */, + D8EC3E1E1E9BDA35006712EB /* WMFChangePasswordViewController.swift in Sources */, + 00E75B5E27EB874A00A45B78 /* NotificationsCenterDetailViewController.swift in Sources */, + D8B166871FD97A0500097D8B /* ViewController.swift in Sources */, + 83C0656D23D23220001821BC /* TableOfContentsItem.swift in Sources */, + B0421AA4206991F500C22630 /* SavedTabBarItemProgressBadgeManager.swift in Sources */, + 7A6ED51E20ADBF950001849F /* LoginFunnel.swift in Sources */, + 6771299024FF76AC00E89CA5 /* ArticleAsLivingDocViewController.swift in Sources */, + 679A23FA2968D865008D7686 /* ShiftingTopViewsData.swift in Sources */, + 7A0CD24221DFA34100066F68 /* TextFormattingToolbarView.swift in Sources */, + D8EC3E251E9BDA35006712EB /* WMFArticleRevisionFetcher.m in Sources */, + D8EC3E261E9BDA35006712EB /* WMFArticleLanguagesSectionHeader.m in Sources */, + 00CB6899288B0CD3002EBB0A /* TalkPageHeaderView.swift in Sources */, + 7A29A5CA1F6C405900E8F42B /* HistoryViewController.swift in Sources */, + 7AA96D5E21B733A800DE9877 /* TextFormattingProvidingTableViewController.swift in Sources */, + 677129A124FFF43000E89CA5 /* ArticleAsLivingDocHorizontallyScrollingCell.swift in Sources */, + 67C6F79327E8C03A00B9C864 /* NotificationsCenterAction.swift in Sources */, + D8EC3E2C1E9BDA35006712EB /* WMFTwoFactorPasswordViewController.swift in Sources */, + 6798331C22C174F00073CE6F /* LinkOnlyTextView.swift in Sources */, + D8EC3E2E1E9BDA35006712EB /* SavedPagesFunnel.m in Sources */, + 7A16C4E8212D941C00F0D5EC /* SubSettingsViewController.swift in Sources */, + 7A25367921B5AA4F00F841A1 /* ContextualHighlightEditToolbarView.swift in Sources */, + 7A203F0D1FDEDCDD00A229EC /* ReadingListHintController.swift in Sources */, + D8EC3E301E9BDA35006712EB /* WMFChange.m in Sources */, + 67C6F7A227E8C7C500B9C864 /* NotificationsCenterCommonViewModel+ActionExtensions.swift in Sources */, + 672428992362113900490629 /* DiffFetcher.swift in Sources */, + D8EC3E321E9BDA35006712EB /* MWKSearchRedirectMapping.m in Sources */, + D8EC3E331E9BDA35006712EB /* MWKImageInfoFetcher+PicOfTheDayInfo.m in Sources */, + 8382F8CF20D9206000AE5250 /* ImageCollectionViewCell.swift in Sources */, + 7A7AC84821B6B89B003B849B /* SectionEditorViewController.swift in Sources */, + D818D3831ED7254D0076110D /* ColumnarCollectionViewController.swift in Sources */, + D8EC3E381E9BDA35006712EB /* WMFScrollViewController.swift in Sources */, + 83E52BC11F682E3E0045E776 /* LicenseView.swift in Sources */, + 7AFEB3F71FE8511700D7BC57 /* SavedArticlesCollectionViewCell.swift in Sources */, + 67E0690F22399D2E008550AC /* ReadingThemesControlsProtocols.swift in Sources */, + 7ADEAB051FD75B9100BB4727 /* AddArticlesToReadingListViewController.swift in Sources */, + 6734115B22735833005B31DA /* OldTalkPagesController.swift in Sources */, + 7AF8B7442102297A009772CC /* SearchSettingsViewController.swift in Sources */, + 6780CF242967683800D45927 /* TalkPageArchivesViewController.swift in Sources */, + 7A1469BF220BBE44000A20F1 /* EditHintViewController.swift in Sources */, + 6780CF292967690200D45927 /* TalkPageArchivesView.swift in Sources */, + D8EC3E3E1E9BDA35006712EB /* PageHistorySection.swift in Sources */, + 7A71565D226964500066FEC4 /* InsertMediaImagePositionSettingsViewController.swift in Sources */, + D818D3AD1ED87E8F0076110D /* ArticleLocationCellUpdating.swift in Sources */, + 836BF56F2869F9C200B98321 /* TalkPageViewController.swift in Sources */, + 67CEF2712351113000D5CA6C /* DiffController.swift in Sources */, + D82117FE1EE58C080076C040 /* MapAnnotation.swift in Sources */, + D8EC3E421E9BDA35006712EB /* TableOfContentsViewController.swift in Sources */, + 67C6F78427E8BC2F00B9C864 /* NotificationsCenterIconType.swift in Sources */, + 8321FCCE2387231E0079F3C7 /* ViewControllerRouter.swift in Sources */, + D8EC3E441E9BDA35006712EB /* ProtectedEditAttemptFunnel.m in Sources */, + D8EC3E451E9BDA35006712EB /* WKWebView+WMFWebViewControllerJavascript.m in Sources */, + 67EA9E11228F0359008D9EFD /* OldTalkPageHeaderView.swift in Sources */, + B0408C572127F2C100AC76CE /* WMFImageGalleryGradientViews.swift in Sources */, + D8EC3E491E9BDA35006712EB /* WMFArticleTextActivitySource.m in Sources */, + 6782DBDB2344EC86003FA21B /* DiffHeaderViewModels.swift in Sources */, + D8EC3E4C1E9BDA35006712EB /* String?+WMFExtras.swift in Sources */, + D8EC3E4F1E9BDA35006712EB /* WMFPasswordResetter.swift in Sources */, + BA4524261F32500C00439C42 /* TextSizeChangeExampleViewController.swift in Sources */, + 67112E3E275E603B007A9850 /* NotificationsCenterInboxViewModel.swift in Sources */, + BA7683C81F30D87E00A487AA /* ProminentSwitch.swift in Sources */, + 83FBE9711F6172ED0026C7EB /* ShareAFactActivityTextItemProvider.swift in Sources */, + 673411562273578A005B31DA /* OldTalkPageFetcher.swift in Sources */, + 679A24042968DBFC008D7686 /* ShiftingNavigationBarView.swift in Sources */, + 00AA5AAD276BF2AE005295B0 /* TextBarButtonItem.swift in Sources */, + 67CE5D22222F70C0007B0A2C /* IconBarButtonItem.swift in Sources */, + D8EC3E521E9BDA35006712EB /* UIBarButtonItem+WMFButtonConvenience.m in Sources */, + D8EC3E531E9BDA35006712EB /* UIViewController+WMFEmptyView.m in Sources */, + B0CD9DE81F70997400051843 /* WMFWelcomeExplorationAnimationView.swift in Sources */, + 67CEF26B2351111D00D5CA6C /* DiffNetworkModels.swift in Sources */, + D88C701A1EE595E90022A26A /* MapView.swift in Sources */, + 0072990728AC44F100DCD2E6 /* TalkPageCellTopicView.swift in Sources */, + 0072991628AC49FA00DCD2E6 /* TalkPageCellCommentSeparator.swift in Sources */, + D8EC3E5B1E9BDA35006712EB /* NSUserDefaults+WMFApplicationDefaults.swift in Sources */, + 00FCB2BF26D8398700F5A47A /* NotificationsCenterCell.swift in Sources */, + 7AFA21BD20110D7900E957E7 /* ReadingListHintViewController.swift in Sources */, + 7AB6F10122AEF4DF00F552B4 /* UIActivity.ActivityType+CustomTypes.swift in Sources */, + 7A393283236CBDD500A89C2F /* PageHistoryComparisonSelectionViewController.swift in Sources */, + 7A9524CD22665E6400C55CDC /* InsertMediaSettingsImageView.swift in Sources */, + B0524B71214854E900D8FD8D /* DescriptionWelcomeContentsViewController.swift in Sources */, + 6730FD0F28998EFD000E5F40 /* TalkPageReplyComposeContentView.swift in Sources */, + D8EC3E5D1E9BDA35006712EB /* AboutViewController.m in Sources */, + 7A6F560721AF527A0076D184 /* TextFormattingInputViewController.swift in Sources */, + D818D38D1ED765470076110D /* ArticleLocationCollectionViewController.swift in Sources */, + 0072991B28AC4C8B00DCD2E6 /* TalkPageCellCommentView.swift in Sources */, + 7A2FE55E20517BAE00F92F8F /* EraseSavedArticlesView.swift in Sources */, + 0042813A25E6E841004945B3 /* NYTScalingImageView.m in Sources */, + 6782DBAB2343B7FC003FA21B /* DiffHeaderEditorView.swift in Sources */, + 67282FBE24855B7B00B73E20 /* ArticleContextMenuPresenting.swift in Sources */, + 00EBB7C827D6878E002025AC /* BarButtonImageStyle.swift in Sources */, + FFA0641A25A943EB00B9460B /* BasicLogger.swift in Sources */, + 673FC3D1273F0EBE006E11AA /* WMFTableHeaderFooterLabelView+Extensions.swift in Sources */, + D8EC3E611E9BDA35006712EB /* WMFArticleLanguagesSectionFooter.m in Sources */, + 7A6ED51920ADBF950001849F /* SettingsFunnel.swift in Sources */, + 6782DBFC234537D0003FA21B /* DiffHeaderExtendedView.swift in Sources */, + 834C26A0240D49F400245BE7 /* ReferenceViewController.swift in Sources */, + 00FCCBCB2900848300C9ECD2 /* TalkPageViewController+FindInPage.swift in Sources */, + 00E75B7727EB946D00A45B78 /* ReusableCell.swift in Sources */, + 67CCB348299155230032439D /* WMFItemSourceExcludingActivityTypes.swift in Sources */, + 83B01F9723DB41D7001185F4 /* ArticleViewController+FindInPage.swift in Sources */, + 7AB809DE22679F9300BFAB7C /* InsertMediaAdvancedSettingsViewController.swift in Sources */, + 7AC19E472301F79700E25B83 /* PageHistoryFilterCountCollectionViewCell.swift in Sources */, + B0C7A07F1F710E94008415E7 /* WMFWelcomeLanguagesAnimationBackgroundView.swift in Sources */, + 8361474D24223689003E49D3 /* ArticleViewController+Announcements.swift in Sources */, + 67B5334228416C0E00C33E13 /* UserDataExportCache.swift in Sources */, + 7AFC79FA21B0367700BB0C50 /* TextFormattingTableViewController.swift in Sources */, + 6782DBC32343FDCA003FA21B /* DiffListChangeCell.swift in Sources */, + 003AD72F2979C512005BDB90 /* EditNoticesViewModel.swift in Sources */, + D8EC3E631E9BDA35006712EB /* WMFWelcomeContainerViewController.swift in Sources */, + 676F39292745FB2000F4D33D /* NotificationsCenterFiltersViewModel.swift in Sources */, + 0042813625E6E841004945B3 /* NYTPhotoDismissalInteractionController.m in Sources */, + 67861A19223C13940044F69D /* FocusNavigationView.swift in Sources */, + 6734EE7422976AE300F00B05 /* InfoBannerView.swift in Sources */, + D8EC3E641E9BDA35006712EB /* WMFAccountCreator.swift in Sources */, + D8EC3E651E9BDA35006712EB /* UIViewController+WMFHideKeyboard.swift in Sources */, + 00FCB2C426D839A500F5A47A /* NotificationsCenterCellViewModel.swift in Sources */, + 00B0B3D12978745400DD7893 /* EditNoticesFetcher.swift in Sources */, + 674711842507253500287951 /* ArticleAsLivingDocSnippetCollectionViewCell.swift in Sources */, + D8EC3E671E9BDA35006712EB /* WeakScriptMessageDelegate.swift in Sources */, + D8EC3E6D1E9BDA35006712EB /* UIView+Animations.swift in Sources */, + 7ABE173D2239DEF0006BA309 /* WelcomeAnimationView.swift in Sources */, + D8EC3E6E1E9BDA35006712EB /* WMFDeleteBackwardReportingTextField.swift in Sources */, + 67E50B2C27EAD3AD00ABA159 /* NotificationsCenterDetailViewModel+ImageExtensions.swift in Sources */, + 7A196F5C21BF199500D9E4B5 /* SectionEditorWebView.swift in Sources */, + 830D71C51F703C980080078B /* ArticleURLListViewController.swift in Sources */, + D8EC3E701E9BDA35006712EB /* MWKLanguageLinkFetcher.m in Sources */, + D8EC3E741E9BDA35006712EB /* WMFImageTextActivitySource.swift in Sources */, + 7ABE17372239DCF6006BA309 /* WelcomeAnimationViewController.swift in Sources */, + D8A47C9123D7338C002AA823 /* ArticleViewController+TableOfContents.swift in Sources */, + 00D46DAB2889B9250015DE9B /* TalkPageCell.swift in Sources */, + 83B01F8323DB1235001185F4 /* SectionFetcher.swift in Sources */, + 67DC5BE523A017CA00B03A84 /* ArticleViewController.swift in Sources */, + 0030592727DBC4CF00E96757 /* NotificationsCenterOnboardingHostingViewController.swift in Sources */, + B0524B2B214854E900D8FD8D /* DescriptionWelcomePanelViewController.swift in Sources */, + 6782DB9F2343B7DB003FA21B /* DiffHeaderTitleView.swift in Sources */, + B0CD9DE71F70997400051843 /* WMFWelcomeIntroductionAnimationView.swift in Sources */, + B0B4236A1EF9D6D500D3DC4C /* OnThisDayViewControllerHeader.swift in Sources */, + 67E8B08B226A57E900537BC9 /* TalkPageContainerViewController.swift in Sources */, + 53A575FB2602C845009835E6 /* WMFAppViewController+Extensions.swift in Sources */, + 7A71567022697AAF0066FEC4 /* InsertMediaCustomImageSizeSettingTableViewCell.swift in Sources */, + D8EC3E7A1E9BDA35006712EB /* ToCInteractionFunnel.m in Sources */, + 7A82898E21B34B86005D7EC1 /* TextFormattingCustomViewTableViewCell.swift in Sources */, + 67E8B07C226A57DE00537BC9 /* AccountViewController.swift in Sources */, + 7A32078A21E40193009E1677 /* SectionEditorMenuItemsController.swift in Sources */, + 6707C034237DBCEE0017E7B6 /* DiffRevisionTransition.swift in Sources */, + 8382F8D520D928BF00AE5250 /* ColumnarCollectionViewControllerLayoutCache.swift in Sources */, + D8EC3E7D1E9BDA35006712EB /* UIViewController+WMFChildViewController.swift in Sources */, + 832A7A5D23EA138C00D0A750 /* ArticleViewController+References.swift in Sources */, + 00FCCBD02900A6C500C9ECD2 /* NSMutableAttributedString+Highlight.swift in Sources */, + 7A49A20321231510005C574C /* CollectionViewFooter.swift in Sources */, + 7A28126520D3F84A009B42B5 /* FeedCardSettingsViewController.swift in Sources */, + D8EC3E7E1E9BDA35006712EB /* WMFCompassView.m in Sources */, + D8EC3E7F1E9BDA35006712EB /* WMFWelcomeIntroductionViewController.swift in Sources */, + D8EC3E851E9BDA35006712EB /* UIImageView+WMFFaceDetectionBasedOnUIApplicationSharedApplication.m in Sources */, + D8EC3E8A1E9BDA35006712EB /* UIView+WMFFrameUtils.m in Sources */, + D8E6FF6E24056AC300686272 /* ArticleViewController+ContextMenu.swift in Sources */, + 8386BDFD2386D754007EE89D /* SinglePageWebViewController.swift in Sources */, + 009B835E298091CD00AABEA3 /* EditNoticesView.swift in Sources */, + D8EC3E8B1E9BDA35006712EB /* WMFWelcomeAnimationViewControllers.swift in Sources */, + D8EC3E8C1E9BDA35006712EB /* TableOfContentsCell.swift in Sources */, + FFD7B85724B3B39A005C2471 /* ReferenceBackLinksViewControllerDelegate.swift in Sources */, + 832289DD1F7291BA0081A5FB /* SizeThatFitsReusableView.swift in Sources */, + 674E8ABB2382DF030053D206 /* DiffTransformer.swift in Sources */, + 83AE1C821F34BB5A004B62E0 /* ImageDimmingExampleViewController.swift in Sources */, + 83C06888292EE5C600DF1403 /* TalkPageFormattingToolbarViewDelegate.swift in Sources */, + D8EC3E8E1E9BDA35006712EB /* WMFSettingsMenuItem.m in Sources */, + D8EC3E901E9BDA35006712EB /* UIScrollView+ScrollSubviewToLocation.m in Sources */, + 00D280F8247EFFFE006BEE23 /* Date+Extensions.swift in Sources */, + 007CCF1226D5BF1300D5EA7C /* NotificationsCenterPresentationDelegate.swift in Sources */, + D8EC3E911E9BDA35006712EB /* MTLValueTransformer+WMFNumericValueTransformer.m in Sources */, + 7AB209FB22FC67B4006FECB4 /* PageHistoryViewController.swift in Sources */, + 6782DBBD2343B861003FA21B /* DiffListViewController.swift in Sources */, + 83C0688F292EEDAF00DF1403 /* TalkPageViewController+TalkPageFormattingToolbar.swift in Sources */, + D8EC3E971E9BDA35006712EB /* TableOfContentsHeader.swift in Sources */, + 7AF6F76822395BEC00949393 /* EditingWelcomeViewController.swift in Sources */, + 6734116622739CCB005B31DA /* TalkPageLocalHandler.swift in Sources */, + 7A4B333E2136EDED00C6C820 /* UnderlineButton.swift in Sources */, + 83C06894292EEF4A00DF1403 /* TalkPageTopicComposeViewController+TalkPageFormattingToolbar.swift in Sources */, + D8C4D3DA1FD5D9260089CEC2 /* TUSafariActivity.m in Sources */, + 837A15F428DA591E00AAC3FC /* TalkPageCache.swift in Sources */, + B0D4917121F999A3002BBDD3 /* EditSaveViewController.swift in Sources */, + 67985A552523D80000EBF353 /* ArticleAsLivingDocController.swift in Sources */, + 83023C0820E51DDF00EC7592 /* SearchLanguagesBarViewController.swift in Sources */, + D8EC3E9A1E9BDA35006712EB /* UIViewController+WMFScrollToTop.swift in Sources */, + B0FFFB2C21C9BED1001E787E /* TextFormattingButton.swift in Sources */, + 7A420DB622A029780005689B /* EditFunnel.swift in Sources */, + D8EC3E9C1E9BDA35006712EB /* WMFAuthButton.swift in Sources */, + 0042811E25E6E841004945B3 /* NYTPhotoTransitionAnimator.m in Sources */, + 00E75B7227EB92F600A45B78 /* NotificationsCenterDetailContentCell.swift in Sources */, + 6761AEE72704FD3F00E47BAD /* NotificationsCenterCellViewModel+IconNameExtensions.swift in Sources */, + B0524B53214854E900D8FD8D /* DescriptionWelcomeContainerViewController.swift in Sources */, + BA69725A1F2BA2D800E35F78 /* SettingsTableViewSection.swift in Sources */, + 41CCB67621CC1F9700206B47 /* SavedArticlesCollectionViewController.swift in Sources */, + 0042813225E6E841004945B3 /* NYTPhotosDataSource.m in Sources */, + 8350FC4E20DA937B00C19D60 /* ArticleLocationAuthorizationCollectionViewCell.swift in Sources */, + D8EC3EA11E9BDA35006712EB /* UIApplicationShortcutItem+WMFShortcutItem.m in Sources */, + 67C6F7AC27E8D22400B9C864 /* NotificationsCenterDetailViewModel.swift in Sources */, + 67C1757728AD4D6000C5ABA4 /* TalkPageDataController.swift in Sources */, + 67ADEE9823A2CFFB0000CAF7 /* ArticleWebMessagingController.swift in Sources */, + 83A642772226CCF1004A1796 /* SwiftKVOCrashWorkaround.swift in Sources */, + 67C78F7228B6DA1400AC207A /* SwiftUITextView.swift in Sources */, + 83927D7D1F70570400051890 /* DisambiguationPagesViewController.swift in Sources */, + D8EC3EA51E9BDA35006712EB /* WMFMapsActivity.swift in Sources */, + 6780CF2E29676AB000D45927 /* ShiftingTopViewsContaining.swift in Sources */, + 8367A28120D293BE00249A92 /* ColumnarCollectionViewLayoutManager.swift in Sources */, + 671DF9C625F2AE4F0011799E /* ShortDescriptionController.swift in Sources */, + D8EC3EA61E9BDA35006712EB /* WMFAuthAccountCreationInfoFetcher.swift in Sources */, + 00E5B3A028EB8E2100D2C51A /* TalkPageTopicReplyOnboardingView.swift in Sources */, + 6747117F250703BB00287951 /* ArticleAsLivingDocReferenceCollectionViewCell.swift in Sources */, + 7AE99B2A21CC4F420092BE7F /* TextSizeFormattingTableViewController.swift in Sources */, + BAC6EEC91F1E519B00228AD0 /* AppearanceSettingsViewController.swift in Sources */, + D82E95871F16502E007BD960 /* WMFLanguagesViewController.m in Sources */, + 00DEE61A28AD6C9500A60DF9 /* TalkPageCellCommentViewModel.swift in Sources */, + D8EC3EA81E9BDA35006712EB /* AppDelegate.m in Sources */, + 675175DD276D3B9700CD2974 /* DisappearingCallbackNavigationController.swift in Sources */, + D8EC3EAA1E9BDA35006712EB /* ArticlePlace.swift in Sources */, + D8EC3EAC1E9BDA35006712EB /* WMFSearchResults.m in Sources */, + 83DE45BB2449C09B00671878 /* SplashScreenViewController.swift in Sources */, + 67D9D1FC29711CA700BFCD4F /* Loadable.swift in Sources */, + 00E2EA8F26E2A45C00B1A741 /* NotificationsCenterCellDisplayState.swift in Sources */, + 6761AEE22704DE4100E47BAD /* NotificationsCenterCellViewModel+TextExtensions.swift in Sources */, + 7A9F2778225E3462002119B3 /* InsertMediaSearchResultsCollectionViewController.swift in Sources */, + 0042812A25E6E841004945B3 /* NYTPhotoViewController.m in Sources */, + 7A4FE5411FA00AF0009FA199 /* ArticlePeekPreviewViewController.swift in Sources */, + D8E6FF7E2405AAC400686272 /* ArticleViewController+Analytics.swift in Sources */, + D8EC3EAE1E9BDA35006712EB /* WMFBarButtonItemPopoverMessageViewController.m in Sources */, + 6761AEF82707E34F00E47BAD /* NotificationsCenterModelController.swift in Sources */, + 672C35ED22D8E7D2007B8D46 /* EmptyViewController.swift in Sources */, + 67E2E490250452E60070F12D /* ArticleAsLivingDocHeaderView.swift in Sources */, + B01E54B1206479CC00374FEE /* ProgressContainer.swift in Sources */, + 83EDC4C228B424B5007D0192 /* VanishAccountPopUpAlertView.swift in Sources */, + 83023C1320E6561900EC7592 /* ViewControllerTransitionsController.swift in Sources */, + D8EC3EB01E9BDA35006712EB /* WMFReferencePageViewController.swift in Sources */, + D850A53C1F8686DE006FD295 /* WMFThemeableNavigationController.m in Sources */, + 6782DBC92343FDE4003FA21B /* DiffListContextCell.swift in Sources */, + B3F21D111EB0EBB4000ED0BB /* PlaceSearchService.swift in Sources */, + D8EC3EB91E9BDA35006712EB /* WikiTextSectionUploader.m in Sources */, + 83ACF8E628E504CF000F3B6F /* SharedContainerCacheHousekeeping.swift in Sources */, + 7A0DE50220CEEC760032AB57 /* ExploreFeedSettingsViewController.swift in Sources */, + D8EC3EBB1E9BDA35006712EB /* WMFAuthLinkLabel.swift in Sources */, + 7A9F060F2266425700856321 /* InsertMediaSettingsViewController.swift in Sources */, + 7A27E85421B19767001B2D21 /* TextStyleFormattingTableViewController.swift in Sources */, + 7AF49F82204EEDCD00578861 /* StorageAndSyncingSettingsViewController.swift in Sources */, + BAA0D91E1F4F165A00091284 /* PageIssuesTableViewController.swift in Sources */, + B01E3AFB21F986750015B715 /* PreviewWebViewContainer.swift in Sources */, + D8421B55203CC8420040F50B /* DebugReadingListsViewController.swift in Sources */, + B0CD9DE91F70997400051843 /* WMFWelcomeLanguagesAnimationView.swift in Sources */, + D8EC3EBE1E9BDA35006712EB /* UIViewController+WMFDynamicHeightPopoverMessage.m in Sources */, + 6780D76E2832908E00265F10 /* Notification+NotificationsCenter.swift in Sources */, + 830ECAD11FBDD8C00080B1EF /* ReadingListsViewController.swift in Sources */, + 7ADF497D21B45CEE009EA338 /* TextFormattingPlainToolbarView.swift in Sources */, + B08624321F72EA1900B770FD /* WMFWelcomeAnimationBackgroundView.swift in Sources */, + 7A6CA2902289AF2200C7FD47 /* EditLinkViewController.swift in Sources */, + 007CCF0826D5A17200D5EA7C /* NotificationsCenterView.swift in Sources */, + D8EC3EC41E9BDA35006712EB /* WMFWelcomeAnalyticsViewController.swift in Sources */, + 7A998AC31FE20F3B007FE06E /* CollectionViewEditControllerNavigationDelegate+Extensions.swift in Sources */, + D8EC3EC51E9BDA35006712EB /* WMFRevisionQueryResults.m in Sources */, + 7A0161B61FE85C6000AEDC3D /* ReadingListsCollectionViewCell.swift in Sources */, + D8EC3EC61E9BDA35006712EB /* WMFAppViewController.m in Sources */, + 8382F8C920D844C700AE5250 /* ArticleLocationCollectionViewCell.swift in Sources */, + 6741245127E97DBC0071177D /* NotificationsCenterDetailViewModel+ActionExtensions.swift in Sources */, + 67E5DA5D2761B0AB00CE827D /* NotificationsCenterFilterView.swift in Sources */, + 7AFEB1BE1FA236A100B8DF32 /* UIViewController+Peekable.swift in Sources */, + 003CD3EA28EF7C77000158E4 /* TalkPageFindInPageSearchController.swift in Sources */, + B0ACB13521265B9D0078C136 /* WMFImageGalleryDescriptionTextView.swift in Sources */, + D8EC3ECA1E9BDA35006712EB /* UIView+IBExtras.swift in Sources */, + 7A1469C7220BC223000A20F1 /* EditHintController.swift in Sources */, + 007F5C6E275AA74200E4B02C /* StackedImageLabelView.swift in Sources */, + 8382F8DB20D9371E00AE5250 /* WMFContentGroup+DetailViewControllers.swift in Sources */, + B0524B21214854E900D8FD8D /* DescriptionWelcomeInitialViewController.swift in Sources */, + 67C6F77F27E3BAC700B9C864 /* NotificationsCenterFlowHostingController.swift in Sources */, + 7A4D227F21B1CD8600D889BD /* TextFormattingTableViewCell.swift in Sources */, + 67C6F7A727E8CB9000B9C864 /* NotificationsCenterCommonViewModel+TextExtensions.swift in Sources */, + 671DF9CA25F2AE4F0011799E /* WikidataDescriptionController.swift in Sources */, + B09CE59C222F623900067D2A /* WKWebView+EditSelectionJavascript.swift in Sources */, + D8EC3ECC1E9BDA35006712EB /* WMFHamburgerMenuFunnel.m in Sources */, + 67E466FC241BED800014149B /* EditHistoryCompareFunnel.swift in Sources */, + D8EC3ECD1E9BDA35006712EB /* UIViewController+WMFWelcomeStoryboard.swift in Sources */, + 8386BDF72386D735007EE89D /* ViewController+URLHandling.swift in Sources */, + D8E27BA81F82B38600F9D2B3 /* RMessage.m in Sources */, + 67FBE336297056EB00A2E4AD /* TalkPageArchivesFetcher.swift in Sources */, + 678D29AF2729F0580036C5D9 /* NotificationsCenterCellViewModel+LinkExtensions.swift in Sources */, + D8EC3ECE1E9BDA35006712EB /* WMFLanguageCell.m in Sources */, + 00EBB7CD27D6A86A002025AC /* SettingsPresentationDelegate.swift in Sources */, + 006ABEEE2901E92D00722DF8 /* VanishAccountWarningViewHostingViewController.swift in Sources */, + B01E3B0121F98BFF0015B715 /* EditPreviewViewController.swift in Sources */, + 7A6ED50A20ADBF950001849F /* SessionsFunnel.swift in Sources */, + 6782DC132346920B003FA21B /* DiffContainerViewModel.swift in Sources */, + 7A8422552268DA2C0074648E /* InsertMediaSearchResultPreviewingViewController.swift in Sources */, + 6789FA3022E7790900E43842 /* TalkPage+Extensions.swift in Sources */, + 7AF56C3121DDEC1C00563A9C /* TextFormattingProviding.swift in Sources */, + D8EC3ED11E9BDA35006712EB /* Array+WMFAllFieldsFilled.swift in Sources */, + 83927D831F705B7B00051890 /* SearchResultsViewController.swift in Sources */, + 00FCCBC6290082C200C9ECD2 /* TalkPageFindInPageState.swift in Sources */, + D8EC3ED21E9BDA35006712EB /* NewsViewController.swift in Sources */, + D8EC3ED31E9BDA35006712EB /* main.m in Sources */, + 7AB809D222675B2300BFAB7C /* ThemeableTextView.swift in Sources */, + D8EC3ED51E9BDA35006712EB /* WMFDailyStatsLoggingFunnel.m in Sources */, + D8EC3ED61E9BDA35006712EB /* WMFRandomDiceButton.m in Sources */, + 41FCAA3821C844CB001D8411 /* ReadingListEntryCollectionViewController.swift in Sources */, + 7A1C4991227254EC00230ED2 /* InsertMediaSearchViewController.swift in Sources */, + B031032F1F677BED00E2FCF6 /* WMFWelcomeExplorationViewController.swift in Sources */, + 678D79EC235E595A006161FF /* DiffListChangeItemViewModel.swift in Sources */, + D858C7B8210B91CE0039E0C9 /* PassthroughView.swift in Sources */, + 6734EE7822976AED00F00B05 /* ActionButton.swift in Sources */, + D8A47C8C23D728A4002AA823 /* ArticleTableOfContentsDisplayController.swift in Sources */, + 7A6ED51420ADBF950001849F /* UserHistoryFunnel.swift in Sources */, + D8E6FF7924058AC600686272 /* WMFWebView.m in Sources */, + D8EC3EDD1E9BDA35006712EB /* WMFFirstRandomViewController.m in Sources */, + 67B64D5D2507E9FD00FA27F3 /* ArticleAsLivingDocSmallEventCollectionViewCell.swift in Sources */, + 7ABE170E2239B5A0006BA309 /* WelcomePageViewController.swift in Sources */, + D8EC3EDF1E9BDA35006712EB /* ArticlePopoverViewController.swift in Sources */, + 7A2432C01FCF401900FB4BA5 /* CreateReadingListViewController.swift in Sources */, + 67FBE33B29705FC200A2E4AD /* TalkPageArchivesItem.swift in Sources */, + D8EC3EE01E9BDA35006712EB /* WMFLogFormatter.m in Sources */, + 009C8EC329071E720056A3AC /* NSString+Range.swift in Sources */, + D818FEBD21E39EE2001A7A00 /* CodemirrorSetupUserScript.swift in Sources */, + 0042812225E6E841004945B3 /* NSBundle+NYTPhotoViewer.m in Sources */, + D8EC3EE11E9BDA35006712EB /* RoundedCornerView.swift in Sources */, + 7AE1D33B1FCD10B900393471 /* SavedViewController.swift in Sources */, + 830D71D11F704DD40080078B /* ArticleFetchedResultsViewController.swift in Sources */, + 67E8B08F226A57E900537BC9 /* TalkPageReplyListViewController.swift in Sources */, + 67D9D1F12970D88E00BFCD4F /* DisclosureButton.swift in Sources */, + 7AF56C3721DE0E2800563A9C /* SectionEditorWebViewMessagingController.swift in Sources */, + D8EC3EE41E9BDA35006712EB /* NSString+FormattedAttributedString.m in Sources */, + 7A610CBF220A582A00C266AE /* HintController.swift in Sources */, + 7A9F061B2266432200856321 /* InsertMediaSettingsTextTableViewCell.swift in Sources */, + 83C06883292EC85700DF1403 /* TalkPageFormattingToolbarView.swift in Sources */, + 00E5B3A528EB8E4F00D2C51A /* TalkPageTopicReplyOnboardingHostingController.swift in Sources */, + 7A2BB1D621F27AC5004C0FDF /* WKWebView+OffsetHack.swift in Sources */, + FF92187C252F7EA300C39A8F /* ThanksGiving.swift in Sources */, + D8E6FF6924054FA100686272 /* ArticleViewController+LinkPreviewing.swift in Sources */, + B0CD9DE41F70997400051843 /* WMFWelcomeAnimationView.swift in Sources */, + B0845E1320618DA400CDD98E /* SavedProgressViewController.swift in Sources */, + 00474A3028DD1B13002E3C09 /* TalkPageCoffeeRollView.swift in Sources */, + 00EACEC728E39D470054DDB4 /* TalkPageEmptyView.swift in Sources */, + B0F9299C1F84789D002A0788 /* WMFWelcomeInitialViewController.swift in Sources */, + 6747118925072D1500287951 /* IconTitleBadge.swift in Sources */, + 00E75B6827EB927B00A45B78 /* NotificationsCenterDetailActionCell.swift in Sources */, + 7AF0265822985CB9000E0A06 /* BeKindInputAccessoryView.swift in Sources */, + 67DAEDA523CE24DA003AA208 /* SavedArticlesFetcher.swift in Sources */, + 7AB7DECA227203A600DD61A2 /* InsertMediaViewController.swift in Sources */, + D8EC3EE91E9BDA35006712EB /* WMFForgotPasswordViewController.swift in Sources */, + D8EC3EEB1E9BDA35006712EB /* WMFImageGalleryViewController.m in Sources */, + 00A7946C245CA4E60063BA18 /* ArticleSurveyTimerController.swift in Sources */, + B09705B6236B29D7006FDB5C /* DiffThanker.swift in Sources */, + D8EC3EEF1E9BDA35006712EB /* WMFAccountCreationViewController.swift in Sources */, + 83FBE9771F6181E00026C7EB /* ShareAFactActivityImageItemProvider.swift in Sources */, + 83836ECE1F615E5B007D1A05 /* ShareViewController.swift in Sources */, + 83B01F9C23DB62CD001185F4 /* ArticleViewController+ArticleInformation.swift in Sources */, + D8A47C8723D7259A002AA823 /* NoIntrinsicContentSizeImageView.swift in Sources */, + D8EC3EF21E9BDA35006712EB /* NSAttributedString+WMFModify.m in Sources */, + 00F5AED127C6C80C006390A8 /* PushNotificationsSettingsViewController.swift in Sources */, + D8EC3EF31E9BDA35006712EB /* WMFSettingsViewController.m in Sources */, + 0042813E25E6E841004945B3 /* NYTPhotosViewController.m in Sources */, + 679471DE275F245900621071 /* NotificationsCenterInboxView.swift in Sources */, + 7A8422492268BBE70074648E /* InsertMediaImageInfoView.swift in Sources */, + 672D69AA273ACAA100B123B3 /* UITabBarAppearance+Extensions.swift in Sources */, + D8EC3EF51E9BDA35006712EB /* UIScrollView+WMFContentOffsetUtils.m in Sources */, + D8EC3EF71E9BDA35006712EB /* WMFImageGalleryDetailOverlayView.m in Sources */, + B0CD9DEA1F70997400051843 /* WMFWelcomeAnalyticsAnimationView.swift in Sources */, + D8EC3EFA1E9BDA35006712EB /* UIApplication+SystemSettings.swift in Sources */, + 7A82765B226E4E41000A2389 /* EditPreviewInternalLinkViewController.swift in Sources */, + B068EDE2206B183500C827D1 /* Progress+ProgressUI.swift in Sources */, + 83EE476C20D019A100A21F34 /* ExploreViewController.swift in Sources */, + B0BCF0BB202537D800986F72 /* Panels.swift in Sources */, + 83B01F7E23DB0BA2001185F4 /* ArticleViewController+Editing.swift in Sources */, + 007CCF0D26D5A5E400D5EA7C /* NotificationsCenterViewModel.swift in Sources */, + 7A6ED50F20ADBF950001849F /* ReadingListsFunnel.swift in Sources */, + D8EC3EFD1E9BDA35006712EB /* WMFArticleRevision.m in Sources */, + B0BCF0AD2023AC7700986F72 /* ScrollableEducationPanelViewController.swift in Sources */, + D8EC3EFE1E9BDA35006712EB /* ArticlePlaceView.swift in Sources */, + D8EC3EFF1E9BDA35006712EB /* WMFCaptchaViewController.swift in Sources */, + 6754E44922773587005EEAD1 /* OldTalkPageReplyCell.swift in Sources */, + 83F1096123D09F80003F3E9E /* ArticleViewController+WIconPopover.swift in Sources */, + 6782DBA52343B7EE003FA21B /* DiffHeaderSummaryView.swift in Sources */, + D8EC3F001E9BDA35006712EB /* DDLog+WMFLogger.m in Sources */, + 7A70797F223AB69000A2BDFC /* WelcomePanelLabelContentViewController.swift in Sources */, + 005E004228DE1F2800721584 /* TalkPageCoffeeRollViewModel.swift in Sources */, + D818D3881ED750E40076110D /* ArticleCollectionViewController.swift in Sources */, + 7A23CED1211A24FD00441A79 /* FeedFunnel.swift in Sources */, + D80ED25E1EE18D0900CE8C50 /* PlacesSearchSuggestionTableViewCell.swift in Sources */, + 83B01F7923DA5348001185F4 /* ArticleViewController+ArticleToolbarHandling.swift in Sources */, + D8EC3F021E9BDA35006712EB /* WMFLegacyReference.swift in Sources */, + 672D69A5273ABD3600B123B3 /* UINavigationBarAppearance+Extensions.swift in Sources */, + 7A13A89B2028BB3600F28254 /* ReadingListsAlertController.swift in Sources */, + FF59DF4E2555E0CB0048E66C /* InternalLinkPreviewing.swift in Sources */, + 0022DD2A25829D8C00790EC1 /* ScribbleIgnoringInteractionDelegate.swift in Sources */, + D8EC3F031E9BDA35006712EB /* WMFWelcomePageViewController.swift in Sources */, + 0042812625E6E841004945B3 /* NYTPhotoTransitionController.m in Sources */, + 67059DB72260D61A009811AA /* SchemeHandler.swift in Sources */, + 834CC34D21075B7600F62818 /* UITabBar+Theme.swift in Sources */, + D8EC3F041E9BDA35006712EB /* MKCoordinateRegion+Dimensions.swift in Sources */, + D8EC3F061E9BDA35006712EB /* WMFAlertManager.swift in Sources */, + D8EC3F071E9BDA35006712EB /* WMFWelcomeLanguageTableViewController.swift in Sources */, + 830AD2BA24D1D615003EEFE6 /* WebPageUserScript.swift in Sources */, + 7AEBAD472102117C002FAB41 /* NSFetchedResultsController+IndexPathValidation.swift in Sources */, + 679A24092968E0D0008D7686 /* ShiftingScrollView.swift in Sources */, + 67DAEDDA27E8DCBF005CF9B6 /* NotificationsCenterDetailViewModel+TextExtensions.swift in Sources */, + 670F766122B0C48F00D87545 /* FakeProgressLoading.swift in Sources */, + D8EC3F091E9BDA35006712EB /* WMFLoginFunnel.m in Sources */, + 83B01F7423DA5327001185F4 /* ArticleViewController+ArticleWebMessageHandling.swift in Sources */, + 00D9276C29511E95004ECBEA /* PageHistoryCountsView.swift in Sources */, + 0010F93A27A49C7700D77848 /* HorizontalSpacerView.swift in Sources */, + 8320331D22B90529004A9EDA /* NavigationStateController.swift in Sources */, + D8EC3F0B1E9BDA35006712EB /* MapUtilities.swift in Sources */, + D8EC3F0D1E9BDA35006712EB /* WKWebView+ElementLocation.m in Sources */, + 678D79FE235E59B2006161FF /* DiffListUneditedViewModel.swift in Sources */, + 67134A1828A73C0A00BA0BB9 /* TalkPageReplyComposeController.swift in Sources */, + D8EC3F0F1E9BDA35006712EB /* UIViewController+WMFStoryboardUtilities.swift in Sources */, + B0016CBB21354D9D00FA1096 /* AutoLayoutSafeMultiLineButton.swift in Sources */, + 83DB4411244A57590046FABE /* RootNavigationController.swift in Sources */, + 7A73B48421E54B4200249E09 /* SectionEditorNavigationItemController.swift in Sources */, + 006ABEE92901E8F600722DF8 /* VanishAccountWarningView.swift in Sources */, + 671DF9C225F2AE4E0011799E /* ArticleDescriptionControlling.swift in Sources */, + D8EC3F111E9BDA35006712EB /* TableOfContentsPresentationController.swift in Sources */, + 7ABE17022239B346006BA309 /* WelcomeViewController.swift in Sources */, + 7ABAD6C120349B91006A364C /* Collection.swift in Sources */, + 67C6F79827E8C0EF00B9C864 /* NotificationsCenterCommonViewModel.swift in Sources */, + 7AB20A0E22FC8432006FECB4 /* PageHistoryCountsViewController.swift in Sources */, + 671F5E012367EDC600111116 /* GlobalUserInfoFetcher.swift in Sources */, + D80BF0A52347735E00B3B522 /* AppSearchButton.swift in Sources */, + 833B8C89281AE2100021C12C /* RemoteNotificationsFunnel.swift in Sources */, + 7AC809C721DD1FE100E8B6E1 /* SectionEditorInputViewsController.swift in Sources */, + 676E813429380D8A00F15258 /* TalkPagesFunnel.swift in Sources */, + D8EC3F141E9BDA35006712EB /* WMFLoginViewController.swift in Sources */, + 67C6F79D27E8C51500B9C864 /* NotificationsCenterCommonViewModel+LinkExtensions.swift in Sources */, + 8330532B23EF0B4200123141 /* ArticleViewController+Media.swift in Sources */, + 8334EC4C286A443B00929DF2 /* TalkPageFetcher.swift in Sources */, + D837CC39231FE9CC00BA6130 /* ThemeableViewController.swift in Sources */, + 8330533023EF107D00123141 /* MediaListGalleryViewController.swift in Sources */, + 7A0161E21FE8B4CA00AEDC3D /* TagCollectionViewCell.swift in Sources */, + D8EC3F181E9BDA35006712EB /* PageHistoryFetcher.swift in Sources */, + D87B13A71F276B1000B27227 /* ShareActivityController.swift in Sources */, + 83F1097023D0E787003F3E9E /* RandomArticleViewController.swift in Sources */, + 7ABE17262239BB54006BA309 /* WelcomePanelViewController.swift in Sources */, + 67E8B083226A57E600537BC9 /* OldTalkPageTopicCell.swift in Sources */, + 678D79F2235E5979006161FF /* DiffListChangeViewModel.swift in Sources */, + D8EC3F1A1E9BDA35006712EB /* CreateAccountFunnel.m in Sources */, + D8EC3F1C1E9BDA35006712EB /* WMFShareFunnel.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 00021DED24D48EFE00476F97 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 00021DE024D48EFD00476F97 /* WidgetsExtension */; + targetProxy = 00021DEC24D48EFE00476F97 /* PBXContainerItemProxy */; + }; + 00AB75C024D4E8FB0041056A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D844D96B1D6CB2600042D692 /* WMF */; + targetProxy = 00AB75BF24D4E8FB0041056A /* PBXContainerItemProxy */; + }; + 0E83806F1D64989F0076EDE4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0E8380621D64989F0076EDE4 /* ContinueReadingWidget */; + targetProxy = 0E83806E1D64989F0076EDE4 /* PBXContainerItemProxy */; + }; + 676C864A26D40AEB00A704C1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 676C864326D40AEA00A704C1 /* NotificationServiceExtension */; + targetProxy = 676C864926D40AEB00A704C1 /* PBXContainerItemProxy */; + }; + 676C867226D416FB00A704C1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 676C864326D40AEA00A704C1 /* NotificationServiceExtension */; + targetProxy = 676C867126D416FB00A704C1 /* PBXContainerItemProxy */; + }; + 676C867526D4170100A704C1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 676C864326D40AEA00A704C1 /* NotificationServiceExtension */; + targetProxy = 676C867426D4170100A704C1 /* PBXContainerItemProxy */; + }; + 676C868426D4545300A704C1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D844D96B1D6CB2600042D692 /* WMF */; + targetProxy = 676C868326D4545300A704C1 /* PBXContainerItemProxy */; + }; + B018501A20BC85E400A508F1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D844D96B1D6CB2600042D692 /* WMF */; + targetProxy = B018501920BC85E400A508F1 /* PBXContainerItemProxy */; + }; + B0606EB420AA6FF0006EC6B9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D4991434181D51DE00E6073C /* Wikipedia */; + targetProxy = B0606EB320AA6FF0006EC6B9 /* PBXContainerItemProxy */; + }; + BCBDE0AE1AA76F19006BD29A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D4991434181D51DE00E6073C /* Wikipedia */; + targetProxy = BCBDE0AD1AA76F19006BD29A /* PBXContainerItemProxy */; + }; + D8479FB11F222FE90025FD7A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D8479FAA1F222FE80025FD7A /* Wikipedia Stickers */; + targetProxy = D8479FB01F222FE90025FD7A /* PBXContainerItemProxy */; + }; + D88DBBBB1D8B322400134A50 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D844D96B1D6CB2600042D692 /* WMF */; + targetProxy = D88DBBBA1D8B322400134A50 /* PBXContainerItemProxy */; + }; + D8A42A4E1E815A9C00D8E281 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D844D96B1D6CB2600042D692 /* WMF */; + targetProxy = D8A42A4F1E815A9C00D8E281 /* PBXContainerItemProxy */; + }; + D8CE24D81E698E2400DAE2E0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D844D96B1D6CB2600042D692 /* WMF */; + targetProxy = D8CE24D91E698E2400DAE2E0 /* PBXContainerItemProxy */; + }; + D8CE24DA1E698E2400DAE2E0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0E8380621D64989F0076EDE4 /* ContinueReadingWidget */; + targetProxy = D8CE24DB1E698E2400DAE2E0 /* PBXContainerItemProxy */; + }; + D8EC3DCF1E9BDA35006712EB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D844D96B1D6CB2600042D692 /* WMF */; + targetProxy = D8EC3DD01E9BDA35006712EB /* PBXContainerItemProxy */; + }; + D8EC3DD11E9BDA35006712EB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0E8380621D64989F0076EDE4 /* ContinueReadingWidget */; + targetProxy = D8EC3DD21E9BDA35006712EB /* PBXContainerItemProxy */; + }; + D8FA19131E1BDFD7009675C3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D844D96B1D6CB2600042D692 /* WMF */; + targetProxy = D8FA19121E1BDFD7009675C3 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 0E83806A1D64989F0076EDE4 /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 0E83806B1D64989F0076EDE4 /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; + D801C8511EB8E131001FA294 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + D801C8521EB8E131001FA294 /* af */, + D801C8551EB8E131001FA294 /* ar */, + D801C8571EB8E131001FA294 /* as */, + D801C8591EB8E131001FA294 /* ast */, + D801C8681EB8E131001FA294 /* bn */, + D801C86A1EB8E131001FA294 /* br */, + D801C86C1EB8E131001FA294 /* bs */, + D801C86F1EB8E131001FA294 /* ca */, + D801C8711EB8E131001FA294 /* ce */, + D801C8731EB8E131001FA294 /* ckb */, + D801C8771EB8E131001FA294 /* cs */, + D801C8791EB8E131001FA294 /* cy */, + D801C87B1EB8E131001FA294 /* da */, + D801C87D1EB8E131001FA294 /* de */, + D801C87F1EB8E131001FA294 /* diq */, + D801C8821EB8E131001FA294 /* el */, + D801C8851EB8E131001FA294 /* en */, + D801C8881EB8E131001FA294 /* eo */, + D801C88A1EB8E131001FA294 /* es */, + D801C88C1EB8E131001FA294 /* eu */, + D801C88E1EB8E131001FA294 /* fa */, + D801C8901EB8E131001FA294 /* fi */, + D801C8921EB8E131001FA294 /* fo */, + D801C8941EB8E131001FA294 /* fr */, + D801C8981EB8E131001FA294 /* gl */, + D801C89C1EB8E131001FA294 /* haw */, + D801C89E1EB8E131001FA294 /* he */, + D801C8A01EB8E131001FA294 /* hi */, + D801C8A21EB8E131001FA294 /* hrx */, + D801C8A41EB8E131001FA294 /* hsb */, + D801C8A61EB8E131001FA294 /* hu */, + D801C8A81EB8E131001FA294 /* hy */, + D801C8AA1EB8E131001FA294 /* id */, + D801C8AC1EB8E131001FA294 /* is */, + D801C8AE1EB8E131001FA294 /* it */, + D801C8B01EB8E131001FA294 /* ja */, + D801C8B31EB8E131001FA294 /* ka */, + D801C8B51EB8E131001FA294 /* km */, + D801C8B71EB8E131001FA294 /* kn */, + D801C8B91EB8E131001FA294 /* ko */, + D801C8BB1EB8E131001FA294 /* krc */, + D801C8BD1EB8E131001FA294 /* ksh */, + D801C8C11EB8E131001FA294 /* lb */, + D801C8C31EB8E131001FA294 /* lt */, + D801C8C51EB8E131001FA294 /* lv */, + D801C8C91EB8E131001FA294 /* mk */, + D801C8CB1EB8E131001FA294 /* ml */, + D801C8CD1EB8E131001FA294 /* mr */, + D801C8CF1EB8E131001FA294 /* ms */, + D801C8D21EB8E131001FA294 /* my */, + D801C8D51EB8E131001FA294 /* nb */, + D801C8D71EB8E131001FA294 /* ne */, + D801C8D91EB8E131001FA294 /* nl */, + D801C8DB1EB8E131001FA294 /* oc */, + D801C8DE1EB8E131001FA294 /* om */, + D801C8E01EB8E131001FA294 /* or */, + D801C8E21EB8E131001FA294 /* pa */, + D801C8E41EB8E131001FA294 /* pl */, + D801C8E71EB8E131001FA294 /* ps */, + D801C8E91EB8E131001FA294 /* pt-br */, + D801C8EB1EB8E131001FA294 /* pt */, + D801C8EF1EB8E131001FA294 /* ro */, + D801C8F11EB8E131001FA294 /* ru */, + D801C8F31EB8E131001FA294 /* sa */, + D801C8F51EB8E131001FA294 /* sah */, + D801C8F71EB8E131001FA294 /* sco */, + D801C8F91EB8E131001FA294 /* sd */, + D801C8FC1EB8E131001FA294 /* sk */, + D801C8FE1EB8E131001FA294 /* sq */, + D801C9001EB8E131001FA294 /* sr-EC */, + D801C9031EB8E131001FA294 /* su */, + D801C9051EB8E131001FA294 /* sv */, + D801C9071EB8E131001FA294 /* sw */, + D801C9091EB8E131001FA294 /* ta */, + D801C90C1EB8E131001FA294 /* te */, + D801C90E1EB8E131001FA294 /* tg-cyrl */, + D801C9101EB8E131001FA294 /* th */, + D801C9131EB8E131001FA294 /* tr */, + D801C9151EB8E131001FA294 /* uk */, + D801C9171EB8E131001FA294 /* ur */, + D801C9191EB8E131001FA294 /* uz */, + D801C91B1EB8E131001FA294 /* vec */, + D801C91D1EB8E131001FA294 /* vi */, + D801C9211EB8E131001FA294 /* yi */, + D801C9231EB8E131001FA294 /* zh-hans */, + D801C9251EB8E131001FA294 /* zh-hant */, + FF6A77E8258D68E7006FA238 /* nqo */, + FF0261872627B2D800CBD55F /* kcg */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + D801C8531EB8E131001FA294 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + D801C8541EB8E131001FA294 /* af */, + D801C8561EB8E131001FA294 /* ar */, + D801C8581EB8E131001FA294 /* as */, + D801C85A1EB8E131001FA294 /* ast */, + D801C85E1EB8E131001FA294 /* ba */, + D801C8691EB8E131001FA294 /* bn */, + D801C86B1EB8E131001FA294 /* br */, + D801C86D1EB8E131001FA294 /* bs */, + D801C8701EB8E131001FA294 /* ca */, + D801C8721EB8E131001FA294 /* ce */, + D801C8741EB8E131001FA294 /* ckb */, + D801C8781EB8E131001FA294 /* cs */, + D801C87A1EB8E131001FA294 /* cy */, + D801C87C1EB8E131001FA294 /* da */, + D801C87E1EB8E131001FA294 /* de */, + D801C8801EB8E131001FA294 /* diq */, + D801C8831EB8E131001FA294 /* el */, + D801C8861EB8E131001FA294 /* en */, + D801C8891EB8E131001FA294 /* eo */, + D801C88B1EB8E131001FA294 /* es */, + D801C88D1EB8E131001FA294 /* eu */, + D801C88F1EB8E131001FA294 /* fa */, + D801C8911EB8E131001FA294 /* fi */, + D801C8931EB8E131001FA294 /* fo */, + D801C8951EB8E131001FA294 /* fr */, + D801C8991EB8E131001FA294 /* gl */, + D801C89D1EB8E131001FA294 /* haw */, + D801C89F1EB8E131001FA294 /* he */, + D801C8A11EB8E131001FA294 /* hi */, + D801C8A51EB8E131001FA294 /* hsb */, + D801C8A71EB8E131001FA294 /* hu */, + D801C8A91EB8E131001FA294 /* hy */, + D801C8AB1EB8E131001FA294 /* id */, + D801C8AD1EB8E131001FA294 /* is */, + D801C8AF1EB8E131001FA294 /* it */, + D801C8B11EB8E131001FA294 /* ja */, + D801C8B21EB8E131001FA294 /* jv */, + D801C8B41EB8E131001FA294 /* ka */, + D801C8B61EB8E131001FA294 /* km */, + D801C8B81EB8E131001FA294 /* kn */, + D801C8BA1EB8E131001FA294 /* ko */, + D801C8BC1EB8E131001FA294 /* krc */, + D801C8BE1EB8E131001FA294 /* ksh */, + D801C8C21EB8E131001FA294 /* lb */, + D801C8C41EB8E131001FA294 /* lt */, + D801C8C61EB8E131001FA294 /* lv */, + D801C8C71EB8E131001FA294 /* mai */, + D801C8CA1EB8E131001FA294 /* mk */, + D801C8CC1EB8E131001FA294 /* ml */, + D801C8CE1EB8E131001FA294 /* mr */, + D801C8D01EB8E131001FA294 /* ms */, + D801C8D31EB8E131001FA294 /* my */, + D801C8D61EB8E131001FA294 /* nb */, + D801C8D81EB8E131001FA294 /* ne */, + D801C8DA1EB8E131001FA294 /* nl */, + D801C8DC1EB8E131001FA294 /* oc */, + D801C8DF1EB8E131001FA294 /* om */, + D801C8E11EB8E131001FA294 /* or */, + D801C8E31EB8E131001FA294 /* pa */, + D801C8E51EB8E131001FA294 /* pl */, + D801C8E81EB8E131001FA294 /* ps */, + D801C8EA1EB8E131001FA294 /* pt-br */, + D801C8EC1EB8E131001FA294 /* pt */, + D801C8F01EB8E131001FA294 /* ro */, + D801C8F21EB8E131001FA294 /* ru */, + D801C8F41EB8E131001FA294 /* sa */, + D801C8F61EB8E131001FA294 /* sah */, + D801C8F81EB8E131001FA294 /* sco */, + D801C8FA1EB8E131001FA294 /* sd */, + D801C8FD1EB8E131001FA294 /* sk */, + D801C8FF1EB8E131001FA294 /* sq */, + D801C9011EB8E131001FA294 /* sr-EC */, + D801C9041EB8E131001FA294 /* su */, + D801C9061EB8E131001FA294 /* sv */, + D801C9081EB8E131001FA294 /* sw */, + D801C90A1EB8E131001FA294 /* ta */, + D801C90B1EB8E131001FA294 /* tcy */, + D801C90D1EB8E131001FA294 /* te */, + D801C90F1EB8E131001FA294 /* tg-cyrl */, + D801C9111EB8E131001FA294 /* th */, + D801C9121EB8E131001FA294 /* tl */, + D801C9141EB8E131001FA294 /* tr */, + D801C9161EB8E131001FA294 /* uk */, + D801C9181EB8E131001FA294 /* ur */, + D801C91A1EB8E131001FA294 /* uz */, + D801C91C1EB8E131001FA294 /* vec */, + D801C91E1EB8E131001FA294 /* vi */, + D801C9221EB8E131001FA294 /* yi */, + D801C9241EB8E131001FA294 /* zh-hans */, + D801C9261EB8E131001FA294 /* zh-hant */, + D85C145A1F388582002186A5 /* kab */, + 8361AEC621949665006B00B0 /* ga */, + 83CF71432326D47E009DEC00 /* sl */, + 834C269B24042DBF00245BE7 /* hr */, + FF338A0A258D572900292602 /* nqo */, + FF19C1AB260548FD000AC20B /* kcg */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + D801C8611EB8E131001FA294 /* Localizable.stringsdict */ = { + isa = PBXVariantGroup; + children = ( + D801C8871EB8E131001FA294 /* en */, + D801C8E61EB8E131001FA294 /* pl */, + D85C29351EC48F12007EF868 /* ast */, + D85C29361EC48F5D007EF868 /* af */, + D85C29371EC4909C007EF868 /* bn */, + D85C29381EC490C4007EF868 /* br */, + D85C29391EC490F1007EF868 /* ckb */, + D85C293A1EC49148007EF868 /* fr */, + D85C293B1EC4915A007EF868 /* ru */, + D85C293C1EC4915C007EF868 /* fi */, + D85C29401EC49196007EF868 /* de */, + D85C29411EC491D8007EF868 /* es */, + D85C29421EC49238007EF868 /* eu */, + D85C29431EC49252007EF868 /* fa */, + D85C29441EC4928A007EF868 /* gl */, + D85C29451EC49299007EF868 /* he */, + D85C29461EC492AC007EF868 /* hi */, + D85C29471EC492CB007EF868 /* id */, + D85C29481EC492E1007EF868 /* it */, + D85C29491EC49304007EF868 /* jv */, + D85C294A1EC49323007EF868 /* ko */, + D85C294B1EC4932F007EF868 /* lb */, + D85C294C1EC49345007EF868 /* mk */, + D85C294D1EC49356007EF868 /* ms */, + D85C294E1EC4938A007EF868 /* nb */, + D85C29501EC493B6007EF868 /* pt-BR */, + D85C29511EC493DE007EF868 /* sd */, + D85C29521EC4940A007EF868 /* sr-EC */, + D85C29531EC4945B007EF868 /* sv */, + D85C29541EC4946D007EF868 /* tr */, + D85C29551EC4947B007EF868 /* zh-Hans */, + D85C29561EC49485007EF868 /* zh-Hant */, + D89927DC1ED310540008F54C /* yi */, + D89927DD1ED310B60008F54C /* su */, + D80877951EDE1EFB00CCA97E /* bs */, + D80877961EDE1F3300CCA97E /* ja */, + D80877971EDE1F4F00CCA97E /* nl */, + D8497F5D1EE027D700100CBD /* hrx */, + D81EF2D61F1D2F0500D26D3F /* cs */, + D85C145B1F38859A002186A5 /* kab */, + D84E2A9A1FFBCAF600878968 /* ta */, + D84E2A9B1FFBCB9400878968 /* pt */, + D84E2A9C1FFBCBBB00878968 /* lv */, + D84E2A9D1FFBCBE900878968 /* vi */, + D84E2A9E1FFBCC0500878968 /* ne */, + D8C8C21F20113E6200B3317B /* sq */, + D8C8C22020113EA000B3317B /* is */, + D8C8C22120113F0500B3317B /* ps */, + D8FFF63F202C7A9400A028E0 /* km */, + D8FFF6552031CAB800A028E0 /* el */, + D8FFF6562031CACC00A028E0 /* ar */, + D8FFF6582031CACE00A028E0 /* bik */, + D8FFF6592031CAD100A028E0 /* ca */, + D8FFF65A2031CAD300A028E0 /* ce */, + D8FFF65C2031CAD700A028E0 /* eo */, + D8FFF65E2031CADB00A028E0 /* haw */, + D8FFF65F2031CADD00A028E0 /* hsb */, + D8FFF6602031CADF00A028E0 /* hu */, + D8FFF6612031CAE000A028E0 /* hy */, + D8FFF6622031CAE200A028E0 /* krc */, + D8FFF6632031CAE500A028E0 /* lt */, + D8FFF6652031CAE800A028E0 /* ml */, + D8FFF6662031CAE900A028E0 /* mr */, + D8FFF6672031CAEB00A028E0 /* oc */, + D8FFF6682031CAED00A028E0 /* ro */, + D8FFF6692031CAEE00A028E0 /* sco */, + D8FFF66B2031CAF600A028E0 /* sk */, + D8FFF66C2031CAF900A028E0 /* te */, + D8FFF66D2031CAFA00A028E0 /* uk */, + D8FFF66E2031CAFD00A028E0 /* da */, + D8FFF66F2031CAFF00A028E0 /* zza */, + D8FFF6702031CB0000A028E0 /* fo */, + D8FFF6722031CB0400A028E0 /* kn */, + D8FFF6732031CB0500A028E0 /* or */, + D8FFF6742031CB0600A028E0 /* tg */, + D8FFF6752031CB0900A028E0 /* th */, + D8FFF6762031CB0C00A028E0 /* av */, + D8FFF6782031CB0F00A028E0 /* ksh */, + D8FFF6792031CB1100A028E0 /* as */, + D8FFF67A2031CB1200A028E0 /* ka */, + D8FFF67B2031CB1400A028E0 /* om */, + D8FFF67C2031CB1500A028E0 /* pa */, + D8FFF67D2031CB1B00A028E0 /* sah */, + D8FFF67E2031CB1D00A028E0 /* sw */, + D8FFF67F2031CB1E00A028E0 /* uz */, + D8FFF6802031CB2000A028E0 /* ur */, + D8FFF6812031CB2100A028E0 /* azb */, + D8FFF6822031CB2300A028E0 /* ba */, + D8FFF6832031CB2500A028E0 /* be-tarask */, + D8FFF6842031CB2600A028E0 /* bgn */, + D8FFF6852031CB2700A028E0 /* cnh */, + D8FFF6892031CB2F00A028E0 /* mai */, + D8FFF68D2031CB3500A028E0 /* sa */, + D8FFF68E2031CB3700A028E0 /* fil */, + D8FFF68F2031CB3800A028E0 /* vec */, + D8FFF6912031CB3B00A028E0 /* xmf */, + D8FFF6922031CB3D00A028E0 /* my */, + D8FFF6932031CB3E00A028E0 /* cy */, + D8FFF6942031CB4000A028E0 /* tcy */, + 8361AEC721949670006B00B0 /* ga */, + 83CF71442326D48A009DEC00 /* sl */, + 834C269C24042DCF00245BE7 /* hr */, + FF338A0B258D572900292602 /* nqo */, + FF19C1AC26054909000AC20B /* kcg */, + ); + name = Localizable.stringsdict; + sourceTree = ""; + }; + D890C85B1D772ED3007132C9 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + D890C85C1D772ED3007132C9 /* Base */, + D890C85E1D772EE7007132C9 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 00021DEF24D48EFE00476F97 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Widgets/WidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = Widgets/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = YES; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 00021DF024D48EFE00476F97 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Widgets/WidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = Widgets/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = YES; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = LocalDebug; + }; + 00021DF224D48EFE00476F97 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Widgets/WidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = Widgets/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = YES; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = UITest; + }; + 00021DF324D48EFE00476F97 /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Widgets/WidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = Widgets/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = YES; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = UserTestingDebug; + }; + 00021DF424D48EFE00476F97 /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Widgets/WidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = Widgets/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = YES; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = StagingDebug; + }; + 00021DF624D48EFE00476F97 /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Widgets/WidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = Widgets/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = YES; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ExperimentalDebug; + }; + 00021DF724D48EFE00476F97 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Widgets/WidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = Widgets/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 00021DF824D48EFE00476F97 /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Widgets/WidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = Widgets/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = UserTesting; + }; + 00021DF924D48EFE00476F97 /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Widgets/WidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = Widgets/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Staging; + }; + 00021DFB24D48EFE00476F97 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Widgets/WidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = Widgets/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Test; + }; + 00021DFC24D48EFE00476F97 /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Widgets/WidgetsExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = Widgets/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.Widgets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Experimental; + }; + 0E8380711D64989F0076EDE4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = ContinueReadingWidget/ContinueReadingWidget.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = ContinueReadingWidget/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -DDEBUG"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.ContinueReadingWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 0E8380731D64989F0076EDE4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = ContinueReadingWidget/ContinueReadingWidget.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = ContinueReadingWidget/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.ContinueReadingWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 0E8380741D64989F0076EDE4 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = ContinueReadingWidget/ContinueReadingWidget.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = ContinueReadingWidget/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.ContinueReadingWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Test; + }; + 0EAB7A201D47AF8000E7CF8E /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "TEST=1", + "DEBUG=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + "WMF_APP_GROUP_IDENTIFIER=$(WMF_APP_GROUP_IDENTIFIER)", + ); + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-DNDEBUG -DTEST"; + SDKROOT = iphoneos; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + WMF_APP_GROUP_IDENTIFIER = group.org.wikimedia.wikipedia; + }; + name = Test; + }; + 0EAB7A211D47AF8000E7CF8E /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Wikipedia-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = Wikipedia; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Test; + }; + 0EAB7A221D47AF8000E7CF8E /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREFIX_HEADER = "WikipediaUnitTests/WikipediaUnitTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SOURCE_ROOT_DIR=@\\\"\"$(SOURCE_ROOT)\"\\\"", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + $CONFIGURATION_TEMP_DIR/Wikipedia.build/DerivedSources, + "$(inherited)", + ); + INFOPLIST_FILE = WikipediaUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Library/Frameworks/, + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.wikimedia.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wikipedia.app/Wikipedia"; + }; + name = Test; + }; + 676C864C26D40AEB00A704C1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 676C864D26D40AEB00A704C1 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = LocalDebug; + }; + 676C864E26D40AEB00A704C1 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = UITest; + }; + 676C864F26D40AEB00A704C1 /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = UserTestingDebug; + }; + 676C865026D40AEB00A704C1 /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = StagingDebug; + }; + 676C865126D40AEB00A704C1 /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ExperimentalDebug; + }; + 676C865226D40AEB00A704C1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 676C865326D40AEB00A704C1 /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = UserTesting; + }; + 676C865426D40AEB00A704C1 /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Staging; + }; + 676C865526D40AEB00A704C1 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Test; + }; + 676C865626D40AEB00A704C1 /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = NotificationServiceExtension/NotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = NotificationServiceExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = ""; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha.NotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Experimental; + }; + 8350FC3B20DA7F0200C19D60 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_NS_ASSERTIONS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + "$(inherited)", + "WMF_APP_GROUP_IDENTIFIER=$(WMF_APP_GROUP_IDENTIFIER)", + "UI_TEST=1", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-DDEBUG -DUI_TEST"; + SDKROOT = iphoneos; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + WMF_APP_GROUP_IDENTIFIER = group.org.wikimedia.wikipedia; + }; + name = UITest; + }; + 8350FC3C20DA7F0200C19D60 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Wikipedia-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = Wikipedia; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = UITest; + }; + 8350FC3D20DA7F0200C19D60 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = UITest; + }; + 8350FC3E20DA7F0200C19D60 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = UITest; + }; + 8350FC3F20DA7F0200C19D60 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = UITest; + }; + 8350FC4220DA7F0200C19D60 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREFIX_HEADER = "WikipediaUnitTests/WikipediaUnitTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SOURCE_ROOT_DIR=@\\\"\"$(SOURCE_ROOT)\"\\\"", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + $CONFIGURATION_TEMP_DIR/Wikipedia.build/DerivedSources, + "$(inherited)", + ); + INFOPLIST_FILE = WikipediaUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Library/Frameworks/, + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.wikimedia.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wikipedia ${CONFIGURATION}.app/Wikipedia ${CONFIGURATION}"; + }; + name = UITest; + }; + 8350FC4420DA7F0200C19D60 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = ContinueReadingWidget/ContinueReadingWidget.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = ContinueReadingWidget/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -DDEBUG"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.ContinueReadingWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = UITest; + }; + 8350FC4720DA7F0200C19D60 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "Wikipedia Stickers/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.WikipediaStickers; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = UITest; + }; + 8350FC4820DA7F0200C19D60 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 0; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "WMF Framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.WMF; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_INSTALL_OBJC_HEADER = YES; + VERSION_INFO_PREFIX = ""; + }; + name = UITest; + }; + 8350FC4920DA7F0200C19D60 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = UITest; + }; + 8350FC4A20DA7F0200C19D60 /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + "UI_TEST=1", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = WikipediaUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.uitesting.WikipediaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = UITest; + }; + B0606EB520AA6FF0006EC6B9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = WikipediaUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.uitesting.WikipediaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + B0606EB820AA6FF0006EC6B9 /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = WikipediaUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.uitesting.WikipediaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = UserTestingDebug; + }; + B0606EB920AA6FF0006EC6B9 /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = WikipediaUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.uitesting.WikipediaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = StagingDebug; + }; + B0606EBB20AA6FF0006EC6B9 /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = WikipediaUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.uitesting.WikipediaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = ExperimentalDebug; + }; + B0606EBC20AA6FF0006EC6B9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = WikipediaUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.uitesting.WikipediaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + B0606EBE20AA6FF0006EC6B9 /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = WikipediaUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.uitesting.WikipediaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = UserTesting; + }; + B0606EBF20AA6FF0006EC6B9 /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = WikipediaUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.uitesting.WikipediaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Staging; + }; + B0606EC120AA6FF0006EC6B9 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = WikipediaUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.uitesting.WikipediaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Test; + }; + B0606EC220AA6FF0006EC6B9 /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = WikipediaUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.uitesting.WikipediaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Experimental; + }; + BC42735B1A7C736800068882 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREFIX_HEADER = "WikipediaUnitTests/WikipediaUnitTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SOURCE_ROOT_DIR=@\\\"\"$(SOURCE_ROOT)\"\\\"", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + $CONFIGURATION_TEMP_DIR/Wikipedia.build/DerivedSources, + "$(inherited)", + ); + INFOPLIST_FILE = WikipediaUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Library/Frameworks/, + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.wikimedia.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wikipedia ${CONFIGURATION}.app/Wikipedia ${CONFIGURATION}"; + }; + name = Debug; + }; + BC42735C1A7C736800068882 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREFIX_HEADER = "WikipediaUnitTests/WikipediaUnitTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SOURCE_ROOT_DIR=@\\\"\"$(SOURCE_ROOT)\"\\\"", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + $CONFIGURATION_TEMP_DIR/Wikipedia.build/DerivedSources, + "$(inherited)", + ); + INFOPLIST_FILE = WikipediaUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Library/Frameworks/, + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.wikimedia.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wikipedia.app/Wikipedia"; + }; + name = Release; + }; + D4991468181D51DF00E6073C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_NS_ASSERTIONS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + "$(inherited)", + "WMF_APP_GROUP_IDENTIFIER=$(WMF_APP_GROUP_IDENTIFIER)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-DDEBUG"; + SDKROOT = iphoneos; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + WMF_APP_GROUP_IDENTIFIER = group.org.wikimedia.wikipedia; + }; + name = Debug; + }; + D4991469181D51DF00E6073C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "NDEBUG=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + "WMF_APP_GROUP_IDENTIFIER=$(WMF_APP_GROUP_IDENTIFIER)", + ); + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_SWIFT_FLAGS = "-DNDEBUG"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + WMF_APP_GROUP_IDENTIFIER = group.org.wikimedia.wikipedia; + }; + name = Release; + }; + D499146B181D51DF00E6073C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Wikipedia-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = Wikipedia; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + D499146C181D51DF00E6073C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Wikipedia-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = Wikipedia; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + D844D9751D6CB2600042D692 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 0; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "WMF Framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.WMF; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_INSTALL_OBJC_HEADER = YES; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + D844D9781D6CB2600042D692 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 0; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "WMF Framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.WMF; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_INSTALL_OBJC_HEADER = YES; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + D844D9791D6CB2600042D692 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 0; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "WMF Framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.WMF; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_INSTALL_OBJC_HEADER = YES; + VERSION_INFO_PREFIX = ""; + }; + name = Test; + }; + D8479FB31F222FE90025FD7A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "Wikipedia Stickers/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.WikipediaStickers; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + D8479FB51F222FE90025FD7A /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "Wikipedia Stickers/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.WikipediaStickers; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = UserTestingDebug; + }; + D8479FB61F222FE90025FD7A /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "Wikipedia Stickers/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta.WikipediaStickers; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = StagingDebug; + }; + D8479FB81F222FE90025FD7A /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "Wikipedia Stickers/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha.WikipediaStickers; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = ExperimentalDebug; + }; + D8479FB91F222FE90025FD7A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "Wikipedia Stickers/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.WikipediaStickers; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + D8479FBB1F222FE90025FD7A /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "Wikipedia Stickers/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.WikipediaStickers; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = UserTesting; + }; + D8479FBC1F222FE90025FD7A /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "Wikipedia Stickers/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta.WikipediaStickers; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Staging; + }; + D8479FBE1F222FE90025FD7A /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "Wikipedia Stickers/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.WikipediaStickers; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Test; + }; + D8479FBF1F222FE90025FD7A /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "Wikipedia Stickers/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha.WikipediaStickers; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Experimental; + }; + D85432322188CC1300E895B5 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_NS_ASSERTIONS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + "$(inherited)", + "WMF_APP_GROUP_IDENTIFIER=$(WMF_APP_GROUP_IDENTIFIER)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-DDEBUG -DWMF_LOCAL"; + SDKROOT = iphoneos; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + WMF_APP_GROUP_IDENTIFIER = group.org.wikimedia.wikipedia; + }; + name = LocalDebug; + }; + D85432332188CC1300E895B5 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Local-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = Wikipedia; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = LocalDebug; + }; + D85432342188CC1300E895B5 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = LocalDebug; + }; + D85432352188CC1300E895B5 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = LocalDebug; + }; + D85432362188CC1300E895B5 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = LocalDebug; + }; + D85432392188CC1300E895B5 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREFIX_HEADER = "WikipediaUnitTests/WikipediaUnitTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SOURCE_ROOT_DIR=@\\\"\"$(SOURCE_ROOT)\"\\\"", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + $CONFIGURATION_TEMP_DIR/Wikipedia.build/DerivedSources, + "$(inherited)", + ); + INFOPLIST_FILE = WikipediaUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Library/Frameworks/, + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.wikimedia.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wikipedia ${CONFIGURATION}.app/Wikipedia ${CONFIGURATION}"; + }; + name = LocalDebug; + }; + D854323B2188CC1300E895B5 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = ContinueReadingWidget/ContinueReadingWidget.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = ContinueReadingWidget/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -DDEBUG"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.ContinueReadingWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = LocalDebug; + }; + D854323E2188CC1300E895B5 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = "iMessage App Icon"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "Wikipedia Stickers/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.WikipediaStickers; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = LocalDebug; + }; + D854323F2188CC1300E895B5 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 0; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "WMF Framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.WMF; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_INSTALL_OBJC_HEADER = YES; + VERSION_INFO_PREFIX = ""; + }; + name = LocalDebug; + }; + D85432402188CC1300E895B5 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = LocalDebug; + }; + D85432412188CC1300E895B5 /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = WikipediaUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.uitesting.WikipediaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = LocalDebug; + }; + D858A7FA1DA6DBC8009C3DEB /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "NDEBUG=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + "WMF_APP_GROUP_IDENTIFIER=$(WMF_APP_GROUP_IDENTIFIER)", + ); + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_SWIFT_FLAGS = "-DNDEBUG"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + WMF_APP_GROUP_IDENTIFIER = group.org.wikimedia.wikipedia.alpha; + }; + name = Experimental; + }; + D858A7FB1DA6DBC8009C3DEB /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + WebKit, + "-framework", + SystemConfiguration, + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "Wikipedia Alpha"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Experimental; + }; + D858A7FC1DA6DBC8009C3DEB /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREFIX_HEADER = "WikipediaUnitTests/WikipediaUnitTests-Prefix.pch"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + $CONFIGURATION_TEMP_DIR/Wikipedia.build/DerivedSources, + "$(inherited)", + ); + INFOPLIST_FILE = WikipediaUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Library/Frameworks/, + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.wikimedia.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wikipedia ${CONFIGURATION}.app/Wikipedia ${CONFIGURATION}"; + }; + name = Experimental; + }; + D858A7FD1DA6DBC8009C3DEB /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = ContinueReadingWidget/ContinueReadingWidget.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = ContinueReadingWidget/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha.ContinueReadingWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Experimental; + }; + D858A8001DA6DBC8009C3DEB /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 0; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "WMF Framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.WMF; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_INSTALL_OBJC_HEADER = YES; + VERSION_INFO_PREFIX = ""; + }; + name = Experimental; + }; + D858A8021DA6DD00009C3DEB /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_NS_ASSERTIONS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + "$(inherited)", + "WMF_APP_GROUP_IDENTIFIER=$(WMF_APP_GROUP_IDENTIFIER)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-DDEBUG"; + SDKROOT = iphoneos; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + WMF_APP_GROUP_IDENTIFIER = group.org.wikimedia.wikipedia.alpha; + }; + name = ExperimentalDebug; + }; + D858A8031DA6DD00009C3DEB /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + SystemConfiguration, + "-framework", + WebKit, + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "Wikipedia Alpha"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = ExperimentalDebug; + }; + D858A8041DA6DD00009C3DEB /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREFIX_HEADER = "WikipediaUnitTests/WikipediaUnitTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SOURCE_ROOT_DIR=@\\\"\"$(SOURCE_ROOT)\"\\\"", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + $CONFIGURATION_TEMP_DIR/Wikipedia.build/DerivedSources, + "$(inherited)", + ); + INFOPLIST_FILE = WikipediaUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Library/Frameworks/, + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.wikimedia.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wikipedia ${CONFIGURATION}.app/Wikipedia ${CONFIGURATION}"; + }; + name = ExperimentalDebug; + }; + D858A8051DA6DD00009C3DEB /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = ContinueReadingWidget/ContinueReadingWidget.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = ContinueReadingWidget/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -DDEBUG"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha.ContinueReadingWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = ExperimentalDebug; + }; + D858A8081DA6DD00009C3DEB /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 0; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "WMF Framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.WMF; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_INSTALL_OBJC_HEADER = YES; + VERSION_INFO_PREFIX = ""; + }; + name = ExperimentalDebug; + }; + D87021641EBA63EF000D02D6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = Debug; + }; + D87021651EBA63EF000D02D6 /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = UserTestingDebug; + }; + D87021661EBA63EF000D02D6 /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = StagingDebug; + }; + D87021681EBA63EF000D02D6 /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = ExperimentalDebug; + }; + D87021691EBA63EF000D02D6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = Release; + }; + D870216A1EBA63EF000D02D6 /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = UserTesting; + }; + D870216B1EBA63EF000D02D6 /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = Staging; + }; + D870216D1EBA63EF000D02D6 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = Test; + }; + D870216E1EBA63EF000D02D6 /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + }; + name = Experimental; + }; + D8A42A3D1E814FAA00D8E281 /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "NDEBUG=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + "WMF_APP_GROUP_IDENTIFIER=$(WMF_APP_GROUP_IDENTIFIER)", + ); + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_SWIFT_FLAGS = "-DNDEBUG -DWMF_STAGING"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + WMF_APP_GROUP_IDENTIFIER = group.org.wikimedia.wikipedia.beta; + }; + name = Staging; + }; + D8A42A3E1E814FAA00D8E281 /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = Wikipedia; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Staging; + }; + D8A42A3F1E814FAA00D8E281 /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Staging; + }; + D8A42A411E814FAA00D8E281 /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREFIX_HEADER = "WikipediaUnitTests/WikipediaUnitTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SOURCE_ROOT_DIR=@\\\"\"$(SOURCE_ROOT)\"\\\"", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + $CONFIGURATION_TEMP_DIR/Wikipedia.build/DerivedSources, + "$(inherited)", + ); + INFOPLIST_FILE = WikipediaUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Library/Frameworks/, + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.wikimedia.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wikipedia.app/Wikipedia"; + }; + name = Staging; + }; + D8A42A421E814FAA00D8E281 /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = ContinueReadingWidget/ContinueReadingWidget.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = ContinueReadingWidget/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta.ContinueReadingWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Staging; + }; + D8A42A441E814FAA00D8E281 /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 0; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "WMF Framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.WMF; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_INSTALL_OBJC_HEADER = YES; + VERSION_INFO_PREFIX = ""; + }; + name = Staging; + }; + D8A42A451E814FE000D8E281 /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_NS_ASSERTIONS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + "$(inherited)", + "WMF_APP_GROUP_IDENTIFIER=$(WMF_APP_GROUP_IDENTIFIER)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-DDEBUG -DWMF_STAGING"; + SDKROOT = iphoneos; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + WMF_APP_GROUP_IDENTIFIER = group.org.wikimedia.wikipedia.beta; + }; + name = StagingDebug; + }; + D8A42A461E814FE000D8E281 /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = Wikipedia; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = StagingDebug; + }; + D8A42A471E814FE000D8E281 /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = StagingDebug; + }; + D8A42A491E814FE000D8E281 /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREFIX_HEADER = "WikipediaUnitTests/WikipediaUnitTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SOURCE_ROOT_DIR=@\\\"\"$(SOURCE_ROOT)\"\\\"", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + $CONFIGURATION_TEMP_DIR/Wikipedia.build/DerivedSources, + "$(inherited)", + ); + INFOPLIST_FILE = WikipediaUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Library/Frameworks/, + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.wikimedia.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wikipedia ${CONFIGURATION}.app/Wikipedia ${CONFIGURATION}"; + }; + name = StagingDebug; + }; + D8A42A4A1E814FE000D8E281 /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = ContinueReadingWidget/ContinueReadingWidget.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = ContinueReadingWidget/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -DDEBUG"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta.ContinueReadingWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = StagingDebug; + }; + D8A42A4C1E814FE000D8E281 /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 0; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "WMF Framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.WMF; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_INSTALL_OBJC_HEADER = YES; + VERSION_INFO_PREFIX = ""; + }; + name = StagingDebug; + }; + D8A42C221E815A9C00D8E281 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + D8A42C231E815A9C00D8E281 /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = StagingDebug; + }; + D8A42C251E815A9C00D8E281 /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + SystemConfiguration, + "-framework", + WebKit, + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = ExperimentalDebug; + }; + D8A42C261E815A9C00D8E281 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + D8A42C271E815A9C00D8E281 /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Staging; + }; + D8A42C291E815A9C00D8E281 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Test; + }; + D8A42C2A1E815A9C00D8E281 /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + WebKit, + "-framework", + SystemConfiguration, + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Experimental; + }; + D8A42C301E815C2800D8E281 /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "NDEBUG=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + "WMF_APP_GROUP_IDENTIFIER=$(WMF_APP_GROUP_IDENTIFIER)", + "WMF_NO_APP_GROUP=1", + ); + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_SWIFT_FLAGS = "-DNDEBUG -DWMF_NO_APP_GROUP"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + WMF_APP_GROUP_IDENTIFIER = group.org.wikimedia.wikipedia; + }; + name = UserTesting; + }; + D8A42C311E815C2800D8E281 /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = Wikipedia; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = UserTesting; + }; + D8A42C321E815C2800D8E281 /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = UserTesting; + }; + D8A42C331E815C2800D8E281 /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = UserTesting; + }; + D8A42C351E815C2800D8E281 /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREFIX_HEADER = "WikipediaUnitTests/WikipediaUnitTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SOURCE_ROOT_DIR=@\\\"\"$(SOURCE_ROOT)\"\\\"", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + $CONFIGURATION_TEMP_DIR/Wikipedia.build/DerivedSources, + "$(inherited)", + ); + INFOPLIST_FILE = WikipediaUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Library/Frameworks/, + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.wikimedia.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wikipedia.app/Wikipedia"; + }; + name = UserTesting; + }; + D8A42C361E815C2800D8E281 /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = ContinueReadingWidget/ContinueReadingWidget.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = ContinueReadingWidget/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.ContinueReadingWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = UserTesting; + }; + D8A42C381E815C2800D8E281 /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 0; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "WMF Framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.WMF; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_INSTALL_OBJC_HEADER = YES; + VERSION_INFO_PREFIX = ""; + }; + name = UserTesting; + }; + D8A42C391E815C3200D8E281 /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_CODE_COVERAGE = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_NS_ASSERTIONS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + "$(inherited)", + "WMF_APP_GROUP_IDENTIFIER=$(WMF_APP_GROUP_IDENTIFIER)", + "WMF_NO_APP_GROUP=1", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-DDEBUG -DWMF_NO_APP_GROUP"; + SDKROOT = iphoneos; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + WMF_APP_GROUP_IDENTIFIER = group.org.wikimedia.wikipedia; + }; + name = UserTestingDebug; + }; + D8A42C3A1E815C3200D8E281 /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = Wikipedia; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = UserTestingDebug; + }; + D8A42C3B1E815C3200D8E281 /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = UserTestingDebug; + }; + D8A42C3C1E815C3200D8E281 /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/User Testing-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.usertesting; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = UserTestingDebug; + }; + D8A42C3E1E815C3200D8E281 /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREFIX_HEADER = "WikipediaUnitTests/WikipediaUnitTests-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "SOURCE_ROOT_DIR=@\\\"\"$(SOURCE_ROOT)\"\\\"", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = ( + $CONFIGURATION_TEMP_DIR/Wikipedia.build/DerivedSources, + "$(inherited)", + ); + INFOPLIST_FILE = WikipediaUnitTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Library/Frameworks/, + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.wikimedia.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "WikipediaUnitTests/Code/WikipediaUnitTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Wikipedia ${CONFIGURATION}.app/Wikipedia ${CONFIGURATION}"; + }; + name = UserTestingDebug; + }; + D8A42C3F1E815C3200D8E281 /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = ContinueReadingWidget/ContinueReadingWidget.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = ContinueReadingWidget/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -DDEBUG"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.ContinueReadingWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = UserTestingDebug; + }; + D8A42C411E815C3200D8E281 /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 0; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "WMF Framework/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.WMF; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_INSTALL_OBJC_HEADER = YES; + VERSION_INFO_PREFIX = ""; + }; + name = UserTestingDebug; + }; + D8B589AA21CD05080027083A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + D8B589AB21CD05080027083A /* LocalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = LocalDebug; + }; + D8B589AD21CD05080027083A /* UITest */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = UITest; + }; + D8B589B021CD05080027083A /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = UserTestingDebug; + }; + D8B589B121CD05080027083A /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = StagingDebug; + }; + D8B589B321CD05080027083A /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = ExperimentalDebug; + }; + D8B589B421CD05080027083A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + D8B589B621CD05080027083A /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = UserTesting; + }; + D8B589B721CD05080027083A /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = Staging; + }; + D8B589B921CD05080027083A /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = Test; + }; + D8B589BA21CD05080027083A /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_HARDENED_RUNTIME = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = Experimental; + }; + D8CE26A91E698E2400DAE2E0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + D8CE26AB1E698E2400DAE2E0 /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + SystemConfiguration, + "-framework", + WebKit, + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = ExperimentalDebug; + }; + D8CE26AC1E698E2400DAE2E0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + D8CE26AD1E698E2400DAE2E0 /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Test; + }; + D8CE26AE1E698E2400DAE2E0 /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Experimental-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + WebKit, + "-framework", + SystemConfiguration, + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Experimental; + }; + D8EC3FA91E9BDA35006712EB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + D8EC3FAA1E9BDA35006712EB /* UserTestingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = UserTestingDebug; + }; + D8EC3FAB1E9BDA35006712EB /* StagingDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = StagingDebug; + }; + D8EC3FAD1E9BDA35006712EB /* ExperimentalDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + SystemConfiguration, + "-framework", + WebKit, + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WRAPPER_EXTENSION = app; + }; + name = ExperimentalDebug; + }; + D8EC3FAE1E9BDA35006712EB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + D8EC3FAF1E9BDA35006712EB /* UserTesting */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = UserTesting; + }; + D8EC3FB01E9BDA35006712EB /* Staging */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfbeta; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Staging; + }; + D8EC3FB21E9BDA35006712EB /* Test */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Test; + }; + D8EC3FB31E9BDA35006712EB /* Experimental */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AlphaAppIcon; + CODE_SIGN_ENTITLEMENTS = Wikipedia/Wikipedia.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = AKK7J2GV64; + ENABLE_TESTABILITY = NO; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NS_BLOCK_ASSERTIONS=1", + ); + INFOPLIST_FILE = "Wikipedia/Staging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + WebKit, + "-framework", + SystemConfiguration, + ); + PRODUCT_BUNDLE_IDENTIFIER = org.wikimedia.wikipedia.tfalpha; + PRODUCT_MODULE_NAME = Wikipedia; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = NO; + SWIFT_OBJC_BRIDGING_HEADER = "Wikipedia/Code/Wikipedia-Bridging-Header.h"; + WRAPPER_EXTENSION = app; + }; + name = Experimental; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 00021DFD24D48EFE00476F97 /* Build configuration list for PBXNativeTarget "WidgetsExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 00021DEF24D48EFE00476F97 /* Debug */, + 00021DF024D48EFE00476F97 /* LocalDebug */, + 00021DF224D48EFE00476F97 /* UITest */, + 00021DF324D48EFE00476F97 /* UserTestingDebug */, + 00021DF424D48EFE00476F97 /* StagingDebug */, + 00021DF624D48EFE00476F97 /* ExperimentalDebug */, + 00021DF724D48EFE00476F97 /* Release */, + 00021DF824D48EFE00476F97 /* UserTesting */, + 00021DF924D48EFE00476F97 /* Staging */, + 00021DFB24D48EFE00476F97 /* Test */, + 00021DFC24D48EFE00476F97 /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0E8380761D64989F0076EDE4 /* Build configuration list for PBXNativeTarget "ContinueReadingWidget" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0E8380711D64989F0076EDE4 /* Debug */, + D854323B2188CC1300E895B5 /* LocalDebug */, + 8350FC4420DA7F0200C19D60 /* UITest */, + D8A42C3F1E815C3200D8E281 /* UserTestingDebug */, + D8A42A4A1E814FE000D8E281 /* StagingDebug */, + D858A8051DA6DD00009C3DEB /* ExperimentalDebug */, + 0E8380731D64989F0076EDE4 /* Release */, + D8A42C361E815C2800D8E281 /* UserTesting */, + D8A42A421E814FAA00D8E281 /* Staging */, + 0E8380741D64989F0076EDE4 /* Test */, + D858A7FD1DA6DBC8009C3DEB /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 676C865726D40AEB00A704C1 /* Build configuration list for PBXNativeTarget "NotificationServiceExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 676C864C26D40AEB00A704C1 /* Debug */, + 676C864D26D40AEB00A704C1 /* LocalDebug */, + 676C864E26D40AEB00A704C1 /* UITest */, + 676C864F26D40AEB00A704C1 /* UserTestingDebug */, + 676C865026D40AEB00A704C1 /* StagingDebug */, + 676C865126D40AEB00A704C1 /* ExperimentalDebug */, + 676C865226D40AEB00A704C1 /* Release */, + 676C865326D40AEB00A704C1 /* UserTesting */, + 676C865426D40AEB00A704C1 /* Staging */, + 676C865526D40AEB00A704C1 /* Test */, + 676C865626D40AEB00A704C1 /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B0606EC320AA6FF0006EC6B9 /* Build configuration list for PBXNativeTarget "WikipediaUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B0606EB520AA6FF0006EC6B9 /* Debug */, + D85432412188CC1300E895B5 /* LocalDebug */, + 8350FC4A20DA7F0200C19D60 /* UITest */, + B0606EB820AA6FF0006EC6B9 /* UserTestingDebug */, + B0606EB920AA6FF0006EC6B9 /* StagingDebug */, + B0606EBB20AA6FF0006EC6B9 /* ExperimentalDebug */, + B0606EBC20AA6FF0006EC6B9 /* Release */, + B0606EBE20AA6FF0006EC6B9 /* UserTesting */, + B0606EBF20AA6FF0006EC6B9 /* Staging */, + B0606EC120AA6FF0006EC6B9 /* Test */, + B0606EC220AA6FF0006EC6B9 /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BC42735A1A7C736800068882 /* Build configuration list for PBXNativeTarget "WikipediaUnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BC42735B1A7C736800068882 /* Debug */, + D85432392188CC1300E895B5 /* LocalDebug */, + 8350FC4220DA7F0200C19D60 /* UITest */, + D8A42C3E1E815C3200D8E281 /* UserTestingDebug */, + D8A42A491E814FE000D8E281 /* StagingDebug */, + D858A8041DA6DD00009C3DEB /* ExperimentalDebug */, + BC42735C1A7C736800068882 /* Release */, + D8A42C351E815C2800D8E281 /* UserTesting */, + D8A42A411E814FAA00D8E281 /* Staging */, + 0EAB7A221D47AF8000E7CF8E /* Test */, + D858A7FC1DA6DBC8009C3DEB /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D4991430181D51DE00E6073C /* Build configuration list for PBXProject "Wikipedia" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D4991468181D51DF00E6073C /* Debug */, + D85432322188CC1300E895B5 /* LocalDebug */, + 8350FC3B20DA7F0200C19D60 /* UITest */, + D8A42C391E815C3200D8E281 /* UserTestingDebug */, + D8A42A451E814FE000D8E281 /* StagingDebug */, + D858A8021DA6DD00009C3DEB /* ExperimentalDebug */, + D4991469181D51DF00E6073C /* Release */, + D8A42C301E815C2800D8E281 /* UserTesting */, + D8A42A3D1E814FAA00D8E281 /* Staging */, + 0EAB7A201D47AF8000E7CF8E /* Test */, + D858A7FA1DA6DBC8009C3DEB /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D499146A181D51DF00E6073C /* Build configuration list for PBXNativeTarget "Wikipedia" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D499146B181D51DF00E6073C /* Debug */, + D85432332188CC1300E895B5 /* LocalDebug */, + 8350FC3C20DA7F0200C19D60 /* UITest */, + D8A42C3A1E815C3200D8E281 /* UserTestingDebug */, + D8A42A461E814FE000D8E281 /* StagingDebug */, + D858A8031DA6DD00009C3DEB /* ExperimentalDebug */, + D499146C181D51DF00E6073C /* Release */, + D8A42C311E815C2800D8E281 /* UserTesting */, + D8A42A3E1E814FAA00D8E281 /* Staging */, + 0EAB7A211D47AF8000E7CF8E /* Test */, + D858A7FB1DA6DBC8009C3DEB /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D844D97B1D6CB2600042D692 /* Build configuration list for PBXNativeTarget "WMF" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D844D9751D6CB2600042D692 /* Debug */, + D854323F2188CC1300E895B5 /* LocalDebug */, + 8350FC4820DA7F0200C19D60 /* UITest */, + D8A42C411E815C3200D8E281 /* UserTestingDebug */, + D8A42A4C1E814FE000D8E281 /* StagingDebug */, + D858A8081DA6DD00009C3DEB /* ExperimentalDebug */, + D844D9781D6CB2600042D692 /* Release */, + D8A42C381E815C2800D8E281 /* UserTesting */, + D8A42A441E814FAA00D8E281 /* Staging */, + D844D9791D6CB2600042D692 /* Test */, + D858A8001DA6DBC8009C3DEB /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D8479FC01F222FE90025FD7A /* Build configuration list for PBXNativeTarget "Wikipedia Stickers" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8479FB31F222FE90025FD7A /* Debug */, + D854323E2188CC1300E895B5 /* LocalDebug */, + 8350FC4720DA7F0200C19D60 /* UITest */, + D8479FB51F222FE90025FD7A /* UserTestingDebug */, + D8479FB61F222FE90025FD7A /* StagingDebug */, + D8479FB81F222FE90025FD7A /* ExperimentalDebug */, + D8479FB91F222FE90025FD7A /* Release */, + D8479FBB1F222FE90025FD7A /* UserTesting */, + D8479FBC1F222FE90025FD7A /* Staging */, + D8479FBE1F222FE90025FD7A /* Test */, + D8479FBF1F222FE90025FD7A /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D870216F1EBA63EF000D02D6 /* Build configuration list for PBXNativeTarget "localization" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D87021641EBA63EF000D02D6 /* Debug */, + D85432402188CC1300E895B5 /* LocalDebug */, + 8350FC4920DA7F0200C19D60 /* UITest */, + D87021651EBA63EF000D02D6 /* UserTestingDebug */, + D87021661EBA63EF000D02D6 /* StagingDebug */, + D87021681EBA63EF000D02D6 /* ExperimentalDebug */, + D87021691EBA63EF000D02D6 /* Release */, + D870216A1EBA63EF000D02D6 /* UserTesting */, + D870216B1EBA63EF000D02D6 /* Staging */, + D870216D1EBA63EF000D02D6 /* Test */, + D870216E1EBA63EF000D02D6 /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D8A42C211E815A9C00D8E281 /* Build configuration list for PBXNativeTarget "User Testing" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8A42C221E815A9C00D8E281 /* Debug */, + D85432362188CC1300E895B5 /* LocalDebug */, + 8350FC3F20DA7F0200C19D60 /* UITest */, + D8A42C3C1E815C3200D8E281 /* UserTestingDebug */, + D8A42C231E815A9C00D8E281 /* StagingDebug */, + D8A42C251E815A9C00D8E281 /* ExperimentalDebug */, + D8A42C261E815A9C00D8E281 /* Release */, + D8A42C331E815C2800D8E281 /* UserTesting */, + D8A42C271E815A9C00D8E281 /* Staging */, + D8A42C291E815A9C00D8E281 /* Test */, + D8A42C2A1E815A9C00D8E281 /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D8B589A921CD05080027083A /* Build configuration list for PBXNativeTarget "languages" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8B589AA21CD05080027083A /* Debug */, + D8B589AB21CD05080027083A /* LocalDebug */, + D8B589AD21CD05080027083A /* UITest */, + D8B589B021CD05080027083A /* UserTestingDebug */, + D8B589B121CD05080027083A /* StagingDebug */, + D8B589B321CD05080027083A /* ExperimentalDebug */, + D8B589B421CD05080027083A /* Release */, + D8B589B621CD05080027083A /* UserTesting */, + D8B589B721CD05080027083A /* Staging */, + D8B589B921CD05080027083A /* Test */, + D8B589BA21CD05080027083A /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D8CE26A81E698E2400DAE2E0 /* Build configuration list for PBXNativeTarget "Experimental" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8CE26A91E698E2400DAE2E0 /* Debug */, + D85432342188CC1300E895B5 /* LocalDebug */, + 8350FC3D20DA7F0200C19D60 /* UITest */, + D8A42C3B1E815C3200D8E281 /* UserTestingDebug */, + D8A42A471E814FE000D8E281 /* StagingDebug */, + D8CE26AB1E698E2400DAE2E0 /* ExperimentalDebug */, + D8CE26AC1E698E2400DAE2E0 /* Release */, + D8A42C321E815C2800D8E281 /* UserTesting */, + D8A42A3F1E814FAA00D8E281 /* Staging */, + D8CE26AD1E698E2400DAE2E0 /* Test */, + D8CE26AE1E698E2400DAE2E0 /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D8EC3FA81E9BDA35006712EB /* Build configuration list for PBXNativeTarget "Staging" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D8EC3FA91E9BDA35006712EB /* Debug */, + D85432352188CC1300E895B5 /* LocalDebug */, + 8350FC3E20DA7F0200C19D60 /* UITest */, + D8EC3FAA1E9BDA35006712EB /* UserTestingDebug */, + D8EC3FAB1E9BDA35006712EB /* StagingDebug */, + D8EC3FAD1E9BDA35006712EB /* ExperimentalDebug */, + D8EC3FAE1E9BDA35006712EB /* Release */, + D8EC3FAF1E9BDA35006712EB /* UserTesting */, + D8EC3FB01E9BDA35006712EB /* Staging */, + D8EC3FB21E9BDA35006712EB /* Test */, + D8EC3FB31E9BDA35006712EB /* Experimental */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 67359892299ED915002EE8D1 /* XCRemoteSwiftPackageReference "wikipedia-ios-components" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/wikimedia/wikipedia-ios-components.git"; + requirement = { + branch = main; + kind = branch; + }; + }; + 67A770C6251BFE0400F94EF9 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/CocoaLumberjack/CocoaLumberjack.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.7.4; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 46EB334729E1D204001D5EAF /* Shared */ = { + isa = XCSwiftPackageProductDependency; + productName = Shared; + }; + 67A770C7251BFE0400F94EF9 /* CocoaLumberjackSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 67A770C6251BFE0400F94EF9 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; + productName = CocoaLumberjackSwift; + }; + 83FFFFB929AEC094005506A0 /* Components */ = { + isa = XCSwiftPackageProductDependency; + package = 67359892299ED915002EE8D1 /* XCRemoteSwiftPackageReference "wikipedia-ios-components" */; + productName = Components; + }; +/* End XCSwiftPackageProductDependency section */ + +/* Begin XCVersionGroup section */ + 70B798122575714100C10BCA /* EventPlatformEvents.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 70B798132575714100C10BCA /* EventPlatformEvents.xcdatamodel */, + ); + currentVersion = 70B798132575714100C10BCA /* EventPlatformEvents.xcdatamodel */; + path = EventPlatformEvents.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; + 7A9133A822B162E7002AEBCF /* RemoteNotifications.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 670AF1B826C573EB005F76D0 /* RemoteNotifications 3.xcdatamodel */, + 83703A7724DC44C600EE98EA /* RemoteNotifications 2.xcdatamodel */, + 7A9133A922B162E8002AEBCF /* RemoteNotifications.xcdatamodel */, + ); + currentVersion = 670AF1B826C573EB005F76D0 /* RemoteNotifications 3.xcdatamodel */; + path = RemoteNotifications.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; + B32535EF1EE856FF00372E93 /* EventLogging.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 83703A7824DC44CD00EE98EA /* EventLogging 2.xcdatamodel */, + B32535F01EE856FF00372E93 /* EventLogging.xcdatamodel */, + ); + currentVersion = 83703A7824DC44CD00EE98EA /* EventLogging 2.xcdatamodel */; + path = EventLogging.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; + D844480D1DDA33D900425630 /* Wikipedia.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 53BAB79925DDDEE100A5ED4E /* Wikipedia 6.xcdatamodel */, + 53478DE425AF8CB900F31DC2 /* Wikipedia 5.xcdatamodel */, + 8387CE8624C8C6CF00439D93 /* Wikipedia 4.xcdatamodel */, + D834DAA823E8538700B7B0E9 /* Wikipedia 3.xcdatamodel */, + 67E8B0B6226F5E3800537BC9 /* Wikipedia 2.xcdatamodel */, + D844480E1DDA33D900425630 /* Wikipedia.xcdatamodel */, + ); + currentVersion = 53BAB79925DDDEE100A5ED4E /* Wikipedia 6.xcdatamodel */; + path = Wikipedia.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; + D8CD97631E83FAB400ECCA9D /* Cache.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 67D6C008240581B2005709B1 /* Cache 2.xcdatamodel */, + D8CD97641E83FAB400ECCA9D /* Cache.xcdatamodel */, + ); + currentVersion = 67D6C008240581B2005709B1 /* Cache 2.xcdatamodel */; + path = Cache.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ + }; + rootObject = D499142D181D51DE00E6073C /* Project object */; +} diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Apps/Wikipedia/Wikipedia.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Apps/Wikipedia/Wikipedia.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Apps/Wikipedia/Wikipedia.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..89937db --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,32 @@ +{ + "pins" : [ + { + "identity" : "cocoalumberjack", + "kind" : "remoteSourceControl", + "location" : "https://github.com/CocoaLumberjack/CocoaLumberjack.git", + "state" : { + "revision" : "80ada1f753b0d53d9b57c465936a7c4169375002", + "version" : "3.7.4" + } + }, + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log.git", + "state" : { + "revision" : "5d66f7ba25daf4f94100e7022febf3c75e37a6c7", + "version" : "1.4.2" + } + }, + { + "identity" : "wikipedia-ios-components", + "kind" : "remoteSourceControl", + "location" : "https://github.com/wikimedia/wikipedia-ios-components.git", + "state" : { + "branch" : "main", + "revision" : "e9d5031d5c75492be55b4143b397cd43227de7ed" + } + } + ], + "version" : 2 +} diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/IDETemplateMacros.plist b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/IDETemplateMacros.plist new file mode 100644 index 0000000..3c3c910 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/IDETemplateMacros.plist @@ -0,0 +1,8 @@ + + + + + FILEHEADER + ~~~**DELETE THIS HEADER**~~~ + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/ContinueReadingWidget.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/ContinueReadingWidget.xcscheme new file mode 100644 index 0000000..15f1051 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/ContinueReadingWidget.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Experimental.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Experimental.xcscheme new file mode 100644 index 0000000..dbbf70c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Experimental.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/FeaturedArticleWidget.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/FeaturedArticleWidget.xcscheme new file mode 100644 index 0000000..9348518 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/FeaturedArticleWidget.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/InTheNewsNotification.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/InTheNewsNotification.xcscheme new file mode 100644 index 0000000..3b15ab6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/InTheNewsNotification.xcscheme @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Local Page Content Service & Announcements.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Local Page Content Service & Announcements.xcscheme new file mode 100644 index 0000000..ac011da --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Local Page Content Service & Announcements.xcscheme @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/NotificationServiceExtension.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/NotificationServiceExtension.xcscheme new file mode 100644 index 0000000..b9dfce5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/NotificationServiceExtension.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Performance Testing.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Performance Testing.xcscheme new file mode 100644 index 0000000..06579d2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Performance Testing.xcscheme @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/RTL.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/RTL.xcscheme new file mode 100644 index 0000000..0c1ae35 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/RTL.xcscheme @@ -0,0 +1,381 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Staging.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Staging.xcscheme new file mode 100644 index 0000000..f398af3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Staging.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/TopReadWidget.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/TopReadWidget.xcscheme new file mode 100644 index 0000000..45b325c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/TopReadWidget.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme new file mode 100644 index 0000000..7544f21 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/UITests.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Update Languages.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Update Languages.xcscheme new file mode 100644 index 0000000..7ef3567 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Update Languages.xcscheme @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Update Localizations.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Update Localizations.xcscheme new file mode 100644 index 0000000..a9f8d84 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Update Localizations.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/User Testing.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/User Testing.xcscheme new file mode 100644 index 0000000..b63eacf --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/User Testing.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/WMF.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/WMF.xcscheme new file mode 100644 index 0000000..894c491 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/WMF.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/WidgetsExtension.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/WidgetsExtension.xcscheme new file mode 100644 index 0000000..1f38adc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/WidgetsExtension.xcscheme @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Wikipedia Stickers.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Wikipedia Stickers.xcscheme new file mode 100644 index 0000000..8f74754 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Wikipedia Stickers.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Wikipedia.xcscheme b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Wikipedia.xcscheme new file mode 100644 index 0000000..7a1b14a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia.xcodeproj/xcshareddata/xcschemes/Wikipedia.xcscheme @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/APIURLComponentsBuilder.swift b/Apps/Wikipedia/Wikipedia/Code/APIURLComponentsBuilder.swift new file mode 100644 index 0000000..9cd25fb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/APIURLComponentsBuilder.swift @@ -0,0 +1,230 @@ +import CocoaLumberjackSwift + +enum APIURLComponentsBuilderError: Error { + case failureConvertingJsonDataToString +} + +/// APIURLComponentsBuilder stores API host components and the base path (/w/api.php, /api/rest_v1, etc) and builds URLs for various endpoints +public struct APIURLComponentsBuilder { + let hostComponents: URLComponents + let basePathComponents: [String] + + func components(byAppending pathComponents: [String] = [], queryParameters: [String: Any]? = nil) -> URLComponents { + var components = hostComponents + components.replacePercentEncodedPathWithPathComponents(basePathComponents + pathComponents) + components.replacePercentEncodedQueryWithQueryParameters(queryParameters) + return components + } + + func components(byAssigningPayloadToPercentEncodedQuery payload: NSObject) throws -> URLComponents { + guard JSONSerialization.isValidJSONObject(payload) else { + throw APIURLComponentsBuilderError.failureConvertingJsonDataToString + } + let payloadJsonData = try JSONSerialization.data(withJSONObject:payload, options: []) + + guard let payloadString = String(data: payloadJsonData, encoding: .utf8) else { + throw APIURLComponentsBuilderError.failureConvertingJsonDataToString + } + + let encodedPayloadJsonString = payloadString.wmf_UTF8StringWithPercentEscapes() + + var components = hostComponents + components.replacePercentEncodedPathWithPathComponents(basePathComponents) + components.percentEncodedQuery = encodedPayloadJsonString + return components + } + + /// RESTBase is a set of REST APIs utilized by the app for the feed, page summaries, page content, and others + /// They exist on most wikis - example doc for enwiki, change the domain for other wikis: https://en.wikipedia.org/api/rest_v1/ + struct RESTBase { + + public enum BuilderType { + case production + case stagingAppsLabsPCS + case localPCS + case localAnnouncements + + func builder(withWikiHost wikiHost: String? = nil) -> APIURLComponentsBuilder { + switch self { + case .production: return RESTBase.productionBuilder(withWikiHost: wikiHost) + case .stagingAppsLabsPCS: return RESTBase.stagingBuilderForAppsLabsPCS(withWikiHost: wikiHost) + case .localPCS: return RESTBase.localBuilderForPCS(withWikiHost: wikiHost) + case .localAnnouncements: return RESTBase.localBuilderForAnnouncements(withWikiHost: wikiHost) + } + } + } + + /// Returns a block that will return a builder for a given host. For production, the host is the host of the wiki: https://en.wikipedia.org/api/rest_v1/ + private static func productionBuilder(withWikiHost wikiHost: String? = nil) -> APIURLComponentsBuilder { + var components = URLComponents() + components.host = wikiHost ?? Configuration.Domain.englishWikipedia + components.scheme = Configuration.Scheme.https + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: Configuration.Path.restBaseAPIComponents) + } + + // For staging and local, the host is the staging host and the wiki host is in the path: + // https://mobileapps.wmflabs.org/en.wikipedia.org/v1/ + private static func stagingBuilderForAppsLabsPCS(withWikiHost wikiHost: String? = nil) -> APIURLComponentsBuilder { + var components = URLComponents() + components.scheme = Configuration.Scheme.https + components.host = Configuration.Domain.appsLabs + + let host = wikiHost ?? Configuration.Domain.metaWiki + let baseComponents = [host, "v1"] + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: baseComponents) + } + + //// For staging and local, the host is the staging host and the wiki host is in the path: http://localhost:8888/en.wikipedia.org/v1/ + private static func localBuilderForPCS(withWikiHost wikiHost: String? = nil) -> APIURLComponentsBuilder { + var components = URLComponents() + components.scheme = Configuration.Scheme.http + components.host = Configuration.Domain.localhost + components.port = 8888 + + let host = wikiHost ?? Configuration.Domain.metaWiki + let baseComponents = [host, "v1"] + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: baseComponents) + } + + // For staging and local, the host is the staging host and the wiki host is in the path: http://localhost:8889/en.wikipedia.org/v1/ + private static func localBuilderForAnnouncements(withWikiHost wikiHost: String? = nil) -> APIURLComponentsBuilder { + var components = URLComponents() + components.scheme = Configuration.Scheme.http + components.host = Configuration.Domain.localhost + components.port = 8889 + + let host = wikiHost ?? Configuration.Domain.metaWiki + let baseComponents = [host, "v1"] + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: baseComponents) + } + } + + /// MediaWiki API + /// Doc for each wiki usually available at the API sandbox. Alter the domain for other wikis: https://en.wikipedia.org/wiki/Special:ApiSandbox + struct MediaWiki { + + public enum BuilderType { + case productionRest + case production + + func builder(withWikiHost wikiHost: String? = nil) -> APIURLComponentsBuilder { + switch self { + case .productionRest: + return MediaWiki.productionRestBuilder(withWikiHost: wikiHost) + case .production: + return MediaWiki.productionBuilder(withWikiHost: wikiHost) + } + } + } + + private static func productionRestBuilder(withWikiHost wikiHost: String? = nil) -> APIURLComponentsBuilder { + var components = URLComponents() + components.host = wikiHost ?? Configuration.Domain.englishWikipedia + components.scheme = Configuration.Scheme.https + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: Configuration.Path.mediaWikiRestAPIComponents) + } + + private static func productionBuilder(withWikiHost wikiHost: String? = nil) -> APIURLComponentsBuilder { + var components = URLComponents() + components.host = wikiHost ?? Configuration.Domain.metaWiki + components.scheme = Configuration.Scheme.https + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: Configuration.Path.mediaWikiAPIComponents) + } + } + + // This is still the MediaWiki API, but because there is no associated language, the calls to build these urls need to be slightly different. + struct Wikidata { + + public enum BuilderType { + case production + case betaLabs + + func builder() -> APIURLComponentsBuilder { + switch self { + case .production: + return Wikidata.productionBuilder() + case .betaLabs: + return Wikidata.betaLabsBuilder() + } + } + } + + private static func productionBuilder() -> APIURLComponentsBuilder { + var components = URLComponents() + components.host = "www.\(Configuration.Domain.wikidata)" + components.scheme = Configuration.Scheme.https + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: Configuration.Path.mediaWikiAPIComponents) + } + + private static func betaLabsBuilder() -> APIURLComponentsBuilder { + var components = URLComponents() + components.host = Configuration.Domain.wikidataBetaLabs + components.scheme = Configuration.Scheme.https + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: Configuration.Path.mediaWikiAPIComponents) + } + } + + // This is still the MediaWiki API, but because there is no associated language, the calls to build these urls need to be slightly different. + struct Commons { + + public enum BuilderType { + case production + case betaLabs + + func builder() -> APIURLComponentsBuilder { + switch self { + case .production: + return Commons.productionBuilder() + case .betaLabs: + return Commons.betaLabsBuilder() + } + } + } + + private static func productionBuilder() -> APIURLComponentsBuilder { + var components = URLComponents() + components.host = "commons.\(Configuration.Domain.wikimedia)" + components.scheme = Configuration.Scheme.https + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: Configuration.Path.mediaWikiAPIComponents) + } + + private static func betaLabsBuilder() -> APIURLComponentsBuilder { + var components = URLComponents() + components.host = Configuration.Domain.commonsBetaLabs + components.scheme = Configuration.Scheme.https + return APIURLComponentsBuilder(hostComponents: components, basePathComponents: Configuration.Path.mediaWikiAPIComponents) + } + } + + struct EventLogging { + + public enum BuilderType { + case production + case staging + + func builder(withWikiHost wikiHost: String? = nil) -> APIURLComponentsBuilder { + switch self { + case .production: + return EventLogging.productionBuilder() + case .staging: + return EventLogging.stagingBuilder() + } + } + } + + private static func productionBuilder() -> APIURLComponentsBuilder { + var eventLoggingComponents = URLComponents() + eventLoggingComponents.scheme = "https" + eventLoggingComponents.host = "meta.wikimedia.org" + return APIURLComponentsBuilder(hostComponents: eventLoggingComponents, basePathComponents: ["beacon","event"]) + } + + private static func stagingBuilder() -> APIURLComponentsBuilder { + var eventLoggingComponents = URLComponents() + eventLoggingComponents.scheme = "https" + eventLoggingComponents.host = "deployment.wikimedia.beta.wmflabs.org" + return APIURLComponentsBuilder(hostComponents: eventLoggingComponents, basePathComponents: ["beacon","event"]) + } + + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/AboutViewController.h b/Apps/Wikipedia/Wikipedia/Code/AboutViewController.h new file mode 100644 index 0000000..071b05d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AboutViewController.h @@ -0,0 +1,9 @@ +@import WebKit; +@import WMF.Swift; +#import "WMFViewController.h" + +@interface AboutViewController : WMFViewController + +- (instancetype)initWithTheme:(WMFTheme *)theme; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/AboutViewController.m b/Apps/Wikipedia/Wikipedia/Code/AboutViewController.m new file mode 100644 index 0000000..ca30b15 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AboutViewController.m @@ -0,0 +1,358 @@ +#import "AboutViewController.h" +#import +#import +#import +#import "UIBarButtonItem+WMFButtonConvenience.h" +#import "Wikipedia-Swift.h" + +static NSString *const kWMFAboutHTMLFile = @"about.html"; +static NSString *const kWMFAboutPlistName = @"AboutViewController"; + +static NSString *const kWMFURLsKey = @"urls"; +static NSString *const kWMFURLsFeedbackKey = @"feedback"; +static NSString *const kWMFURLsTranslateWikiKey = @"twn"; +static NSString *const kWMFURLsWikimediaKey = @"wmf"; +static NSString *const kWMFURLsSpecialistGuildKey = @"tsg"; +static NSString *const kWMFURLsMITKey = @"mit"; +static NSString *const kWMFURLsShareAlikeKey = @"sharealike"; +static NSString *const kWMFURLsAppleMapsKey = @"applemaps"; + +static NSString *const kWMFRepositoriesKey = @"repositories"; + +static NSString *const kWMFLibrariesKey = @"libraries"; +static NSString *const kWMFLibraryNameKey = @"Name"; +static NSString *const kWMFLibraryURLKey = @"Source URL"; +static NSString *const kWMFLibraryLicenseTextKey = @"License Text"; + +static NSString *const kWMFLicenseScheme = @"wmflicense"; +static NSString *const kWMFLicenseRedirectScheme = @"about"; +static NSString *const kWMFLicenseRedirectResourceIdentifier = @"blank"; + +static NSString *const kWMFContributorsKey = @"contributors"; + +@interface WKWebView (AboutViewControllerJavascript) + +@end + +@implementation WKWebView (AboutViewControllerJavascript) + +- (void)wmf_setInnerHTML:(NSString *)html ofElementId:(NSString *)elementId { + // Valid JSON object needs to be an array or dictionary. + NSArray *arrayForEncoding = @[html]; + // Rely on NSJSONSerialization for string escaping: http://stackoverflow.com/a/13569786 + NSString *jsonString = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:arrayForEncoding options:0 error:nil] encoding:NSUTF8StringEncoding]; + NSString *escapedString = [jsonString substringWithRange:NSMakeRange(2, jsonString.length - 4)]; + + [self evaluateJavaScript:[NSString stringWithFormat:@"document.getElementById('%@').innerHTML = \"%@\";", elementId, escapedString] completionHandler:NULL]; +}; + +- (void)wmf_setTextDirection { + NSString *textDirection = ([[UIApplication sharedApplication] wmf_isRTL] ? @"rtl" : @"ltr"); + NSString *textDirectionJS = [NSString stringWithFormat:@"document.body.style.direction = '%@'", textDirection]; + [self evaluateJavaScript:textDirectionJS completionHandler:nil]; +} + +- (void)wmf_setTextFontSize { + NSString *fontSizeJS = [NSString stringWithFormat:@"document.body.style.fontSize = '%f%%'", 100.0f]; + [self evaluateJavaScript:fontSizeJS completionHandler:nil]; +} + +- (void)wmf_setTextFontColor:(WMFTheme *)theme { + NSString *fontColorJS = [NSString stringWithFormat:@"" + "function styleWithSelector (selector, styleSheetID) {" + " function ruleWithSelector(rule) {" + " return (rule.selectorText === selector)" + " }" + " return Array.from(document.getElementById(styleSheetID).sheet.rules)" + " .find(ruleWithSelector)" + " .style" + "}" + "styleWithSelector('body', 'styles').color = '#%@';" + "styleWithSelector('.heading', 'styles').color = '#%@';" + "styleWithSelector('.title', 'styles').color = '#%@';" + "styleWithSelector('A', 'styles').color = '#%@';", + theme.colors.primaryText.wmf_hexString, + theme.colors.primaryText.wmf_hexString, + theme.colors.secondaryText.wmf_hexString, + theme.colors.link.wmf_hexString]; + + [self evaluateJavaScript:fontColorJS completionHandler:nil]; +} + +- (void)wmf_setLogoStyleWithTheme:(WMFTheme *)theme { + // White logo on Dark mode + // Black logo on Default and Sepia modes + if (theme.isDark) { + [self evaluateJavaScript:[NSString stringWithFormat:@"wmf.applyDarkThemeLogo()"] + completionHandler:nil]; + } else { + [self evaluateJavaScript:[NSString stringWithFormat:@"wmf.applyLightThemeLogo()"] + completionHandler:nil]; + } +} + +- (void)wmf_preventTextFromExpandingOnRotation { + [self evaluateJavaScript:@"document.body.style['-webkit-text-size-adjust'] = 'none';" completionHandler:nil]; +} + +@end + +@interface AboutViewController () + +@property (strong, nonatomic) WKWebView *webView; +@property (nonatomic, strong) UIBarButtonItem *buttonX; +@property (nonatomic, strong) UIBarButtonItem *buttonCaretLeft; + +@end + +@implementation AboutViewController + +#pragma mark - UIViewController + +- (instancetype)initWithTheme:(WMFTheme *)theme { + self = [super init]; + if (self) { + self.theme = theme; + } + return self; +} + +- (UIScrollView *)scrollView { + return self.webView.scrollView; +} + +- (void)viewDidLoad { + WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; + WKWebView *wv = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config]; + [super viewDidLoad]; + [self.view wmf_addSubviewWithConstraintsToEdges:wv]; + + wv.navigationDelegate = self; + + self.webView = wv; + + [self loadAboutHTML]; + + self.webView.opaque = NO; + [self applyTheme:self.theme]; + + self.buttonX = [UIBarButtonItem wmf_buttonType:WMFButtonTypeX target:self action:@selector(closeButtonPressed)]; + + self.buttonCaretLeft = [UIBarButtonItem wmf_buttonType:WMFButtonTypeCaretLeft target:self action:@selector(leftButtonPressed)]; + + self.buttonX.accessibilityLabel = WMFLocalizedStringWithDefaultValue(@"menu-cancel-accessibility-label", nil, nil, @"Cancel", @"Accessible label text for toolbar cancel button {{Identical|Cancel}}"); + self.buttonCaretLeft.accessibilityLabel = WMFCommonStrings.accessibilityBackTitle; + + [self updateNavigationBar]; +} + +- (void)loadAboutHTML { + NSURL *assetsFolderURL = [[NSBundle wmf] wmf_assetsFolderURL]; + NSURL *aboutFileURL = [assetsFolderURL URLByAppendingPathComponent:@"about.html" isDirectory:NO]; + [self.webView loadFileURL:aboutFileURL allowingReadAccessToURL:assetsFolderURL]; +} +- (void)closeButtonPressed { + [self.presentingViewController dismissViewControllerAnimated:YES + completion:nil]; +} + +- (void)leftButtonPressed { + [self loadAboutHTML]; +} + +- (BOOL)prefersStatusBarHidden { + return NO; +} + +#pragma mark - Navigation Bar Configuration + +- (void)updateNavigationBar { + self.title = self.title; + self.navigationItem.leftBarButtonItem = [self isDisplayingLicense] ? self.buttonCaretLeft : nil; +} + +- (NSString *)title { + if ([self isDisplayingLicense]) { + return WMFLocalizedStringWithDefaultValue(@"about-libraries-license", nil, nil, @"License", @"About page link title that will display a license for a library used in the app {{Identical|License}}"); + } + return WMFLocalizedStringWithDefaultValue(@"about-title", nil, nil, @"About", @"Title for credits page {{Identical|About}}"); +} + +#pragma mark - Accessors + +- (NSDictionary *)data { + NSString *plistPath = [[NSBundle mainBundle] pathForResource:kWMFAboutPlistName ofType:@"plist"]; + return [NSMutableDictionary dictionaryWithContentsOfFile:plistPath]; +} + +- (NSString *)contributors { + return [self.data[kWMFContributorsKey] componentsJoinedByString:@", "]; +} + +- (NSDictionary *)urls { + return self.data[kWMFURLsKey]; +} + +- (NSString *)createRTLCompatibleLicenseLink:(NSString *)licenseLink { + // See: http://stackoverflow.com/a/7931735 + return [NSString stringWithFormat:@" (%@)‎", licenseLink]; +} + +- (NSString *)repositoryLinks { + NSMutableDictionary *repos = (NSMutableDictionary *)self.data[kWMFRepositoriesKey]; + + for (NSString *repo in [repos copy]) { + repos[repo] = [[self class] linkHTMLForURLString:repos[repo] title:repo]; + } + + NSString *output = [repos.allValues componentsJoinedByString:@", "]; + return output; +} + +- (NSString *)feedbackURL { + NSString *feedbackUrl = self.urls[kWMFURLsFeedbackKey]; + feedbackUrl = [feedbackUrl stringByReplacingOccurrencesOfString:@"$1" withString:[WikipediaAppUtils versionedUserAgent]]; + + NSString *encodedUrlString = + [feedbackUrl stringByRemovingPercentEncoding]; + + return encodedUrlString; +} + +#pragma mark - HTML Injection + +- (void)injectAboutPageContentIntoWebView:(WKWebView *)webView { + void (^setDivHTML)(NSString *, NSString *) = ^void(NSString *divId, NSString *twnString) { + [self.webView wmf_setInnerHTML:twnString ofElementId:divId]; + }; + + setDivHTML(@"version", [[NSBundle mainBundle] wmf_versionForCurrentBundleIdentifier]); + setDivHTML(@"wikipedia", WMFCommonStrings.plainWikipediaName); + setDivHTML(@"contributors_title", WMFLocalizedStringWithDefaultValue(@"about-contributors", nil, nil, @"Contributors", @"Header text for contributors section of the about page. Is not capitalised for aesthetic reasons, but could be capitalised in translations. {{Identical|Contributor}}")); + setDivHTML(@"contributors_body", self.contributors); + setDivHTML(@"translators_title", WMFLocalizedStringWithDefaultValue(@"about-translators", nil, nil, @"Translators", @"Header text for translators section of the about page. Is not capitalised for aesthetic reasons, but could be capitalised in translations. {{Identical|Translator}}")); + setDivHTML(@"testers_title", WMFLocalizedStringWithDefaultValue(@"about-testers", nil, nil, @"Testers", @"Header text for (software) testers section of the about page. Is not capitalised for aesthetic reasons, but could be capitalised in translations.")); + setDivHTML(@"libraries_title", WMFLocalizedStringWithDefaultValue(@"about-libraries", nil, nil, @"Libraries used", @"Header text for libraries section (as in a collection of subprograms used to develop software) of the about page. Is not capitalised for aesthetic reasons, but could be capitalised in translations.")); + setDivHTML(@"libraries_body", [[self class] linkHTMLForURLString:@"wmflicense://licenses" title:WMFLocalizedStringWithDefaultValue(@"about-libraries-complete-list", nil, nil, @"Complete list", @"Title for link to complete list of libraries use by the app")]); + setDivHTML(@"repositories_title", WMFLocalizedStringWithDefaultValue(@"about-repositories", nil, nil, @"Repositories", @"Header text for repositories section of the about page. Is not capitalised for aesthetic reasons, but could be capitalised in translations. {{Identical|Repository}}")); + setDivHTML(@"repositories_body", self.repositoryLinks); + + setDivHTML(@"repositories_subtitle", [NSString stringWithFormat:WMFLocalizedStringWithDefaultValue(@"about-repositories-app-source-license", nil, nil, @"Source code available under the %1$@.", @"Text explaining the app source licensing. %1$@ is the message {{msg-wikimedia|about-repositories-app-source-license-mit}}."), [[self class] linkHTMLForURLString:self.urls[kWMFURLsMITKey] title:WMFLocalizedStringWithDefaultValue(@"about-repositories-app-source-license-mit", nil, nil, @"MIT License", @"Name of the \"MIT\" license")]]); + + setDivHTML(@"feedback_body", [[self class] linkHTMLForURLString:self.feedbackURL title:WMFLocalizedStringWithDefaultValue(@"about-send-feedback", nil, nil, @"Send app feedback", @"Link text for sending app feedback")]); + + setDivHTML(@"places_maps_license_title", WMFLocalizedStringWithDefaultValue(@"about-places-maps-license", nil, nil, @"Places maps license", @"Header text for maps license section")); + setDivHTML(@"places_maps_license_body", [NSString stringWithFormat:WMFLocalizedStringWithDefaultValue(@"about-places-maps-license-details", nil, nil, @"Places uses maps provided by Apple Maps. %1$@.", @"Text explaining license of maps content. %1$@ is the message {{msg-wikimedia|about-places-maps-license-details-link-text}}."), [[self class] linkHTMLForURLString:self.urls[kWMFURLsAppleMapsKey] title:WMFLocalizedStringWithDefaultValue(@"about-places-maps-license-details-link-text", nil, nil, @"Please see here for license details", @"Text used for link to maps license")]]); + + setDivHTML(@"license_title", WMFLocalizedStringWithDefaultValue(@"about-content-license", nil, nil, @"Content license", @"Header text for content license section")); + + setDivHTML(@"license_body", [NSString stringWithFormat:WMFLocalizedStringWithDefaultValue(@"about-content-license-details", nil, nil, @"Unless otherwise specified, content is available under a %1$@.", @"Text explaining license of app content. %1$@ is the message {{msg-wikimedia|about-content-license-details-share-alike-license}}."), [[self class] linkHTMLForURLString:self.urls[kWMFURLsShareAlikeKey] title:WMFLocalizedStringWithDefaultValue(@"about-content-license-details-share-alike-license", nil, nil, @"Creative Commons Attribution-ShareAlike License", @"Name of the \"Creative Commons Attribution-ShareAlike\" license")]]); + + setDivHTML(@"translators_body", [NSString stringWithFormat:WMFLocalizedStringWithDefaultValue(@"about-translators-details", nil, nil, @"Translated by volunteers at %1$@", @"Description of volunteer translation. %1$@ is translatewiki url."), [[self class] linkHTMLForURLString:self.urls[kWMFURLsTranslateWikiKey] title:[self.urls[kWMFURLsTranslateWikiKey] substringFromIndex:7]]]); + setDivHTML(@"testers_body", [NSString stringWithFormat:WMFLocalizedStringWithDefaultValue(@"about-testers-details", nil, nil, @"QA tested by %1$@", @"Description of the Quality Assurance (QA) testers. %1$@ is specialistsguild.org, the website of the testing group."), [[self class] linkHTMLForURLString:self.urls[kWMFURLsSpecialistGuildKey] title:[self.urls[kWMFURLsSpecialistGuildKey] substringFromIndex:7]]]); + + setDivHTML(@"footer", [NSString stringWithFormat:WMFLocalizedStringWithDefaultValue(@"about-product-of", nil, nil, @"Made by the %1$@ with the help of volunteers like you", @"Description of who produced the app. %1$@ is the message {{msg-wikimedia|wikipedia-ios-about-wikimedia-foundation}}."), [[self class] linkHTMLForURLString:self.urls[kWMFURLsWikimediaKey] title:WMFLocalizedStringWithDefaultValue(@"about-wikimedia-foundation", nil, nil, @"Wikimedia Foundation", @"Name of the Wikimedia Foundation. Used by the message {{Msg-wikimedia|wikipedia-ios-about-product-of}}.")]]); + + [webView wmf_setTextDirection]; + [webView wmf_setTextFontSize]; + [webView wmf_setTextFontColor:self.theme]; + [webView wmf_setLogoStyleWithTheme:self.theme]; +} + +#pragma mark - Introspection + +- (BOOL)isDisplayingLicense { + if ([[[self.webView URL] scheme] isEqualToString:kWMFLicenseRedirectScheme] && + [[[self.webView URL] resourceSpecifier] isEqualToString:kWMFLicenseRedirectResourceIdentifier]) { + return YES; + } + + return NO; +} + +#pragma mark - WKWebViewDelegate + +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + WKNavigationType navigationType = navigationAction.navigationType; + NSURL *requestURL = navigationAction.request.URL; + + if ([[self class] isLicenseURL:requestURL]) { + + LibrariesUsedViewController *vc = [LibrariesUsedViewController wmf_viewControllerFromStoryboardNamed:LibrariesUsedViewController.storyboardName]; + [vc applyTheme:self.theme]; + vc.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:self.navigationItem.backBarButtonItem.style target:nil action:nil]; + + WMFThemeableNavigationController *nc = [[WMFThemeableNavigationController alloc] initWithRootViewController:vc theme:self.theme]; + [self presentViewController:nc animated:YES completion:nil]; + + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + + if (navigationType == WKNavigationTypeLinkActivated && + [[self class] isExternalURL:requestURL]) { + [self wmf_navigateToURL:requestURL]; + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + decisionHandler(WKNavigationActionPolicyAllow); +} + +- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation { + if (!([[self class] isLicenseURL:[webView URL]] || [[self class] isLicenseRedirectURL:[webView URL]])) { + [self injectAboutPageContentIntoWebView:webView]; + } else { + [webView wmf_preventTextFromExpandingOnRotation]; + } + [self updateNavigationBar]; +} + +#pragma mark - Utility Methods + ++ (NSString *)linkHTMLForURLString:(NSString *)url title:(NSString *)title { + return [NSString stringWithFormat:@"%@", url, title]; +} + ++ (NSString *)licenseURLPathForLibraryName:(NSString *)name { + return [NSString stringWithFormat:@"%@://%@", kWMFLicenseScheme, name]; +} + ++ (BOOL)isLicenseURL:(NSURL *)url { + if ([[url scheme] isEqualToString:kWMFLicenseScheme]) { + return YES; + } + + return NO; +} + ++ (BOOL)isLicenseRedirectURL:(NSURL *)url { + if ([[url scheme] isEqualToString:kWMFLicenseRedirectScheme]) { + return YES; + } + + return NO; +} + ++ (BOOL)isExternalURL:(NSURL *)url { + if ([[url scheme] isEqualToString:@"http"] || + [[url scheme] isEqualToString:@"https"] || + [[url scheme] isEqualToString:@"mailto"]) { + return YES; + } + + return NO; +} + +#pragma mark - WMFThemeable + +- (void)applyTheme:(WMFTheme *)theme { + [super applyTheme:theme]; + if (self.viewIfLoaded == nil) { + return; + } + self.view.backgroundColor = theme.colors.paperBackground; + [self.webView wmf_setTextFontColor:theme]; + [self.webView wmf_setLogoStyleWithTheme:theme]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/AboutViewController.plist b/Apps/Wikipedia/Wikipedia/Code/AboutViewController.plist new file mode 100644 index 0000000..0448ed4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AboutViewController.plist @@ -0,0 +1,133 @@ + + + + + urls + + sharealike + https://creativecommons.org/licenses/by-sa/3.0/ + mit + https://raw.githubusercontent.com/wikimedia/wikipedia-ios/main/LICENSE.txt + tsg + https://tsgteam.org + feedback + mailto:ios-support@wikimedia.org?subject=Feedback:$1 + twn + https://translatewiki.net + wmf + https://wikimediafoundation.org + applemaps + https://gspe21-ssl.ls.apple.com/html/attribution.html + + repositories + + GitHub + https://github.com/wikimedia/wikipedia-ios + Gerrit + https://gerrit.wikimedia.org/r/#/q/project:apps/ios/wikipedia,n,z + + contributors + + Abbey Ripstra + Adam Baso + Alexander Claussen + Amir E. Aharoni + Ammar Abdulhamid + Anirudh S + André Costa + Anthony Borba + Antoine Musso + Alexey Karataev + Barbara Martina Rodeker + bart-kneepkens + Bernd Sitzmann + Boris Dušek + brett ohland + Brian Gerstle + Brion Vibber + Carolyn Li-Madeo + Chelsy Xie + Cole Roberts + Corey Floyd + cromulentlabs + Daisy Chen + Dan Garry + Daniel McGlinchey + David Xia + Deepak Mantena + Dmitry Brant + Elena Tonkovidova + Ethan Holshouser + Gabicoware + Gianmarco Salerno + Hua Zhenyu + Hynek Hanke + Ian MacFarlane + Isidore Baldado + Jan Berkel + Joe Walsh + Jon Harald Søby + Jonatan Svensson Glad + Joshua Minor + Julien Bodet + Junho Lee + Kaity Hammerstein + Kelly Roach + Kenan Wang + Kotaro Fujita + Kunal Mehta + Logan Keller + Luiggi Minaya Salcedo + MagikCow + Maryana Pinchuk + Marina Azevedo + Matt Cleinman + Max Semenik + Maxim Korobov + Michael Holloway + Michal Ciurus + Mikhail Popov + Moiz Syed + Monte Hurd + Morgan Davison + Mun May Tee + Natalia Harateh + negati-ve + Nick DiStefano + Nicole Borrelli + Niklas Laxström + Nirzar Pangarkar + Oliver Keyes + Ori Livneh + Pedro Vereza + Prayag Verma + Rita Ho + Rummana Yasmeen + Salman Jamil + Sam Symons + Scott Petit + Sean Villalta + Seshadri Mahalingam + Sherah Smith + Siebrand Mazeland + Spencer Edgecombe + Stephen Niedzielski + Steve Peak + Steven Schobert + Sunil Sharma + Thomas PT + Tiago Martinho + Tim Johnsen + Timo Tijhof + Tomasz Finc + Toni Sevener + Vibha Bamba + Victor Barrera + Victor Peschenkov + waffleboot + Yongmin Hong + Yuvi Panda + Zoran Dori + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/AccountViewController.swift b/Apps/Wikipedia/Wikipedia/Code/AccountViewController.swift new file mode 100644 index 0000000..fb64f46 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AccountViewController.swift @@ -0,0 +1,217 @@ +import UIKit +import SwiftUI +import WMF + +@objc(WMFAccountViewControllerDelegate) +protocol AccountViewControllerDelegate: AnyObject { + func accountViewControllerDidTapLogout(_ accountViewController: AccountViewController) +} + +private enum ItemType { + case logout + case talkPage + case talkPageAutoSignDiscussions + case vanishAccount +} + +private struct Section { + let items: [Item] + let headerTitle: String? + let footerTitle: String? +} + +private struct Item { + let title: String + let subtitle: String? + let iconName: String? + let iconColor: UIColor? + let iconBackgroundColor: UIColor? + let type: ItemType +} + +@objc(WMFAccountViewController) +class AccountViewController: SubSettingsViewController { + + @objc var dataStore: MWKDataStore! + @objc weak var delegate: AccountViewControllerDelegate? + + private lazy var sections: [Section] = { + + guard let username = dataStore.authenticationManager.loggedInUsername else { + assertionFailure("Should not reach this screen if user isn't logged in.") + return [] + } + + let logout = Item(title: username, subtitle: CommonStrings.logoutTitle, iconName: "settings-user", iconColor: .white, iconBackgroundColor: UIColor.orange600, type: .logout) + let talkPage = Item(title: WMFLocalizedString("account-talk-page-title", value: "Your talk page", comment: "Title for button and page letting user view their account page."), subtitle: nil, iconName: "settings-talk-page", iconColor: .white, iconBackgroundColor: .blue600 , type: .talkPage) + let vanishAccount = Item(title: CommonStrings.vanishAccount, subtitle: nil, iconName: "vanish-account", iconColor: .white, iconBackgroundColor: .red, type: .vanishAccount) + let account = Section(items: [logout, talkPage, vanishAccount], headerTitle: WMFLocalizedString("account-group-title", value: "Your Account", comment: "Title for account group on account settings screen."), footerTitle: nil) + + let autoSignDiscussions = Item(title: WMFLocalizedString("account-talk-preferences-auto-sign-discussions", value: "Auto-sign discussions", comment: "Title for talk page preference that configures adding signature to new posts"), subtitle: nil, iconName: nil, iconColor: nil, iconBackgroundColor: nil, type: .talkPageAutoSignDiscussions) + let talkPagePreferences = Section(items: [autoSignDiscussions], headerTitle: WMFLocalizedString("account-talk-preferences-title", value: "Talk page preferences", comment: "Title for talk page preference sections in account settings"), footerTitle: WMFLocalizedString("account-talk-preferences-auto-sign-discussions-setting-explanation", value: "Auto-signing of discussions will use the signature defined in Signature settings", comment: "Text explaining how setting the auto-signing of talk page discussions preference works")) + + return [account, talkPagePreferences] + }() + + override func viewDidLoad() { + super.viewDidLoad() + title = CommonStrings.account + tableView.register(WMFSettingsTableViewCell.wmf_classNib(), forCellReuseIdentifier: WMFSettingsTableViewCell.identifier) + tableView.register(WMFTableHeaderFooterLabelView.wmf_classNib(), forHeaderFooterViewReuseIdentifier: WMFTableHeaderFooterLabelView.identifier) + tableView.sectionHeaderHeight = UITableView.automaticDimension + tableView.estimatedSectionHeaderHeight = 44 + tableView.sectionFooterHeight = UITableView.automaticDimension + tableView.estimatedSectionFooterHeight = 44 + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return sections.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return sections[safeIndex: section]?.items.count ?? 0 + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + guard let cell = tableView.dequeueReusableCell(withIdentifier: WMFSettingsTableViewCell.identifier, for: indexPath) as? WMFSettingsTableViewCell, + let item = sections[safeIndex: indexPath.section]?.items[safeIndex: indexPath.row] else { + return UITableViewCell() + } + + cell.iconName = item.iconName + cell.iconColor = item.iconColor + cell.iconBackgroundColor = item.iconBackgroundColor + cell.title = item.title + + switch item.type { + case .logout: + cell.disclosureType = .viewControllerWithDisclosureText + cell.disclosureText = item.type == .logout ? CommonStrings.logoutTitle : nil + cell.accessibilityTraits = .button + case .talkPage: + cell.disclosureType = .viewController + cell.disclosureText = nil + cell.accessibilityTraits = .button + case .talkPageAutoSignDiscussions: + cell.disclosureType = .switch + cell.selectionStyle = .none + cell.disclosureSwitch.isOn = UserDefaults.standard.autoSignTalkPageDiscussions + cell.disclosureSwitch.addTarget(self, action: #selector(autoSignTalkPageDiscussions(_:)), for: .valueChanged) + case .vanishAccount: + cell.disclosureType = .viewController + cell.accessibilityTraits = .button + } + + cell.apply(theme) + + return cell + } + + @objc private func autoSignTalkPageDiscussions(_ sender: UISwitch) { + UserDefaults.standard.autoSignTalkPageDiscussions = sender.isOn + } + + @objc func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + defer { + tableView.deselectRow(at: indexPath, animated: true) + } + guard let item = sections[safeIndex: indexPath.section]?.items[safeIndex: indexPath.row] else { + return + } + switch item.type { + case .logout: + showLogoutAlert() + case .talkPage: + guard let username = dataStore.authenticationManager.loggedInUsername, + let siteURL = dataStore.primarySiteURL else { + return + } + + let title = OldTalkPageType.user.titleWithCanonicalNamespacePrefix(title: username, siteURL: siteURL) + + if FeatureFlags.needsNewTalkPage { + if let viewModel = TalkPageViewModel(pageType: .user, pageTitle: title, siteURL: siteURL, source: .account, articleSummaryController: dataStore.articleSummaryController, authenticationManager: dataStore.authenticationManager, languageLinkController: dataStore.languageLinkController) { + let newTalkPage = TalkPageViewController(theme: theme, viewModel: viewModel) + self.navigationController?.pushViewController(newTalkPage, animated: true) + } + } else { + let title = OldTalkPageType.user.titleWithCanonicalNamespacePrefix(title: username, siteURL: siteURL) + let loadingFlowController = TalkPageContainerViewController.talkPageContainer(title: title, siteURL: siteURL, type: .user, dataStore: dataStore, theme: theme) + self.navigationController?.pushViewController(loadingFlowController, animated: true) + } + + case .vanishAccount: + let warningViewController = VanishAccountWarningViewHostingViewController(theme: theme) + warningViewController.delegate = self + present(warningViewController, animated: true) + default: + break + } + } + + @objc func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return UITableView.automaticDimension + } + + @objc func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + + let text = sections[safeIndex: section]?.headerTitle + return WMFTableHeaderFooterLabelView.headerFooterViewForTableView(tableView, text: text, type: .header, theme: theme) + } + + @objc func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + + guard let text = sections[safeIndex: section]?.footerTitle, + !text.isEmpty else { + return 0 + } + + return UITableView.automaticDimension + } + + @objc func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + + let text = sections[safeIndex: section]?.footerTitle + return WMFTableHeaderFooterLabelView.headerFooterViewForTableView(tableView, text: text, type: .footer, theme: theme) + } + + private func showLogoutAlert() { + let alertController = UIAlertController(title: WMFLocalizedString("main-menu-account-logout-are-you-sure", value: "Are you sure you want to log out?", comment: "Header asking if user is sure they wish to log out."), message: WMFLocalizedString("main-menu-account-logout-are-you-sure-message", value: "Logging out will delete your locally stored account data (notifications and messages), but your account data will still be available on the web and will be re-downloaded if you log back in.", comment: "Message explaining what happens to local data when logging out."), preferredStyle: .alert) + let logoutAction = UIAlertAction(title: CommonStrings.logoutTitle, style: .destructive) { [weak self] (action) in + guard let self = self else { + return + } + self.delegate?.accountViewControllerDidTapLogout(self) + self.navigationController?.popViewController(animated: true) + } + let cancelAction = UIAlertAction(title: WMFLocalizedString("main-menu-account-logout-cancel", value: "Cancel", comment: "Button text for hiding the log out menu. {{Identical|Cancel}}"), style: .cancel, handler: nil) + alertController.addAction(logoutAction) + alertController.addAction(cancelAction) + present(alertController, animated: true, completion: nil) + } + + override func apply(theme: Theme) { + super.apply(theme: theme) + + guard viewIfLoaded != nil else { + return + } + + view.backgroundColor = theme.colors.paperBackground + tableView.backgroundColor = theme.colors.baseBackground + } +} + +extension AccountViewController: VanishAccountWarningViewDelegate { + + func userDidDismissVanishAccountWarningView(presentVanishView: Bool) { + guard presentVanishView, let username = dataStore.authenticationManager.loggedInUsername else { + return + } + + let viewController = VanishAccountContainerViewController(title: CommonStrings.vanishAccount.localizedCapitalized, theme: theme, username: username) + navigationController?.pushViewController(viewController, animated: true) + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ActionHandlerScript.swift b/Apps/Wikipedia/Wikipedia/Code/ActionHandlerScript.swift new file mode 100644 index 0000000..4f5cbfd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ActionHandlerScript.swift @@ -0,0 +1,179 @@ +import WebKit + +/// Handles interaction with the Page Content Service JavaScript interface +/// Passes setup parameters to the webpage (theme, margins, etc) and sets up a listener to recieve events (link tapped, image tapped, etc) through the messaging bridge +/// https://www.mediawiki.org/wiki/Page_Content_Service +final class PageContentService { + struct Setup { + struct Parameters: Codable { + var platform = "ios" + var version = 1 + + var theme: String + var dimImages: Bool + + struct Margins: Codable { + // these values are strings to allow for units to be included + let top: String + let right: String + let bottom: String + let left: String + } + var margins: Margins + var leadImageHeight: String // units are included + + var areTablesInitiallyExpanded: Bool + var textSizeAdjustmentPercentage: String // string like '125%' + + var userGroups: [String] + } + } + + struct Footer { + struct Menu: Codable { + static let fragment = "pcs-footer-container-menu" + enum Item: String, Codable { + case lastEdited + case pageIssues + case disambiguation + case coordinate + case talkPage + } + let items: [Item] + let editedDaysAgo: Int? + } + + struct ReadMore: Codable { + static let fragment = "pcs-footer-container-readmore" + let itemCount: Int + let baseURL: String + } + + struct Parameters: Codable { + let title: String + let menu: Menu + let readMore: ReadMore + } + } + + static let paramsEncoder = JSONEncoder() + static let messageHandlerName = "pcs" + + /// - Parameter encodable: the object to encode + /// - Returns: a JavaScript string that will call JSON.parse on the JSON representation of the encodable + class func getJavascriptFor(_ encodable: T) throws -> String where T: Encodable { + let data = try PageContentService.paramsEncoder.encode(encodable) + guard let string = String(data: data, encoding: .utf8) else { + throw RequestError.invalidParameters + } + return "JSON.parse(`\(string.sanitizedForJavaScriptTemplateLiterals)`)" + } + + final class SetupScript: PageUserScript { + required init(_ parameters: Setup.Parameters) throws { + let source = """ + document.pcsActionHandler = (action) => { + window.webkit.messageHandlers.\(PageContentService.messageHandlerName).postMessage(action) + }; + document.pcsSetupSettings = \(try PageContentService.getJavascriptFor(parameters)); + """ + super.init(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: true) + } + } + + final class PropertiesScript: PageUserScript { + static let source: String = { + guard + let fileURL = Bundle.main.url(forResource: "Properties", withExtension: "js"), + let data = try? Data(contentsOf: fileURL), + let jsString = String(data: data, encoding: .utf8)?.replacingOccurrences(of: "{{messageHandlerName}}", with: PageContentService.messageHandlerName) + else { + return "" + } + return jsString + }() + init() { + super.init(source: PropertiesScript.source, injectionTime: .atDocumentEnd, forMainFrameOnly: true) + } + } + + final class UtilitiesScript: PageUserScript { + static let source: String = { + guard + let fileURL = Bundle.wmf.url(forResource: "index", withExtension: "js", subdirectory: "assets"), + let data = try? Data(contentsOf: fileURL), + let jsString = String(data: data, encoding: .utf8) + else { + return "" + } + return jsString + }() + + init() { + super.init(source: UtilitiesScript.source, injectionTime: .atDocumentEnd, forMainFrameOnly: true) + } + } + + + final class StyleScript: PageUserScript { + static let source: String = { + guard + let fileURL = Bundle.wmf.url(forResource: "styleoverrides", withExtension: "css", subdirectory: "assets"), + let data = try? Data(contentsOf: fileURL), + let cssString = String(data: data, encoding: .utf8)?.sanitizedForJavaScriptTemplateLiterals + else { + return "" + } + return "const style = document.createElement('style'); style.innerHTML = `\(cssString)`; document.head.appendChild(style);" + }() + + init() { + super.init(source: StyleScript.source, injectionTime: .atDocumentEnd, forMainFrameOnly: true) + } + } + + final class SignificantEventsStyleScript: PageUserScript { + + static func sourceForTheme(_ theme: String) -> String { + + let cssFileName: String + switch theme { + case "sepia": cssFileName = "significant-events-styles-sepia" + case "dark": cssFileName = "significant-events-styles-dark" + case "black": cssFileName = "significant-events-styles-black" + default: cssFileName = "significant-events-styles-light" + } + + guard + let originalFileURL = Bundle.wmf.url(forResource: "styleoverrides", withExtension: "css", subdirectory: "assets"), + let originalData = try? Data(contentsOf: originalFileURL), + let originalCssString = String(data: originalData, encoding: .utf8)?.sanitizedForJavaScriptTemplateLiterals, + let baseFileURL = Bundle.wmf.url(forResource: "significant-events-styles-base", withExtension: "css", subdirectory: "assets"), + let baseData = try? Data(contentsOf: baseFileURL), + let baseCssString = String(data: baseData, encoding: .utf8)?.sanitizedForJavaScriptTemplateLiterals, + let fileURL = Bundle.wmf.url(forResource: cssFileName, withExtension: "css", subdirectory: "assets"), + let data = try? Data(contentsOf: fileURL), + let cssString = String(data: data, encoding: .utf8)?.sanitizedForJavaScriptTemplateLiterals + else { + return "" + } + return """ + var existing = document.getElementById('significant-events-styles'); + if (existing) { + existing.remove(); + } + var style = document.createElement('style'); + style.id = 'significant-events-styles'; + style.innerHTML = `\(originalCssString + baseCssString + cssString)`; + document.head.appendChild(style); + """ + } + + init(theme: String) { + + let calculatedSource = SignificantEventsStyleScript.sourceForTheme(theme) + + super.init(source: calculatedSource, injectionTime: .atDocumentEnd, forMainFrameOnly: true) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/AddArticlesToReadingListViewController.swift b/Apps/Wikipedia/Wikipedia/Code/AddArticlesToReadingListViewController.swift new file mode 100644 index 0000000..5e33a67 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AddArticlesToReadingListViewController.swift @@ -0,0 +1,128 @@ +import UIKit +import CocoaLumberjackSwift + +protocol AddArticlesToReadingListDelegate: NSObjectProtocol { + func addArticlesToReadingListWillClose(_ addArticlesToReadingList: AddArticlesToReadingListViewController) + func addArticlesToReadingListDidDisappear(_ addArticlesToReadingList: AddArticlesToReadingListViewController) + func addArticlesToReadingList(_ addArticlesToReadingList: AddArticlesToReadingListViewController, didAddArticles articles: [WMFArticle], to readingList: ReadingList) +} + +extension AddArticlesToReadingListDelegate where Self: EditableCollection { + func addArticlesToReadingListWillClose(_ addArticlesToReadingList: AddArticlesToReadingListViewController) { + editController.close() + } + + func addArticlesToReadingList(_ addArticlesToReadingList: AddArticlesToReadingListViewController, didAddArticles articles: [WMFArticle], to readingList: ReadingList) { + editController.close() + } +} + +extension AddArticlesToReadingListDelegate { + func addArticlesToReadingListDidDisappear(_ addArticlesToReadingList: AddArticlesToReadingListViewController) { + + } +} + +@objc(WMFAddArticlesToReadingListViewController) +class AddArticlesToReadingListViewController: ViewController { + + private let dataStore: MWKDataStore + private let articles: [WMFArticle] + public let moveFromReadingList: ReadingList? + + private let readingListsViewController: ReadingListsViewController + public weak var delegate: AddArticlesToReadingListDelegate? + + @objc var eventLogAction: (() -> Void)? + + @objc public init(with dataStore: MWKDataStore, articles: [WMFArticle], moveFromReadingList: ReadingList? = nil, theme: Theme) { + self.dataStore = dataStore + self.articles = articles + self.moveFromReadingList = moveFromReadingList + self.readingListsViewController = ReadingListsViewController(with: dataStore, articles: articles) + super.init() + self.theme = theme + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func closeButtonPressed() { + dismiss(animated: true) + delegate?.addArticlesToReadingListWillClose(self) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + delegate?.addArticlesToReadingListDidDisappear(self) + } + + @objc private func createNewReadingListButtonPressed() { + readingListsViewController.createReadingList(with: articles, moveFromReadingList: moveFromReadingList) + } + + override func accessibilityPerformEscape() -> Bool { + closeButtonPressed() + return true + } + + private var isCreateNewReadingListButtonViewHidden: Bool = false { + didSet { + if isCreateNewReadingListButtonViewHidden { + navigationBar.removeUnderNavigationBarView() + readingListsViewController.createNewReadingListButtonView.button.removeTarget(self, action: #selector(createNewReadingListButtonPressed), for: .touchUpInside) + } else { + readingListsViewController.createNewReadingListButtonView.button.addTarget(self, action: #selector(createNewReadingListButtonPressed), for: .touchUpInside) + navigationBar.addUnderNavigationBarView(readingListsViewController.createNewReadingListButtonView) + } + } + } + + override func viewDidLoad() { + super.viewDidLoad() + navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "close"), style: .plain, target: self, action: #selector(closeButtonPressed)) + let title = moveFromReadingList != nil ? WMFLocalizedString("move-articles-to-reading-list", value:"Move {{PLURAL:%1$d|%1$d article|%1$d articles}} to reading list", comment:"Title for the view in charge of moving articles to a reading list - %1$@ is replaced with the number of articles to move") : WMFLocalizedString("add-articles-to-reading-list", value:"Add {{PLURAL:%1$d|%1$d article|%1$d articles}} to reading list", comment:"Title for the view in charge of adding articles to a reading list - %1$@ is replaced with the number of articles to add") + navigationItem.title = String.localizedStringWithFormat(title, articles.count) + navigationBar.displayType = .modal + navigationBar.isBarHidingEnabled = false + navigationBar.isUnderBarViewHidingEnabled = true + isCreateNewReadingListButtonViewHidden = readingListsViewController.isEmpty + addChild(readingListsViewController) + view.wmf_addSubviewWithConstraintsToEdges(readingListsViewController.view) + readingListsViewController.didMove(toParent: self) + readingListsViewController.delegate = self + scrollView = readingListsViewController.scrollView + apply(theme: theme) + } + + // MARK: Themeable + + override func apply(theme: Theme) { + super.apply(theme: theme) + readingListsViewController.apply(theme: theme) + } +} + +// MARK: ReadingListsViewControllerDelegate + +extension AddArticlesToReadingListViewController: ReadingListsViewControllerDelegate { + func readingListsViewController(_ readingListsViewController: ReadingListsViewController, didAddArticles articles: [WMFArticle], to readingList: ReadingList) { + if let moveFromReadingList = moveFromReadingList { + do { + try dataStore.readingListsController.remove(articles: articles, readingList: moveFromReadingList) + } catch let error { + DDLogError("Error removing articles after move: \(error)") + } + } + delegate?.addArticlesToReadingList(self, didAddArticles: articles, to: readingList) + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(200)) { + self.dismiss(animated: true) + } + eventLogAction?() + } + + func readingListsViewControllerDidChangeEmptyState(_ readingListsViewController: ReadingListsViewController, isEmpty: Bool) { + isCreateNewReadingListButtonViewHidden = isEmpty + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaAdvancedSettingsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaAdvancedSettingsViewController.swift new file mode 100644 index 0000000..19752f3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaAdvancedSettingsViewController.swift @@ -0,0 +1,157 @@ +import UIKit + +final class InsertMediaAdvancedSettingsViewController: ViewController { + static let title = WMFLocalizedString("advanced-settings-title", value: "Advanced settings", comment: "Title for advanced settings screen") + private let tableView = UITableView() + + typealias AdvancedSettings = InsertMediaSettings.Advanced + + var advancedSettings: AdvancedSettings { + return AdvancedSettings(wrapTextAroundImage: textWrappingSwitch.isOn, imagePosition: imagePositionSettingsViewController.selectedImagePosition(isTextWrappingEnabled: textWrappingSwitch.isOn), imageType: imageTypeSettingsViewController.selectedImageType, imageSize: imageSizeSettingsViewController.selectedImageSize) + } + + struct ViewModel { + let title: String + let detailText: String? + let accessoryView: UIView? + let accessoryType: UITableViewCell.AccessoryType + let isEnabled: Bool + let selectionStyle: UITableViewCell.SelectionStyle + let onSelection: (() -> Void)? + + init(title: String, detailText: String? = nil, accessoryView: UIView? = nil, accessoryType: UITableViewCell.AccessoryType = .disclosureIndicator, isEnabled: Bool = true, selectionStyle: UITableViewCell.SelectionStyle = .default, onSelection: (() -> Void)? = nil) { + self.title = title + self.detailText = detailText + self.accessoryView = accessoryView + self.accessoryType = accessoryType + self.isEnabled = isEnabled + self.selectionStyle = selectionStyle + self.onSelection = onSelection + } + } + + private lazy var textWrappingSwitch: UISwitch = { + let textWrappingSwitch = UISwitch() + textWrappingSwitch.isOn = true + textWrappingSwitch.addTarget(self, action: #selector(toggleImagePositionEnabledState(_:)), for: .valueChanged) + return textWrappingSwitch + }() + + private lazy var imagePositionSettingsViewController = InsertMediaImagePositionSettingsViewController() + private lazy var imageTypeSettingsViewController = InsertMediaImageTypeSettingsViewController() + private lazy var imageSizeSettingsViewController = InsertMediaImageSizeSettingsViewController() + + private var viewModels: [ViewModel] { + let textWrappingViewModel = ViewModel(title: WMFLocalizedString("insert-media-image-text-wrapping-setting", value: "Wrap text around image", comment: "Title for image setting that wraps text around image"), accessoryView: textWrappingSwitch, accessoryType: .none, selectionStyle: .none) + let imagePositionViewModel = ViewModel(title: AdvancedSettings.ImagePosition.displayTitle, detailText: imagePositionSettingsViewController.selectedImagePosition(isTextWrappingEnabled: textWrappingSwitch.isOn).displayTitle, isEnabled: textWrappingSwitch.isOn) { [weak self] in + guard let self = self else { + return + } + self.push(self.imagePositionSettingsViewController) + } + let imageTypeViewModel = ViewModel(title: AdvancedSettings.ImageType.displayTitle, detailText: imageTypeSettingsViewController.selectedImageType.displayTitle) { [weak self] in + guard let self = self else { + return + } + self.push(self.imageTypeSettingsViewController) + } + let imageSizeViewModel = ViewModel(title: AdvancedSettings.ImageSize.displayTitle, detailText: imageSizeSettingsViewController.selectedImageSize.displayTitle) { [weak self] in + guard let self = self else { + return + } + self.push(self.imageSizeSettingsViewController) + } + return [textWrappingViewModel, imagePositionViewModel, imageTypeViewModel, imageSizeViewModel] + } + + private func push(_ viewController: UIViewController & Themeable) { + viewController.apply(theme: theme) + navigationController?.pushViewController(viewController, animated: true) + } + + @objc private func toggleImagePositionEnabledState(_ sender: UISwitch) { + tableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .automatic) + } + + override func viewDidLoad() { + scrollView = tableView + super.viewDidLoad() + navigationBar.isBarHidingEnabled = false + tableView.dataSource = self + tableView.delegate = self + view.wmf_addSubviewWithConstraintsToEdges(tableView) + tableView.separatorInset = .zero + tableView.tableFooterView = UIView() + title = InsertMediaAdvancedSettingsViewController.title + apply(theme: theme) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + defer { + isFirstAppearance = false + } + guard !isFirstAppearance else { + return + } + tableView.reloadData() + } + + // MARK: - Themeable + + override func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.paperBackground + tableView.backgroundColor = view.backgroundColor + tableView.separatorColor = theme.colors.border + tableView.reloadData() + } +} + +// MARK: - Table view data source + +extension InsertMediaAdvancedSettingsViewController: UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewModels.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.identifier) ?? UITableViewCell(style: .value1, reuseIdentifier: UITableViewCell.identifier) + let viewModel = viewModels[indexPath.row] + cell.textLabel?.text = viewModel.title + cell.accessoryView = viewModel.accessoryView + cell.accessoryType = viewModel.accessoryType + cell.isUserInteractionEnabled = viewModel.isEnabled + cell.detailTextLabel?.textAlignment = .right + cell.detailTextLabel?.text = viewModel.detailText + cell.selectionStyle = cell.isUserInteractionEnabled ? viewModel.selectionStyle : .none + apply(theme: theme, to: cell) + return cell + } + + private func apply(theme: Theme, to cell: UITableViewCell) { + cell.backgroundColor = theme.colors.paperBackground + cell.contentView.backgroundColor = theme.colors.paperBackground + let selectedBackgroundView = UIView() + selectedBackgroundView.backgroundColor = theme.colors.midBackground + cell.selectedBackgroundView = selectedBackgroundView + cell.textLabel?.textColor = cell.isUserInteractionEnabled ? theme.colors.primaryText : theme.colors.secondaryText + cell.detailTextLabel?.textColor = theme.colors.secondaryText + } +} + +// MARK: - Table view delegate + +extension InsertMediaAdvancedSettingsViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let viewModel = viewModels[indexPath.row] + viewModel.onSelection?() + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaCustomImageSizeSettingTableViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaCustomImageSizeSettingTableViewCell.swift new file mode 100644 index 0000000..c264d41 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaCustomImageSizeSettingTableViewCell.swift @@ -0,0 +1,51 @@ +import UIKit + +class InsertMediaCustomImageSizeSettingTableViewCell: UITableViewCell { + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var textFieldLabel: UILabel! + @IBOutlet weak var textField: ThemeableTextField! + + private var theme = Theme.standard + + func configure(title: String, textFieldLabelText: String, textFieldText: String, theme: Theme) { + titleLabel.text = title + textFieldLabel.text = textFieldLabelText + textField.text = textFieldText + textField.isUnderlined = false + textField.rightView = nil + updateFonts() + apply(theme: theme) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + titleLabel.font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + textFieldLabel.font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + textField.font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + } + + override var isUserInteractionEnabled: Bool { + didSet { + textField.isUserInteractionEnabled = isUserInteractionEnabled + apply(theme: theme) + } + } +} + +extension InsertMediaCustomImageSizeSettingTableViewCell: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + let textColor = isUserInteractionEnabled ? theme.colors.primaryText : theme.colors.secondaryText + titleLabel.textColor = textColor + textFieldLabel.textColor = textColor + let selectedBackgroundView = UIView() + selectedBackgroundView.backgroundColor = theme.colors.midBackground + self.selectedBackgroundView = selectedBackgroundView + textField.apply(theme: theme) + textField.textColor = textColor + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaCustomImageSizeSettingTableViewCell.xib b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaCustomImageSizeSettingTableViewCell.xib new file mode 100644 index 0000000..6310dc0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaCustomImageSizeSettingTableViewCell.xib @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaImagePositionSettingsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaImagePositionSettingsViewController.swift new file mode 100644 index 0000000..48faa73 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaImagePositionSettingsViewController.swift @@ -0,0 +1,115 @@ +final class InsertMediaImagePositionSettingsViewController: ViewController { + private let tableView = UITableView() + private var selectedIndexPath: IndexPath? + + typealias ImagePosition = InsertMediaSettings.Advanced.ImagePosition + + func selectedImagePosition(isTextWrappingEnabled: Bool) -> ImagePosition { + guard isTextWrappingEnabled else { + return .none + } + guard let selectedIndexPath = selectedIndexPath else { + return .right + } + return viewModels[selectedIndexPath.row].imagePosition + } + + struct ViewModel { + let imagePosition: ImagePosition + let title: String + let isSelected: Bool + + init(imagePosition: ImagePosition, isSelected: Bool = false) { + self.imagePosition = imagePosition + self.title = imagePosition.displayTitle + self.isSelected = isSelected + } + } + + private lazy var viewModels: [ViewModel] = { + let rightViewModel = ViewModel(imagePosition: .right, isSelected: true) + let leftViewModel = ViewModel(imagePosition: .left) + let centerViewModel = ViewModel(imagePosition: .center) + return [rightViewModel, leftViewModel, centerViewModel] + }() + + override func viewDidLoad() { + scrollView = tableView + super.viewDidLoad() + navigationBar.isBarHidingEnabled = false + tableView.dataSource = self + tableView.delegate = self + view.wmf_addSubviewWithConstraintsToEdges(tableView) + tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.identifier) + tableView.separatorInset = .zero + tableView.tableFooterView = UIView() + title = ImagePosition.displayTitle + apply(theme: theme) + } + + private func apply(theme: Theme, to cell: UITableViewCell) { + cell.backgroundColor = theme.colors.paperBackground + cell.contentView.backgroundColor = theme.colors.paperBackground + cell.textLabel?.textColor = theme.colors.primaryText + let selectedBackgroundView = UIView() + selectedBackgroundView.backgroundColor = theme.colors.midBackground + cell.selectedBackgroundView = selectedBackgroundView + } + + // MARK: - Themeable + + override func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.paperBackground + tableView.backgroundColor = view.backgroundColor + tableView.separatorColor = theme.colors.border + tableView.reloadData() + } +} + +extension InsertMediaImagePositionSettingsViewController: UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewModels.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.identifier, for: indexPath) + let viewModel = viewModels[indexPath.row] + cell.textLabel?.text = viewModel.title + cell.accessoryType = viewModel.isSelected ? .checkmark : .none + if viewModel.isSelected { + cell.accessoryType = .checkmark + selectedIndexPath = indexPath + } else { + cell.accessoryType = .none + } + apply(theme: theme, to: cell) + return cell + } +} + +extension InsertMediaImagePositionSettingsViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? { + guard + let selectedIndexPath = selectedIndexPath, + let selectedCell = tableView.cellForRow(at: selectedIndexPath) + else { + return indexPath + } + selectedCell.accessoryType = .none + return indexPath + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let selectedCell = tableView.cellForRow(at: indexPath) + selectedCell?.accessoryType = .checkmark + selectedIndexPath = indexPath + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaImageSizeSettingsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaImageSizeSettingsViewController.swift new file mode 100644 index 0000000..2ca4462 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaImageSizeSettingsViewController.swift @@ -0,0 +1,145 @@ +fileprivate protocol ViewModel { + var title: String { get } +} + +final class InsertMediaImageSizeSettingsViewController: ViewController { + private let tableView = UITableView() + + typealias ImageSize = InsertMediaSettings.Advanced.ImageSize + + var selectedImageSize: ImageSize { + guard + customSwitch.isOn, + let widthString = textFieldsGroupedByMeasure[.width]?.text, + let width = Int(widthString), + let heightString = textFieldsGroupedByMeasure[.height]?.text, + let height = Int(heightString) + else { + return .default + } + return .custom(width: width, height: height) + } + + private var textFieldsGroupedByMeasure = [Measure: UITextField]() + + private struct ImageSizeViewModel: ViewModel { + let title: String + let accessoryView: UIView + } + + private enum Measure: Hashable { + case width, height + + var displayTitle: String { + switch self { + case .width: + return WMFLocalizedString("insert-media-image-size-settings-measure-width", value: "Width", comment: "Display title for the measurement of image from side to side") + case .height: + return WMFLocalizedString("insert-media-image-size-settings-measure-height", value: "Height", comment: "Display title for the measurement of image from top to base") + } + } + } + + private struct MeasureViewModel: ViewModel { + let measure: Measure + let title: String + let defaultValue: String + let unitName: String + + init(measure: Measure, defaultValue: String, unitName: String) { + self.measure = measure + self.title = measure.displayTitle + self.defaultValue = defaultValue + self.unitName = unitName + } + } + + private lazy var customSwitch: UISwitch = { + let customSwitch = UISwitch() + customSwitch.addTarget(self, action: #selector(reloadData), for: .valueChanged) + return customSwitch + }() + + @objc private func reloadData() { + tableView.reloadData() + } + + private lazy var viewModels: [ViewModel] = { + let customImageSize = ImageSize.custom(width: ImageSize.defaultWidth, height: ImageSize.defaultHeight) + let customViewModel = ImageSizeViewModel(title: customImageSize.displayTitle, accessoryView: customSwitch) + let widthViewModel = MeasureViewModel(measure: .width, defaultValue: "\(ImageSize.defaultWidth)", unitName: ImageSize.unitName) + let heightViewModel = MeasureViewModel(measure: .height, defaultValue: "\(ImageSize.defaultHeight)", unitName: ImageSize.unitName) + return [customViewModel, widthViewModel, heightViewModel] + }() + + override func viewDidLoad() { + scrollView = tableView + super.viewDidLoad() + navigationBar.isBarHidingEnabled = false + tableView.dataSource = self + view.wmf_addSubviewWithConstraintsToEdges(tableView) + tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.identifier) + tableView.register(InsertMediaCustomImageSizeSettingTableViewCell.wmf_classNib(), forCellReuseIdentifier: InsertMediaCustomImageSizeSettingTableViewCell.identifier) + tableView.separatorInset = .zero + tableView.tableFooterView = UIView() + title = ImageSize.displayTitle + apply(theme: theme) + } + + private func apply(theme: Theme, to cell: UITableViewCell) { + cell.backgroundColor = theme.colors.paperBackground + cell.contentView.backgroundColor = theme.colors.paperBackground + cell.textLabel?.textColor = theme.colors.primaryText + let selectedBackgroundView = UIView() + selectedBackgroundView.backgroundColor = theme.colors.midBackground + cell.selectedBackgroundView = selectedBackgroundView + } + + // MARK: - Themeable + + override func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.paperBackground + tableView.backgroundColor = view.backgroundColor + tableView.separatorColor = theme.colors.border + tableView.reloadData() + } +} + +extension InsertMediaImageSizeSettingsViewController: UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewModels.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let viewModel = viewModels[indexPath.row] + switch viewModel { + case let imageSizeViewModel as ImageSizeViewModel: + let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.identifier, for: indexPath) + cell.textLabel?.text = imageSizeViewModel.title + cell.accessoryView = imageSizeViewModel.accessoryView + cell.selectionStyle = .none + apply(theme: theme, to: cell) + return cell + case let measureViewModel as MeasureViewModel: + guard let cell = tableView.dequeueReusableCell(withIdentifier: InsertMediaCustomImageSizeSettingTableViewCell.identifier, for: indexPath) as? InsertMediaCustomImageSizeSettingTableViewCell else { + return UITableViewCell() + } + cell.configure(title: measureViewModel.title, textFieldLabelText: measureViewModel.unitName, textFieldText: measureViewModel.defaultValue, theme: theme) + cell.selectionStyle = .none + cell.isUserInteractionEnabled = customSwitch.isOn + textFieldsGroupedByMeasure[measureViewModel.measure] = cell.textField + cell.apply(theme: theme) + return cell + default: + return UITableViewCell() + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaImageTypeSettingsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaImageTypeSettingsViewController.swift new file mode 100644 index 0000000..4f59616 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaImageTypeSettingsViewController.swift @@ -0,0 +1,133 @@ +final class InsertMediaImageTypeSettingsViewController: ViewController { + private let tableView = UITableView() + private var selectedIndexPath: IndexPath? + + typealias ImageType = InsertMediaSettings.Advanced.ImageType + + var selectedImageType: ImageType { + guard let selectedIndexPath = selectedIndexPath else { + return .thumbnail + } + return viewModels[selectedIndexPath.row].imageType + } + + struct ViewModel { + let imageType: ImageType + let title: String + let isSelected: Bool + + init(imageType: ImageType, isSelected: Bool = false) { + self.imageType = imageType + self.title = imageType.displayTitle + self.isSelected = isSelected + } + } + + private lazy var viewModels: [ViewModel] = { + let thumbnailViewModel = ViewModel(imageType: .thumbnail, isSelected: true) + let framelessViewModel = ViewModel(imageType: .frameless) + let frameViewModel = ViewModel(imageType: .frame) + let basicViewModel = ViewModel(imageType: .basic) + return [thumbnailViewModel, framelessViewModel, frameViewModel, basicViewModel] + }() + + override func viewDidLoad() { + scrollView = tableView + super.viewDidLoad() + navigationBar.isBarHidingEnabled = false + tableView.dataSource = self + tableView.delegate = self + view.wmf_addSubviewWithConstraintsToEdges(tableView) + tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.identifier) + tableView.separatorInset = .zero + autolayoutTableViewFooter = InsertMediaLabelTableFooterView(text: WMFLocalizedString("insert-media-image-type-settings-footer-title", value: "You can set how the media item appears on the page. This should be the thumbnail format to be consistent with other pages in almost all cases.", comment: "Footer for ")) + title = ImageType.displayTitle + apply(theme: theme) + } + + private var autolayoutTableViewFooter: UIView? { + get { + return tableView.tableFooterView + } + set { + tableView.tableFooterView = newValue + guard let footer = newValue else { return } + footer.setNeedsLayout() + footer.layoutIfNeeded() + footer.frame.size = + footer.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) + tableView.tableFooterView = footer + } + } + + private func apply(theme: Theme, to cell: UITableViewCell) { + cell.backgroundColor = theme.colors.paperBackground + cell.contentView.backgroundColor = theme.colors.paperBackground + cell.textLabel?.textColor = theme.colors.primaryText + let selectedBackgroundView = UIView() + selectedBackgroundView.backgroundColor = theme.colors.midBackground + cell.selectedBackgroundView = selectedBackgroundView + } + + // MARK: - Themeable + + override func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.paperBackground + tableView.backgroundColor = view.backgroundColor + tableView.separatorColor = theme.colors.border + (autolayoutTableViewFooter as? Themeable)?.apply(theme: theme) + tableView.reloadData() + } +} + +// MARK: - UITableViewDataSource + +extension InsertMediaImageTypeSettingsViewController: UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewModels.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.identifier, for: indexPath) + let viewModel = viewModels[indexPath.row] + cell.textLabel?.text = viewModel.title + if viewModel.isSelected { + cell.accessoryType = .checkmark + selectedIndexPath = indexPath + } else { + cell.accessoryType = .none + } + apply(theme: theme, to: cell) + return cell + } +} + +// MARK: - UITableViewDelegate + +extension InsertMediaImageTypeSettingsViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? { + guard + let selectedIndexPath = selectedIndexPath, + let selectedCell = tableView.cellForRow(at: selectedIndexPath) + else { + return indexPath + } + selectedCell.accessoryType = .none + return indexPath + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let selectedCell = tableView.cellForRow(at: indexPath) + selectedCell?.accessoryType = .checkmark + selectedIndexPath = indexPath + } +} + diff --git a/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaLabelTableFooterView.swift b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaLabelTableFooterView.swift new file mode 100644 index 0000000..dfe2aee --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Advanced Settings/InsertMediaLabelTableFooterView.swift @@ -0,0 +1,53 @@ +final class InsertMediaLabelTableFooterView: SetupView, Themeable { + private let label = UILabel() + private let separator = UIView() + + init(text: String) { + label.text = text + super.init(frame: .zero) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func setup() { + super.setup() + separator.translatesAutoresizingMaskIntoConstraints = false + addSubview(separator) + let separatorLeadingConstraint = separator.leadingAnchor.constraint(equalTo: leadingAnchor) + let separatorTrailingConstraint = separator.trailingAnchor.constraint(equalTo: trailingAnchor) + let separatorTopConstraint = separator.topAnchor.constraint(equalTo: topAnchor) + let separatorHeightConstraint = separator.heightAnchor.constraint(equalToConstant: 0.5) + label.numberOfLines = 0 + label.translatesAutoresizingMaskIntoConstraints = false + addSubview(label) + let labelLeadingConstraint = label.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 15) + let labelTrailingConstraint = label.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -15) + let labelBottomConstraint = label.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: 15) + let labelTopConstraint = label.topAnchor.constraint(equalTo: topAnchor, constant: 5) + NSLayoutConstraint.activate([separatorLeadingConstraint, separatorTrailingConstraint, separatorTopConstraint, separatorHeightConstraint, labelLeadingConstraint, labelTrailingConstraint, labelBottomConstraint, labelTopConstraint]) + updateFonts() + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + label.font = UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + } + + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + label.backgroundColor = backgroundColor + label.textColor = theme.colors.secondaryText + separator.backgroundColor = theme.colors.border + } + + override func layoutSubviews() { + super.layoutSubviews() + label.preferredMaxLayoutWidth = label.bounds.width + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/AlignedImageButton.swift b/Apps/Wikipedia/Wikipedia/Code/AlignedImageButton.swift new file mode 100644 index 0000000..c6f1f26 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AlignedImageButton.swift @@ -0,0 +1,102 @@ +import UIKit + +@objc(WMFAlignedImageButton) +public class AlignedImageButton: UIButton { + private var isFirstLayout = true + + public override func layoutSubviews() { + super.layoutSubviews() + if isFirstLayout { + updateSemanticContentAttribute() + adjustInsets() + isFirstLayout = false + } + } + + /// Spacing between the image and title + @IBInspectable open var horizontalSpacing: CGFloat = 8 { + didSet { + adjustInsets() + } + } + + /// Padding added to the top and bottom of the button + @IBInspectable open var verticalPadding: CGFloat = 0 { + didSet { + adjustInsets() + } + } + + @IBInspectable open var leftPadding: CGFloat = 0 { + didSet { + adjustInsets() + } + } + + @IBInspectable open var rightPadding: CGFloat = 0 { + didSet { + adjustInsets() + } + } + + @IBInspectable open var imageIsRightAligned: Bool = false { + didSet { + updateSemanticContentAttribute() + adjustInsets() + } + } + + public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + let newLayoutDirection: UIUserInterfaceLayoutDirection = traitCollection.layoutDirection == .rightToLeft ? .rightToLeft : .leftToRight + guard newLayoutDirection != layoutDirection else { + return + } + updateSemanticContentAttribute() + adjustInsets() + } + + public override func wmf_sizeThatFits(_ maximumSize: CGSize) -> CGSize { + // When iOS's Bold Text setting was turned on, labels on Explore feed buttons were moving to two lines, + // presumably due to rounding errors in this function. The extra 1 here allows them to layout as expected. + // The buttons having problems were AlignedImageButtons and SaveButtons (which are subclasses of AlignedImageButtons). + // (Details: The button image's width is calculated as 12, however on screen the image takes a width of 13.) + let defaultSize = super.wmf_sizeThatFits(maximumSize) + + guard self.image(for: .normal) != nil else { + // If image, it should layout fine. + return defaultSize + } + return CGSize(width: defaultSize.width + 1, height: defaultSize.height) + } + + var layoutDirection: UIUserInterfaceLayoutDirection = .leftToRight + fileprivate func updateSemanticContentAttribute() { + layoutDirection = traitCollection.layoutDirection == .rightToLeft ? .rightToLeft : .leftToRight + if imageIsRightAligned { + if layoutDirection == .leftToRight { + semanticContentAttribute = .forceRightToLeft + imageView?.semanticContentAttribute = .forceLeftToRight + } else { + semanticContentAttribute = .forceLeftToRight + imageView?.semanticContentAttribute = .forceRightToLeft + } + } else { + if layoutDirection == .leftToRight { + semanticContentAttribute = .forceLeftToRight + imageView?.semanticContentAttribute = .forceLeftToRight + } else { + semanticContentAttribute = .forceRightToLeft + imageView?.semanticContentAttribute = .forceRightToLeft + } + } + } + + fileprivate func adjustInsets() { + let inset = semanticContentAttribute == .forceRightToLeft ? -0.5 * horizontalSpacing : 0.5 * horizontalSpacing + imageEdgeInsets = UIEdgeInsets(top: 0, left: -inset, bottom: 0, right: inset) + titleEdgeInsets = UIEdgeInsets(top: verticalPadding, left: inset, bottom: verticalPadding, right: -inset) + contentEdgeInsets = UIEdgeInsets(top: verticalPadding, left: abs(inset) + leftPadding, bottom: verticalPadding, right: abs(inset) + rightPadding) + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/AnnouncementCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/AnnouncementCollectionViewCell.swift new file mode 100644 index 0000000..37f304f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AnnouncementCollectionViewCell.swift @@ -0,0 +1,257 @@ +import UIKit + +public protocol AnnouncementCollectionViewCellDelegate: NSObjectProtocol { + func announcementCellDidTapDismiss(_ cell: AnnouncementCollectionViewCell) + func announcementCellDidTapActionButton(_ cell: AnnouncementCollectionViewCell) + func announcementCell(_ cell: AnnouncementCollectionViewCell, didTapLinkURL: URL) +} + +open class AnnouncementCollectionViewCell: CollectionViewCell { + public weak var delegate: AnnouncementCollectionViewCellDelegate? + + public let imageView = UIImageView() + private let messageTextView = UITextView() + public let actionButton = UIButton() + public let dismissButton = UIButton() + private let captionTextView = UITextView() + public let captionSeparatorView = UIView() + public let messageSpacing: CGFloat = 20 + public let buttonMargin: CGFloat = 40 + public let actionButtonHeight: CGFloat = 40 + public let dismissButtonSpacing: CGFloat = 8 + public let dismissButtonHeight: CGFloat = 32 + public var imageViewDimension: CGFloat = 150 + public let captionSpacing: CGFloat = 20 + + open override func setup() { + layoutMargins = UIEdgeInsets(top: 8, left: 15, bottom: 8, right: 15) + + imageView.contentMode = .scaleAspectFit + imageView.clipsToBounds = true + contentView.addSubview(imageView) + + messageTextView.isScrollEnabled = false + messageTextView.isEditable = false + messageTextView.delegate = self + contentView.addSubview(messageTextView) + + contentView.addSubview(actionButton) + + contentView.addSubview(dismissButton) + + contentView.addSubview(captionSeparatorView) + + captionTextView.isScrollEnabled = false + captionTextView.isEditable = false + captionTextView.delegate = self + contentView.addSubview(captionTextView) + + actionButton.contentEdgeInsets = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15) + actionButton.titleLabel?.numberOfLines = 0 + actionButton.addTarget(self, action: #selector(actionButtonPressed), for: .touchUpInside) + + dismissButton.contentEdgeInsets = UIEdgeInsets(top: 5, left: 15, bottom: 8, right: 15) + dismissButton.titleLabel?.numberOfLines = 0 + dismissButton.addTarget(self, action: #selector(dismissButtonPressed), for: .touchUpInside) + + super.setup() + } + + @objc func actionButtonPressed() { + delegate?.announcementCellDidTapActionButton(self) + } + + @objc func dismissButtonPressed() { + delegate?.announcementCellDidTapDismiss(self) + } + + // This method is called to reset the cell to the default configuration. It is called on initial setup and prepareForReuse. Subclassers should call super. + override open func reset() { + super.reset() + imageView.wmf_reset() + imageViewDimension = 150 + updateFonts(with: traitCollection) + captionHTML = nil + messageHTML = nil + isImageViewHidden = true + isUrgent = false + dismissButtonTitle = nil + } + + open override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + actionButton.titleLabel?.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection) + dismissButton.titleLabel?.font = UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + updateCaptionTextViewWithAttributedCaption() + } + + public var isImageViewHidden = false { + didSet { + imageView.isHidden = isImageViewHidden + setNeedsLayout() + } + } + + fileprivate var isCaptionHidden = false { + didSet { + captionSeparatorView.isHidden = isCaptionHidden + captionTextView.isHidden = isCaptionHidden + setNeedsLayout() + } + } + + fileprivate func updateCaptionTextViewWithAttributedCaption() { + guard let html = captionHTML else { + isCaptionHidden = true + return + } + let attributedText = html.byAttributingHTML(with: .footnote, matching: traitCollection, color: captionTextView.textColor) + let pStyle = NSMutableParagraphStyle() + pStyle.lineBreakMode = .byWordWrapping + pStyle.baseWritingDirection = .natural + let attributes: [NSAttributedString.Key : Any] = [NSAttributedString.Key.paragraphStyle: pStyle] + attributedText.addAttributes(attributes, range: NSRange(location: 0, length: attributedText.length)) + captionTextView.attributedText = attributedText + isCaptionHidden = false + } + + public var captionHTML: String? { + didSet { + updateCaptionTextViewWithAttributedCaption() + } + } + + public var isUrgent: Bool = false + private var messageUnderlineColor: UIColor = UIColor.black + private var messageEmphasisColor: UIColor = UIColor.black + private var messageLineHeightMultiple: CGFloat = 1 + private func updateMessageTextViewWithAttributedMessage() { + guard let html = messageHTML else { + messageTextView.attributedText = nil + return + } + let attributedText = html.byAttributingHTML(with: .subheadline, + boldWeight: .bold, + matching: traitCollection, + color: messageTextView.textColor, + tagMapping: ["em": "i"], // em tags are generally italicized by default, match this behavior + additionalTagAttributes: [ + "u": [ + NSAttributedString.Key.underlineColor: messageUnderlineColor, + NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue) + ], + "strong": [ + NSAttributedString.Key.foregroundColor: messageEmphasisColor + ] + ]) + let pStyle = NSMutableParagraphStyle() + pStyle.lineHeightMultiple = messageLineHeightMultiple + let attributes: [NSAttributedString.Key : Any] = [NSAttributedString.Key.paragraphStyle: pStyle] + attributedText.addAttributes(attributes, range: NSRange(location: 0, length: attributedText.length)) + messageTextView.attributedText = attributedText + } + + public var messageHTML: String? { + didSet { + updateMessageTextViewWithAttributedMessage() + } + } + + public var dismissButtonTitle: String? { + didSet { + let newTitle = dismissButtonTitle ?? CommonStrings.dismissButtonTitle + dismissButton.setTitle(newTitle, for: .normal) + setNeedsLayout() + } + } + + open override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let widthMinusMargins = layoutWidth(for: size) + let displayScale = traitCollection.displayScale > 0 ? traitCollection.displayScale : 2.0 + var origin = CGPoint(x: layoutMargins.left + layoutMarginsAdditions.left, y: 0) + + if !isImageViewHidden { + if apply { + imageView.frame = CGRect(x: 0, y: 0, width: size.width, height: imageViewDimension) + } + origin.y += imageViewDimension + } + + origin.y += messageSpacing + + let messageTextSize = messageTextView.sizeThatFits(CGSize(width: widthMinusMargins, height: CGFloat.greatestFiniteMagnitude)) + let messageFrame = CGRect(origin: origin, size: CGSize(width: widthMinusMargins, height: messageTextSize.height)) + if apply { + messageTextView.frame = messageFrame + } + origin.y += messageFrame.layoutHeight(with: messageSpacing) + + let buttonMinimumWidth = min(250, widthMinusMargins) + + origin.y += actionButton.wmf_preferredHeight(at: origin, maximumWidth: widthMinusMargins, minimumWidth: buttonMinimumWidth, horizontalAlignment: .center, spacing: dismissButtonSpacing, apply: apply) + origin.y += dismissButton.wmf_preferredHeight(at: origin, maximumWidth: widthMinusMargins, minimumWidth: buttonMinimumWidth, horizontalAlignment: .center, spacing: 0, apply: apply) + + + if !isCaptionHidden { + origin.y += dismissButtonSpacing + let separatorFrame = CGRect(x: origin.x, y: origin.y, width: widthMinusMargins, height: 1.0 / displayScale) + if apply { + captionSeparatorView.frame = separatorFrame + } + origin.y += separatorFrame.height + origin.y += captionSpacing + let captionTextViewSize = captionTextView.sizeThatFits(CGSize(width: widthMinusMargins, height: CGFloat.greatestFiniteMagnitude)) + let captionFrame = CGRect(origin: origin, size: CGSize(width: widthMinusMargins, height: captionTextViewSize.height)) + if apply { + captionTextView.frame = captionFrame + } + origin.y += captionFrame.height + origin.y += captionSpacing + } else { + origin.y += layoutMargins.bottom + } + + return CGSize(width: size.width, height: origin.y) + } +} + + +extension AnnouncementCollectionViewCell: UITextViewDelegate { + public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool { + delegate?.announcementCell(self, didTapLinkURL: URL) + return false + } +} + +extension AnnouncementCollectionViewCell: Themeable { + @objc(applyTheme:) + public func apply(theme: Theme) { + setBackgroundColors(theme.colors.cardBackground, selected: theme.colors.selectedCardBackground) + messageTextView.textColor = theme.colors.primaryText + messageTextView.backgroundColor = .clear + dismissButton.setTitleColor(theme.colors.secondaryText, for: .normal) + imageView.backgroundColor = theme.colors.midBackground + imageView.alpha = theme.imageOpacity + actionButton.setTitleColor(theme.colors.link, for: .normal) + actionButton.backgroundColor = theme.colors.cardButtonBackground + messageLineHeightMultiple = 1.25 + if isUrgent { + messageUnderlineColor = theme.colors.error + messageEmphasisColor = theme.colors.error + layer.borderWidth = 3 + layer.borderColor = theme.colors.error.cgColor + layer.cornerRadius = Theme.exploreCardCornerRadius + } else { + layer.borderWidth = 0 + layer.cornerRadius = 0 + messageUnderlineColor = messageTextView.textColor ?? theme.colors.primaryText + messageEmphasisColor = messageTextView.textColor ?? theme.colors.primaryText + } + actionButton.layer.cornerRadius = 5 + captionSeparatorView.backgroundColor = theme.colors.border + captionTextView.textColor = theme.colors.secondaryText + captionTextView.backgroundColor = .clear + updateCaptionTextViewWithAttributedCaption() + updateMessageTextViewWithAttributedMessage() + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/AppDelegate.h b/Apps/Wikipedia/Wikipedia/Code/AppDelegate.h new file mode 100644 index 0000000..c73920c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AppDelegate.h @@ -0,0 +1,7 @@ +@import UIKit; + +@interface AppDelegate : UIResponder + +@property (nonatomic, strong) UIWindow *window; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/AppDelegate.m b/Apps/Wikipedia/Wikipedia/Code/AppDelegate.m new file mode 100644 index 0000000..c59f178 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AppDelegate.m @@ -0,0 +1,277 @@ +#import "AppDelegate.h" +@import UserNotifications; +@import BackgroundTasks; +@import WMF.NSUserActivity_WMFExtensions; +@import WMF.NSFileManager_WMFGroup; +#import "WMFAppViewController.h" +#import "UIApplicationShortcutItem+WMFShortcutItem.h" +#import "Wikipedia-Swift.h" +#import "WMFQuoteMacros.h" + +static NSTimeInterval const WMFBackgroundFetchInterval = 10800; // 3 Hours +static NSString *const WMFBackgroundAppRefreshTaskIdentifier = @"org.wikimedia.wikipedia.appRefresh"; +static NSString *const WMFBackgroundDatabaseHousekeeperTaskIdentifier = @"org.wikimedia.wikipedia.databaseHousekeeper"; + +@interface AppDelegate () + +@property (nonatomic, strong) WMFAppViewController *appViewController; +@property (nonatomic) BOOL appNeedsResume; + +@end + +@implementation AppDelegate + +#pragma mark - Defaults + ++ (void)load { + /** + * Register default application preferences. + * @note This must be loaded before application launch so unit tests can run + */ + [[NSUserDefaults standardUserDefaults] registerDefaults:@{ + @"WMFAutoSignTalkPageDiscussions": @YES + }]; +} + +#pragma mark - Accessors + +- (UIWindow *)window { + if (!_window) { + _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + } + return _window; +} + +#pragma mark - Shortcuts + +- (void)updateDynamicIconShortcutItems { + if (![[UIApplication sharedApplication] respondsToSelector:@selector(shortcutItems)]) { + return; + } + + NSMutableArray *shortcutItems = + [[NSMutableArray alloc] initWithObjects: + [UIApplicationShortcutItem wmf_random], + [UIApplicationShortcutItem wmf_nearby], + nil]; + + [shortcutItems addObject:[UIApplicationShortcutItem wmf_search]]; + + [UIApplication sharedApplication].shortcutItems = shortcutItems; +} + +#pragma mark - UIApplicationDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [self registerBackgroundTasksForApplication:application]; + +#if DEBUG + // Use NSLog so we can break and copy/paste. DDLogDebug is async. + NSLog(@"\nSimulator container directory:\n\t%@\n", + [[NSFileManager defaultManager] wmf_containerPath]); +#endif + +#if UI_TEST + if ([[NSUserDefaults standardUserDefaults] wmf_isFastlaneSnapshotInProgress]) { + [UIView setAnimationsEnabled:NO]; + } +#endif + + [[NSUserDefaults standardUserDefaults] wmf_migrateFontSizeMultiplier]; + NSUserDefaults.standardUserDefaults.shouldRestoreNavigationStackOnResume = [self shouldRestoreNavigationStackOnResumeAfterBecomingActive]; + + self.appNeedsResume = YES; + WMFAppViewController *vc = [[WMFAppViewController alloc] init]; + [[UIApplication sharedApplication] registerForRemoteNotifications]; + [UNUserNotificationCenter currentNotificationCenter].delegate = vc; // this needs to be set before the end of didFinishLaunchingWithOptions: + [vc launchAppInWindow:self.window waitToResumeApp:self.appNeedsResume]; + self.appViewController = vc; + + [self updateDynamicIconShortcutItems]; + + return YES; +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + [self cancelPendingBackgroundTasks]; +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + [self resumeAppIfNecessary]; + [[WMFMetricsClientBridge sharedInstance] appInForeground]; +} + +- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler { + [self.appViewController processShortcutItem:shortcutItem completion:completionHandler]; +} + +#pragma mark - AppVC Resume + +- (void)resumeAppIfNecessary { + if (self.appNeedsResume) { + [self.appViewController hideSplashScreenAndResumeApp]; + self.appNeedsResume = false; + } +} + +- (BOOL)shouldRestoreNavigationStackOnResumeAfterBecomingActive { + BOOL shouldOpenAppOnSearchTab = [NSUserDefaults standardUserDefaults].wmf_openAppOnSearchTab; + if (shouldOpenAppOnSearchTab) { + return NO; + } + + return YES; +} + +#pragma mark - NSUserActivity Handling + +- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType { + return YES; +} + +- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> *__nullable restorableObjects))restorationHandler { + [self.appViewController showSplashView]; + + // Assign deep link user info source before routing + NSMutableDictionary *mutableUserInfo = userActivity.userInfo != nil ? [[NSMutableDictionary alloc] initWithDictionary:userActivity.userInfo] : [[NSMutableDictionary alloc] init]; + mutableUserInfo[WMFRoutingUserInfoKeys.source] = WMFRoutingUserInfoSourceValue.deepLinkRawValue; + NSDictionary *newUserInfo = [[NSDictionary alloc] initWithDictionary:mutableUserInfo]; + userActivity.userInfo = newUserInfo; + + BOOL result = [self.appViewController processUserActivity:userActivity + animated:NO + completion:^{ + if (self.appNeedsResume) { + [self resumeAppIfNecessary]; + } else { + [self.appViewController hideSplashViewAnimated:YES]; + } + }]; + return result; +} + +- (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error { + DDLogDebug(@"didFailToContinueUserActivityWithType: %@ error: %@", userActivityType, error); +} + +- (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserActivity *)userActivity { + DDLogDebug(@"didUpdateUserActivity: %@", userActivity); +} + +#pragma mark - NSURL Handling + +- (BOOL)application:(UIApplication *)app + openURL:(NSURL *)url + options:(NSDictionary *)options { + NSUserActivity *activity = [NSUserActivity wmf_activityForWikipediaScheme:url] ?: [NSUserActivity wmf_activityForURL:url]; + if (activity) { + [self.appViewController showSplashView]; + BOOL result = [self.appViewController processUserActivity:activity + animated:NO + completion:^{ + if (self.appNeedsResume) { + [self resumeAppIfNecessary]; + } else { + [self.appViewController hideSplashViewAnimated:YES]; + } + }]; + return result; + } else { + [self resumeAppIfNecessary]; + return NO; + } +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + [[NSUserDefaults standardUserDefaults] wmf_setAppResignActiveDate:[NSDate date]]; + [[WMFMetricsClientBridge sharedInstance] appInBackground]; +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + [self updateDynamicIconShortcutItems]; + [self scheduleBackgroundAppRefreshTask]; + [self scheduleDatabaseHousekeeperTask]; +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + [self updateDynamicIconShortcutItems]; + [[WMFMetricsClientBridge sharedInstance] appWillClose]; +} + +#pragma mark - Background Fetch + +/// Cancels any pending background tasks, if applicable on the current platform +- (void)cancelPendingBackgroundTasks { + [[BGTaskScheduler sharedScheduler] cancelAllTaskRequests]; +} + +/// Register for any necessary background tasks or updates with the method appropriate for the platform +- (void)registerBackgroundTasksForApplication:(UIApplication *)application { + [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:WMFBackgroundAppRefreshTaskIdentifier + usingQueue:dispatch_get_main_queue() + launchHandler:^(__kindof BGTask *_Nonnull task) { + [self.appViewController performBackgroundFetchWithCompletion:^(UIBackgroundFetchResult result) { + switch (result) { + case UIBackgroundFetchResultFailed: + [task setTaskCompletedWithSuccess:NO]; + break; + default: + [task setTaskCompletedWithSuccess:YES]; + break; + } + // The next task needs to be scheduled + [self scheduleBackgroundAppRefreshTask]; + }]; + }]; + + [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:WMFBackgroundDatabaseHousekeeperTaskIdentifier + usingQueue:dispatch_get_main_queue() + launchHandler:^(__kindof BGTask *_Nonnull task) { + [self.appViewController performDatabaseHousekeepingWithCompletion:^(NSError *error) { + if (error != nil) { + [task setTaskCompletedWithSuccess:NO]; + } else { + [task setTaskCompletedWithSuccess:YES]; + } + }]; + }]; +} + +/// Schedule the next background refresh, if applicable on the current platform +- (void)scheduleBackgroundAppRefreshTask { + BGAppRefreshTaskRequest *appRefreshTask = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:WMFBackgroundAppRefreshTaskIdentifier]; + appRefreshTask.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:WMFBackgroundFetchInterval]; + NSError *taskSubmitError = nil; + if (![[BGTaskScheduler sharedScheduler] submitTaskRequest:appRefreshTask error:&taskSubmitError]) { + DDLogError(@"Unable to schedule background task: %@", taskSubmitError); + } +} + +- (void)scheduleDatabaseHousekeeperTask { + BGProcessingTaskRequest *databaseHousekeeperTask = [[BGProcessingTaskRequest alloc] initWithIdentifier:WMFBackgroundDatabaseHousekeeperTaskIdentifier]; + databaseHousekeeperTask.earliestBeginDate = nil; // Docs indicate nil = no start delay. + NSError *taskSubmitError = nil; + if (![[BGTaskScheduler sharedScheduler] submitTaskRequest:databaseHousekeeperTask error:&taskSubmitError]) { + DDLogError(@"Unable to schedule background task: %@", taskSubmitError); + } +} + +#pragma mark - Notifications + +- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + DDLogError(@"Remote notification registration failure: %@", error.localizedDescription); + [self.appViewController setRemoteNotificationRegistrationStatusWithDeviceToken:nil error:error]; +} + +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + [self.appViewController setRemoteNotificationRegistrationStatusWithDeviceToken:deviceToken error:nil]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/AppSearchButton.swift b/Apps/Wikipedia/Wikipedia/Code/AppSearchButton.swift new file mode 100644 index 0000000..92f6e98 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AppSearchButton.swift @@ -0,0 +1,14 @@ +import UIKit + +@objc(WMFAppSearchBarButtonItem) +class AppSearchBarButtonItem: UIBarButtonItem { + @objc static var newAppSearchBarButtonItem: AppSearchBarButtonItem { + let button = AppSearchBarButtonItem(image: UIImage(named: "search"), style: .plain, target: self, action: #selector(makeAppSearchViewActivityActive)) + button.accessibilityLabel = WMFLocalizedString("search-button-accessibility-label", value: "Search Wikipedia", comment: "Accessibility label for a button that opens a search box to search Wikipedia.") + return button + } + + @objc static func makeAppSearchViewActivityActive() { + NSUserActivity.wmf_navigate(to: NSUserActivity.wmf_searchView()) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/AppTabBarDelegate.swift b/Apps/Wikipedia/Wikipedia/Code/AppTabBarDelegate.swift new file mode 100644 index 0000000..81f2aa6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AppTabBarDelegate.swift @@ -0,0 +1,6 @@ +import Foundation + +@objc(WMFAppTabBarDelegate) +protocol AppTabBarDelegate { + var tabBar: UITabBar { get } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/AppearanceSettingsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/AppearanceSettingsViewController.swift new file mode 100644 index 0000000..555a247 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AppearanceSettingsViewController.swift @@ -0,0 +1,305 @@ +import UIKit + +protocol AppearanceSettingsItem { + var title: String? { get } + var subtitle: String? { get } +} + +struct AppearanceSettingsDimSwitchItem: AppearanceSettingsItem { + let title: String? + let subtitle: String? +} + +struct AppearanceSettingsAutomaticTableOpenSwitchItem: AppearanceSettingsItem { + let title: String? + let subtitle: String? +} + +struct AppearanceSettingsCheckmarkItem: AppearanceSettingsItem { + let title: String? + let subtitle: String? + let theme: String + let checkmarkAction: () -> Void +} + +struct AppearanceSettingsSection { + let headerTitle: String? + let footerText: String? + let items: [AppearanceSettingsItem] +} + +struct AppearanceSettingsCustomViewItem: AppearanceSettingsItem { + let title: String? + let subtitle: String? + let viewController: UIViewController +} + +struct AppearanceSettingsSpacerViewItem: AppearanceSettingsItem { + var title: String? + let subtitle: String? + let spacing: CGFloat +} + +@objc(WMFAppearanceSettingsViewController) +final class AppearanceSettingsViewController: SubSettingsViewController { + static let customViewCellReuseIdentifier = "org.wikimedia.custom" + + var sections = [AppearanceSettingsSection]() + + @objc static var disclosureText: String { + return UserDefaults.standard.themeDisplayName + } + + deinit { + NSObject.cancelPreviousPerformRequests(withTarget: self) + } + + override public func viewDidLoad() { + super.viewDidLoad() + extendedLayoutIncludesOpaqueBars = true + title = CommonStrings.readingPreferences + tableView.register(WMFSettingsTableViewCell.wmf_classNib(), forCellReuseIdentifier: WMFSettingsTableViewCell.identifier) + tableView.register(UITableViewCell.self, forCellReuseIdentifier: AppearanceSettingsViewController.customViewCellReuseIdentifier) + tableView.register(WMFTableHeaderFooterLabelView.wmf_classNib(), forHeaderFooterViewReuseIdentifier: WMFTableHeaderFooterLabelView.identifier) + tableView.sectionHeaderHeight = UITableView.automaticDimension + tableView.estimatedSectionHeaderHeight = 44 + tableView.sectionFooterHeight = UITableView.automaticDimension + tableView.estimatedSectionFooterHeight = 44 + sections = sectionsForAppearanceSettings() + } + + func sectionsForAppearanceSettings() -> [AppearanceSettingsSection] { + + func checkmarkItem(for theme: Theme) -> (AppearanceSettingsCheckmarkItem) { + return AppearanceSettingsCheckmarkItem(title: theme.displayName, subtitle: nil, theme: theme.name) { [weak self] in + self?.userDidSelect(theme: theme.name) + } + } + + let subtitle = WMFLocalizedString("theme-default-explanation", value:"Matches system theme", comment: "Explains that the default theme matches the iOS system theme setting") + + let defaultThemeItem = AppearanceSettingsCheckmarkItem(title: CommonStrings.defaultThemeDisplayName, subtitle: subtitle, theme: Theme.defaultThemeName, checkmarkAction: { [weak self] in + self?.userDidSelect(theme: Theme.defaultThemeName) + }) + + let items = [defaultThemeItem, checkmarkItem(for: Theme.light), checkmarkItem(for: Theme.sepia), checkmarkItem(for: Theme.dark), checkmarkItem(for: Theme.black)] + + let readingThemesSection = + AppearanceSettingsSection(headerTitle: WMFLocalizedString("appearance-settings-reading-themes", value: "Reading themes", comment: "Title of the Reading themes section in Appearance settings"), footerText: nil, items: items) + + let themeOptionsSection = AppearanceSettingsSection(headerTitle: WMFLocalizedString("appearance-settings-theme-options", value: "Theme options", comment: "Title of the Theme options section in Appearance settings"), footerText: WMFLocalizedString("appearance-settings-image-dimming-footer", value: "Decrease the opacity of images on the dark and black themes", comment: "Footer of the Theme options section in Appearance settings, explaining image dimming"), items: [AppearanceSettingsCustomViewItem(title: nil, subtitle: nil, viewController: ImageDimmingExampleViewController(nibName: "ImageDimmingExampleViewController", bundle: nil)), AppearanceSettingsSpacerViewItem(title: nil, subtitle: nil, spacing: 15.0), AppearanceSettingsDimSwitchItem(title: CommonStrings.dimImagesTitle, subtitle: nil)]) + + let tableAutomaticOpenSection = AppearanceSettingsSection(headerTitle: WMFLocalizedString("appearance-settings-set-automatic-table-opening", value: "Table Settings", comment: "Tables in article will be opened automatically"), footerText: WMFLocalizedString("appearance-settings-expand-tables-footer", value: "Set all tables in all articles to be open by default, including Quick facts, References, Notes and External links.", comment: "Footer of the expand tables section in Appearance settings, explaining the expand tables setting"), items: [AppearanceSettingsAutomaticTableOpenSwitchItem(title: WMFLocalizedString("appearance-settings-expand-tables", value: "Expand tables", comment: "Title for the setting that expands tables in an article by default"), subtitle: nil)]) + + let textSizingSection = AppearanceSettingsSection(headerTitle: WMFLocalizedString("appearance-settings-adjust-text-sizing", value: "Adjust article text sizing", comment: "Header of the Text sizing section in Appearance settings"), footerText: nil, items: [AppearanceSettingsCustomViewItem(title: nil, subtitle: nil, viewController: TextSizeChangeExampleViewController(nibName: "TextSizeChangeExampleViewController", bundle: nil)), AppearanceSettingsSpacerViewItem(title: nil, subtitle: nil, spacing: 15.0), AppearanceSettingsCustomViewItem(title: nil, subtitle: nil, viewController: FontSizeSliderViewController(nibName: "FontSizeSliderViewController", bundle: nil))]) + + return [readingThemesSection, themeOptionsSection, tableAutomaticOpenSection, textSizingSection] + } + + public override func numberOfSections(in tableView: UITableView) -> Int { + return sections.count + } + + public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return sections[section].items.count + } + + public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let item = sections[indexPath.section].items[indexPath.item] + + if let customViewItem = item as? AppearanceSettingsCustomViewItem { + let cell = tableView.dequeueReusableCell(withIdentifier: AppearanceSettingsViewController.customViewCellReuseIdentifier, for: indexPath) + let vc = customViewItem.viewController + + if let view = vc.view { + addChild(vc) + view.frame = cell.contentView.bounds + view.autoresizingMask = [.flexibleWidth, .flexibleHeight] + cell.contentView.addSubview(view) + vc.didMove(toParent: self) + } + + if let themeable = vc as? Themeable { + themeable.apply(theme: self.theme) + cell.backgroundColor = vc.view.backgroundColor + } + + if let dimming = vc as? ImageDimmingExampleViewController { + // themeTODO: define a semantic color for this instead of checking isDark + dimming.view.backgroundColor = self.theme.isDark ? self.theme.colors.paperBackground : .gray650 + dimming.isImageDimmed = UserDefaults.standard.wmf_isImageDimmingEnabled + } + + cell.selectionStyle = .none + return cell + } + + if item is AppearanceSettingsSpacerViewItem { + let cell = tableView.dequeueReusableCell(withIdentifier: AppearanceSettingsViewController.customViewCellReuseIdentifier, for: indexPath) + cell.backgroundColor = tableView.backgroundColor + return cell + } + + guard let cell = tableView.dequeueReusableCell(withIdentifier: WMFSettingsTableViewCell.identifier, for: indexPath) as? WMFSettingsTableViewCell else { + return UITableViewCell() + } + + cell.title = item.title + cell.subtitle = item.subtitle + cell.iconName = nil + + if let tc = cell as Themeable? { + tc.apply(theme: theme) + } + + if item is AppearanceSettingsDimSwitchItem { + cell.disclosureType = .switch + cell.disclosureSwitch.isOn = UserDefaults.standard.wmf_isImageDimmingEnabled + cell.disclosureSwitch.addTarget(self, action: #selector(self.handleImageDimmingSwitchValueChange(_:)), for: .valueChanged) + cell.iconName = "settings-image-dimming" + cell.iconBackgroundColor = .gray400 + cell.iconColor = .white + cell.selectionStyle = .none + } else if item is AppearanceSettingsAutomaticTableOpenSwitchItem { + cell.disclosureType = .switch + cell.disclosureSwitch.isEnabled = true + cell.disclosureSwitch.isOn = UserDefaults.standard.wmf_isAutomaticTableOpeningEnabled + cell.disclosureSwitch.addTarget(self, action: #selector(self.handleAutomaticTableOpenSwitchValueChange(_:)), for: .valueChanged) + cell.iconName = "settings-tables-expand" + cell.iconBackgroundColor = .blue300 + cell.iconColor = .white + cell.selectionStyle = .none + } else { + cell.disclosureType = .none + } + + if let iconBackgroundColor = theme.colors.iconBackground, let iconColor = theme.colors.icon { + cell.iconBackgroundColor = iconColor + cell.iconColor = iconBackgroundColor + } + + return cell + } + + // keep @objc on UITableViewDelegate methods otherwise they aren't called on release builds + @objc public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { + let item = sections[indexPath.section].items[indexPath.item] + guard let customViewItem = item as? AppearanceSettingsCustomViewItem else { + return + } + let vc = customViewItem.viewController + vc.willMove(toParent: nil) + vc.view.removeFromSuperview() + vc.removeFromParent() + if let cell = cell as? WMFSettingsTableViewCell { + cell.disclosureSwitch.removeTarget(nil, action: nil, for: .valueChanged) + } + } + + func userDidSelect(theme: String, isImageDimmingEnabled: Bool? = nil) { + var userInfo: [String: Any] = [ReadingThemesControlsViewController.WMFUserDidSelectThemeNotificationThemeNameKey: theme] + if let isImageDimmingEnabled = isImageDimmingEnabled { + userInfo[ReadingThemesControlsViewController.WMFUserDidSelectThemeNotificationIsImageDimmingEnabledKey] = NSNumber(booleanLiteral: isImageDimmingEnabled) + } + NotificationCenter.default.post(name: Notification.Name(ReadingThemesControlsViewController.WMFUserDidSelectThemeNotification), object: nil, userInfo: userInfo) + } + + @objc public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + if let customViewItem = sections[indexPath.section].items[indexPath.item] as? AppearanceSettingsCustomViewItem { + return customViewItem.viewController.view.frame.height + } else if let spacerViewItem = sections[indexPath.section].items[indexPath.item] as? AppearanceSettingsSpacerViewItem { + return spacerViewItem.spacing + } + return tableView.rowHeight + } + + @objc public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let item = sections[indexPath.section].items[indexPath.item] as? AppearanceSettingsCheckmarkItem else { + return + } + item.checkmarkAction() + tableView.reloadData() + } + + @objc public func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? { + guard sections[indexPath.section].items[indexPath.item] is AppearanceSettingsCheckmarkItem else { + return nil + } + return indexPath + } + + @objc public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + let currentAppTheme = UserDefaults.standard.themeName + + if let checkmarkItem = sections[indexPath.section].items[indexPath.item] as? AppearanceSettingsCheckmarkItem { + if currentAppTheme.hasPrefix(checkmarkItem.theme) { + cell.accessoryType = .checkmark + cell.isSelected = true + } else { + cell.accessoryType = .none + cell.isSelected = false + } + } + } + + @objc func applyImageDimmingChange(isOn: NSNumber) { + let currentTheme = UserDefaults.standard.themeName + userDidSelect(theme: currentTheme, isImageDimmingEnabled: isOn.boolValue) + tableView.reloadData() + } + + @objc func handleImageDimmingSwitchValueChange(_ sender: UISwitch) { + let selector = #selector(applyImageDimmingChange) + NSObject.cancelPreviousPerformRequests(withTarget: self) + perform(selector, with: NSNumber(value: sender.isOn), afterDelay: CATransaction.animationDuration()) + } + + @objc func applyAutomaticTableOpenChange(isOn: NSNumber) { + UserDefaults.standard.wmf_isAutomaticTableOpeningEnabled = isOn.boolValue + } + + @objc func handleAutomaticTableOpenSwitchValueChange(_ sender: UISwitch) { + let selector = #selector(applyAutomaticTableOpenChange) + NSObject.cancelPreviousPerformRequests(withTarget: self) + perform(selector, with: NSNumber(value: sender.isOn), afterDelay: CATransaction.animationDuration()) + } + + @objc func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return UITableView.automaticDimension + } + + @objc func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + + let text = sections[safeIndex: section]?.headerTitle + return WMFTableHeaderFooterLabelView.headerFooterViewForTableView(tableView, text: text, type: .header, theme: theme) + } + + @objc func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + guard let text = sections[safeIndex: section]?.footerText, + !text.isEmpty else { + + return section < (self.sections.count - 1) ? 0 : 44 + } + + return UITableView.automaticDimension + } + + @objc func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + + let text = sections[safeIndex: section]?.footerText + return WMFTableHeaderFooterLabelView.headerFooterViewForTableView(tableView, text: text, type: .footer, theme: theme) + } + + // MARK: - Themeable + + override public func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.baseBackground + tableView.backgroundColor = theme.colors.baseBackground + tableView.reloadData() + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Array+Chunked.swift b/Apps/Wikipedia/Wikipedia/Code/Array+Chunked.swift new file mode 100644 index 0000000..f937ca9 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Array+Chunked.swift @@ -0,0 +1,7 @@ +public extension Array { + func chunked(into size: Int) -> [[Element]] { + return stride(from: 0, to: count, by: size).map { + Array(self[$0 ..< Swift.min($0 + size, count)]) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Array+WMFAllFieldsFilled.swift b/Apps/Wikipedia/Wikipedia/Code/Array+WMFAllFieldsFilled.swift new file mode 100644 index 0000000..743aba8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Array+WMFAllFieldsFilled.swift @@ -0,0 +1,15 @@ +// MARK: - Extension for arrays comprised of UITextFields +extension Array where Element: UITextField { + /// Determines whether all UITextFields in this array have had text characters entered into them. + /// + /// - Returns: Bool indicating whether all UITextFields in this array have had text characters entered into them. + func wmf_allFieldsFilled() -> Bool { + let emptyElement: UITextField? = first { (element) -> Bool in + guard let text = element.text else { + return true + } + return text.isEmpty + } + return emptyElement == nil + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ActivityIndicatorCollectionViewFooter.swift b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ActivityIndicatorCollectionViewFooter.swift new file mode 100644 index 0000000..a0dc42d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ActivityIndicatorCollectionViewFooter.swift @@ -0,0 +1,43 @@ +class ActivityIndicatorCollectionViewFooter: UICollectionReusableView { + private let loadingIndicator = UIActivityIndicatorView(style: .medium) + private let loadingLabel = UILabel() + + override init(frame: CGRect) { + super.init(frame: .zero) + setupView() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupView() { + loadingLabel.font = UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + loadingLabel.text = WMFLocalizedString("loading-indicator-text", value: "Loading", comment: "Text shown underneath loading indicator.").uppercased() + + addSubview(loadingLabel) + addSubview(loadingIndicator) + loadingLabel.translatesAutoresizingMaskIntoConstraints = false + loadingIndicator.translatesAutoresizingMaskIntoConstraints = false + + // Need to give this a non-1000 priority to avoid constraint issues in the logs. + let separationConstraint = loadingLabel.topAnchor.constraint(equalTo: loadingIndicator.bottomAnchor, constant: 4) + separationConstraint.priority = UILayoutPriority(999) + NSLayoutConstraint.activate([ + loadingIndicator.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 30), + separationConstraint, + loadingLabel.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -30), + loadingIndicator.centerXAnchor.constraint(equalTo: centerXAnchor), + loadingLabel.centerXAnchor.constraint(equalTo: centerXAnchor) + ]) + loadingIndicator.startAnimating() + } +} + +extension ActivityIndicatorCollectionViewFooter: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + loadingLabel.textColor = theme.colors.secondaryText + loadingIndicator.color = theme.colors.secondaryText + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocController.swift b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocController.swift new file mode 100644 index 0000000..10b899c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocController.swift @@ -0,0 +1,488 @@ +import Foundation +import UIKit +import CocoaLumberjackSwift +import WMF + +protocol ArticleAsLivingDocControllerDelegate: AnyObject { + var articleURL: URL { get } + var article: WMFArticle { get } + var messagingController: ArticleWebMessagingController { get } + var theme: Theme { get } + var webView: WKWebView { get } + var leadImageContainerView: UIView { get } + func updateArticleMargins() + var isInValidSurveyCampaignAndArticleList: Bool { get } + var abTestsController: ABTestsController { get } + func extendTimerForPresentingModal() +} + +enum ArticleAsLivingDocSurveyLinkState { + case notInExperiment + case inExperimentLoadingEvents + case inExperimentFailureLoadingEvents + case inExperimentLoadedEventsDidNotSeeModal + case inExperimentLoadedEventsDidSeeModal +} + +class ArticleAsLivingDocController: NSObject { + + enum Errors: Error { + case viewModelInstantiationFailure + } + + typealias ArticleAsLivingDocConformingViewController = ArticleAsLivingDocControllerDelegate & UIViewController & HintPresenting & ArticleAsLivingDocViewControllerDelegate + + private weak var delegate: ArticleAsLivingDocConformingViewController? + private(set) var surveyLinkState: ArticleAsLivingDocSurveyLinkState + + var _articleAsLivingDocViewModel: ArticleAsLivingDocViewModel? + var articleAsLivingDocViewModel: ArticleAsLivingDocViewModel? { + get { + return _articleAsLivingDocViewModel + } + set { + guard let newValue = newValue else { + // should only occur when resetting to nil shortly before a pull to refresh was triggered. + _articleAsLivingDocViewModel = nil + return + } + + if let oldModel = _articleAsLivingDocViewModel { + // should only be triggered via paging. + // update everything except sha and htmlInsert and + // append sections instead of replace sections + let appendedSections = oldModel.sections + newValue.sections + let oldHtmlSnippets = oldModel.articleInsertHtmlSnippets + let oldLastUpdatedTimestamp = oldModel.lastUpdatedTimestamp + _articleAsLivingDocViewModel = ArticleAsLivingDocViewModel(nextRvStartId: newValue.nextRvStartId, sha: oldModel.sha, sections: appendedSections, summaryText: newValue.summaryText, articleInsertHtmlSnippets: oldHtmlSnippets, lastUpdatedTimestamp: oldLastUpdatedTimestamp) + articleAsLivingDocViewController?.appendSections(newValue.sections) + } else { + // should only be triggered via pull to refresh or fresh load. update everything + _articleAsLivingDocViewModel = newValue + // note, we aren't updating data source in VC here. So far we won't reach this situation where a refresh + // is triggered while the events modal is still on screen, so not needed at this point. + } + } + } + var articleAsLivingDocEditMetrics: [NSNumber]? + + var articleAsLivingDocViewController: ArticleAsLivingDocViewController? + + let shouldAttemptToShowArticleAsLivingDoc = false + + var shouldShowArticleAsLivingDoc: Bool { + if let articleAsLivingDocViewModel = articleAsLivingDocViewModel, + articleAsLivingDocViewModel.sections.count > 0, + shouldAttemptToShowArticleAsLivingDoc { + return true + } + + return false + } + + var injectingSkeleton = false + var hasSkeleton = false + var loadingArticleContent = true + var isPullToRefreshing = false + var failedLastInitialFetch = false + + private var currentFetchRvStartIds: [UInt] = [] + var isFetchingAdditionalPages: Bool { + return !currentFetchRvStartIds.isEmpty + } + + var hintController: HintController? + + required init(delegate: ArticleAsLivingDocConformingViewController) { + self.delegate = delegate + surveyLinkState = .notInExperiment + + super.init() + + if shouldAttemptToShowArticleAsLivingDoc { + surveyLinkState = .inExperimentLoadingEvents + } + } + + func articleTitleAndSiteURL() -> (title: String, siteURL: URL)? { + + guard let delegate = delegate, + let title = delegate.articleURL.wmf_title?.denormalizedPageTitle, + let siteURL = delegate.articleURL.wmf_site else { + return nil + } + + return (title, siteURL) + } + + func articleDidTriggerPullToRefresh() { + articleAsLivingDocViewModel = nil + isPullToRefreshing = true + } + + func articleContentFinishedLoading() { + self.loadingArticleContent = false + let delay = self.isPullToRefreshing ? 0.0 : 0.3 + self.isPullToRefreshing = false + if self.articleAsLivingDocViewModel != nil { + self.configureForArticleAsLivingDocResult() + } else { + self.scheduleInjectArticleAsLivingDocSkeletonAfterDelay(delay) + } + } + + func articleContentWillBeginLoading(traitCollection: UITraitCollection, theme: Theme) { + loadingArticleContent = true + articleAsLivingDocViewModel = nil + fetchInitialArticleAsLivingDoc(traitCollection: traitCollection, theme: theme) + } + + func setupLeadImageView() { + if shouldAttemptToShowArticleAsLivingDoc { + toggleContentVisibilityExceptLeadImage(shouldHide: true) + } + } + + func scheduleInjectArticleAsLivingDocSkeletonAfterDelay(_ delay: TimeInterval) { + + guard shouldAttemptToShowArticleAsLivingDoc, + articleAsLivingDocViewModel == nil else { + return + } + + if delay > 0 { + perform(#selector(injectArticleAsLivingDocSkeletonIfNeeded), with: nil, afterDelay: delay) + } else { + injectArticleAsLivingDocSkeletonIfNeeded() + } + } + + @objc func injectArticleAsLivingDocSkeletonIfNeeded() { + guard shouldAttemptToShowArticleAsLivingDoc, + articleAsLivingDocViewModel == nil, + let delegate = delegate else { + return + } + + injectingSkeleton = true + delegate.messagingController.injectSkeletonArticleAsLivingDocContent { [weak self] (success) in + + guard let self = self else { + return + } + + let completion = { + self.injectingSkeleton = false + self.hasSkeleton = true + self.toggleContentVisibilityExceptLeadImage(shouldHide: false) + if self.articleAsLivingDocViewModel != nil { + self.injectArticleAsALivingDocument() + } else if self.failedLastInitialFetch { + self.showError() + } + } + + if success { + self.delegate?.updateArticleMargins() + completion() + } else { + completion() + } + } + } + + func fetchInitialArticleAsLivingDoc(traitCollection: UITraitCollection, theme: Theme) { + + // triggered via initial load or pull to refresh + + guard let articleTitleAndSiteURL = self.articleTitleAndSiteURL(), + shouldAttemptToShowArticleAsLivingDoc, + let delegate = delegate else { + return + } + + failedLastInitialFetch = false + + fetchArticleAsLivingDocViewModel(rvStartId: nil, title: articleTitleAndSiteURL.title, siteURL: articleTitleAndSiteURL.siteURL, traitCollection: traitCollection, theme: theme) { [weak self] (result) in + + guard let self = self else { + return + } + + defer { + self.configureForArticleAsLivingDocResult() + } + switch result { + case .success(let articleAsLivingDocViewModel): + self.articleAsLivingDocViewModel = articleAsLivingDocViewModel + self.surveyLinkState = .inExperimentLoadedEventsDidNotSeeModal + case .failure(let error): + if self.hasSkeleton { + self.showError() + } else { + self.failedLastInitialFetch = true + } + self.surveyLinkState = .inExperimentFailureLoadingEvents + DDLogDebug("Failure getting article as living doc view models: \(error)") + } + } + + fetchEditMetrics(for: articleTitleAndSiteURL.title, pageURL: delegate.articleURL) { [weak self] result in + DispatchQueue.main.async { + guard let self = self else { + return + } + switch result { + case .failure(let error): + self.articleAsLivingDocEditMetrics = nil + DDLogDebug("Error fetching edit metrics for article as a living document: \(error)") + case .success(let timeseriesOfEditCounts): + self.articleAsLivingDocEditMetrics = timeseriesOfEditCounts + } + } + } + } + + func showError() { + delegate?.messagingController.removeArticleAsLivingDocContent() + self.show(hintViewController: ArticleAsLivingDocHintViewController()) + } + + func configureForArticleAsLivingDocResult() { + + guard !loadingArticleContent else { + return + } + + NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(injectArticleAsLivingDocSkeletonIfNeeded), object: nil) + + guard !injectingSkeleton else { + return + } + + injectArticleAsALivingDocument() + } + + + private let userDefaultsKey = "article-as-living-doc-shas" + func getPersistedShaForArticleKey(_ articleKey: String) -> String? { + guard let shas = UserDefaults.standard.dictionary(forKey: userDefaultsKey), + let sha = shas[articleKey] as? String else { + return nil + } + + return sha + } + + func persistShaForArticleKey(_ articleKey: String, sha: String) { + var shas = UserDefaults.standard.dictionary(forKey: userDefaultsKey) ?? [:] + shas[articleKey] = sha + UserDefaults.standard.setValue(shas, forKey: userDefaultsKey) + } + + func injectArticleAsALivingDocument() { + + guard let delegate = delegate, + let articleKey = delegate.article.key else { + return + } + + if let viewModel = articleAsLivingDocViewModel, + shouldShowArticleAsLivingDoc { + let htmlSnippets = viewModel.articleInsertHtmlSnippets + let lastPersistedSha = getPersistedShaForArticleKey(articleKey) + let shouldShowNewChangesBadge = viewModel.sha != nil ? lastPersistedSha != viewModel.sha : false + let topBadgeType: ArticleWebMessagingController.TopBadgeType = shouldShowNewChangesBadge ? .newChanges : .lastUpdated + let timestamp = viewModel.lastUpdatedTimestamp + + delegate.messagingController.injectArticleAsLivingDocContent(articleInsertHtmlSnippets: htmlSnippets, topBadgeType: topBadgeType, timestamp: timestamp) { [weak self, weak delegate] (success) in + + guard let self = self else { + return + } + + if success { + self.hasSkeleton = false + delegate?.updateArticleMargins() + } + + if let sha = viewModel.sha { + self.persistShaForArticleKey(articleKey, sha: sha) + } + + self.toggleContentVisibilityExceptLeadImage(shouldHide: false) + } + } else if shouldAttemptToShowArticleAsLivingDoc { + toggleContentVisibilityExceptLeadImage(shouldHide: false) + } + } + + func presentArticleAsLivingDoc(scrollToInitialIndexPath initialIndexPath: IndexPath? = nil) { + + guard let delegate = delegate else { + return + } + + if articleAsLivingDocViewModel != nil { + articleAsLivingDocViewController = ArticleAsLivingDocViewController(articleTitle: delegate.article.displayTitle, editMetrics: articleAsLivingDocEditMetrics, theme: delegate.theme, delegate: delegate, scrollToInitialIndexPath: initialIndexPath) + articleAsLivingDocViewController?.apply(theme: delegate.theme) + + if let articleAsLivingDocViewController = articleAsLivingDocViewController { + let navigationController = WMFThemeableNavigationController(rootViewController: articleAsLivingDocViewController, theme: delegate.theme) + navigationController.modalPresentationStyle = .pageSheet + navigationController.isNavigationBarHidden = true + surveyLinkState = .inExperimentLoadedEventsDidSeeModal + delegate.extendTimerForPresentingModal() + delegate.present(navigationController, animated: true) + } + } + } + + func toggleContentVisibilityExceptLeadImage(shouldHide: Bool) { + // seems usually thanks to a margin update taking a little bit of time, pushing the + // unhide out a little bit gives us a smoother experience + + guard let delegate = delegate else { + return + } + + let toggleBlock = { [weak delegate] in + + guard let delegate = delegate else { + return + } + + delegate.webView.scrollView.subviews.forEach { (view) in + if view != delegate.leadImageContainerView { + view.isHidden = shouldHide + } + } + } + + if !shouldHide { + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(200)) { + toggleBlock() + } + } else { + toggleBlock() + } + + } + + func handleArticleAsLivingDocLinkForAnchor(_ anchor: String, articleURL: URL) { + guard anchor.contains("significant-events") else { + return + } + + let splitItems = anchor.split(separator: "-") + + if splitItems.count == 4, + splitItems[2] == "username", + let userName = String(splitItems[3]).addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPathAllowed) { + + let href = "./User:\(userName)" + guard let resolvedURL = articleURL.resolvingRelativeWikiHref(href) else { + assertionFailure("Unable to read link as article as a living doc link.") + return + } + + delegate?.navigate(to: resolvedURL) + return + } + + // example: anchor of "significant-events-1-2-3" means scroll to initial index path (item: 1, section: 2) and log ArticleContentInsertEventDescriptionType(rawValue: 3) + guard splitItems.count == 5, + let item = Int(splitItems[2]), + let section = Int(splitItems[3]) + else { + + presentArticleAsLivingDoc() + return + } + + + let indexPath = IndexPath(item: item, section: section) + presentArticleAsLivingDoc(scrollToInitialIndexPath: indexPath) + } + + private func show(hintViewController: HintViewController) { + + guard let delegate = delegate else { + return + } + + let showHint = { + self.hintController = HintController(hintViewController: hintViewController) + self.hintController?.toggle(presenter: delegate, context: nil, theme: delegate.theme) + self.hintController?.setHintHidden(false) + } + if let hintController = self.hintController { + hintController.setHintHidden(true) { + showHint() + } + } else { + showHint() + } + } + + // MARK: Fetcher Methods + private let fetcher = SignificantEventsFetcher() + func fetchArticleAsLivingDocViewModel(rvStartId: UInt? = nil, title: String, siteURL: URL, traitCollection: UITraitCollection, theme: Theme, completion: @escaping ((Result) -> Void)) { + fetcher.fetchSignificantEvents(rvStartId: rvStartId, title: title, siteURL: siteURL) { (result) in + switch result { + case .failure(let error): + DispatchQueue.main.async { + completion(.failure(error)) + } + case .success(let significantEvents): + if let viewModel = ArticleAsLivingDocViewModel(significantEvents: significantEvents, traitCollection: traitCollection, theme: theme) { + DispatchQueue.main.async { + completion(.success(viewModel)) + } + } else { + DispatchQueue.main.async { + completion(.failure(ArticleAsLivingDocController.Errors.viewModelInstantiationFailure)) + } + } + } + } + } + + func fetchEditMetrics(for pageTitle: String, pageURL: URL, completion: @escaping (Result<[NSNumber], Error>) -> Void ) { + fetcher.fetchEditMetrics(for: pageTitle, pageURL: pageURL) { (result) in + DispatchQueue.main.async { + completion(result) + } + } + } + + func fetchNextPage(nextRvStartId: UInt, traitCollection: UITraitCollection, theme: Theme) { + + guard let articleTitleAndSiteURL = self.articleTitleAndSiteURL(), + shouldAttemptToShowArticleAsLivingDoc else { + return + } + + currentFetchRvStartIds.append(nextRvStartId) + + fetchArticleAsLivingDocViewModel(rvStartId: nextRvStartId, title: articleTitleAndSiteURL.title, siteURL: articleTitleAndSiteURL.siteURL, traitCollection: traitCollection, theme: theme) { [weak self] (result) in + guard let self = self else { + return + } + + defer { + self.currentFetchRvStartIds.removeAll(where: {$0 == nextRvStartId}) + if self.currentFetchRvStartIds.isEmpty { + self.articleAsLivingDocViewController?.collectionView.reloadData() + } + } + + switch result { + case .failure(let error): + DDLogDebug("Failure fetching next significant events page \(error)") + case .success(let articleAsLivingDocViewModel): + self.articleAsLivingDocViewModel = articleAsLivingDocViewModel + } + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHeaderView.swift b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHeaderView.swift new file mode 100644 index 0000000..c25c596 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHeaderView.swift @@ -0,0 +1,105 @@ +import UIKit + +class ArticleAsLivingDocHeaderView: UIView { + + @IBOutlet private var headerLabel: UILabel! + @IBOutlet private var titleLabel: UILabel! + @IBOutlet private var summaryLabel: UILabel! + @IBOutlet private var sparklineView: WMFSparklineView! + @IBOutlet var viewFullHistoryButton: ActionButton! + @IBOutlet private var dividerView: UIView! + @IBOutlet private var divHeightConstraint: NSLayoutConstraint! + + private var editMetrics: [NSNumber]? { + didSet { + if shouldShowSparkline { + sparklineView.isHidden = false + sparklineView.dataValues = editMetrics ?? [] + } else { + sparklineView.isHidden = true + } + } + } + + private var shouldShowSparkline: Bool { + guard let editMetrics = editMetrics, + editMetrics.count > 1 else { + return false + } + + return true + } + + private var theme = Theme.standard + + override func awakeFromNib() { + + super.awakeFromNib() + + viewFullHistoryButton.titleLabelFont = .semiboldHeadline + + sparklineView.showsVerticalGridlines = true + + sparklineView.isAccessibilityElement = true + sparklineView.accessibilityLabel = WMFLocalizedString("page-history-graph-accessibility-label", value: "Graph of edits over time", comment: "Accessibility label text used for edits graph") + + titleLabel.numberOfLines = 0 + summaryLabel.numberOfLines = 0 + + viewFullHistoryButton.setTitle(CommonStrings.viewFullHistoryText, for: .normal) + + divHeightConstraint.constant = 1 / UIScreen.main.scale + } + + func configure(headerText: String, titleText: String?, summaryText: String?, editMetrics: [NSNumber]?, theme: Theme) { + self.headerLabel.text = headerText + self.titleLabel.text = titleText + self.summaryLabel.text = summaryText + + self.editMetrics = editMetrics + + updateFonts(with: traitCollection) + } + + // MARK: - Dynamic Type + // Only applies new fonts if the content size category changes + + open override func setNeedsLayout() { + maybeUpdateFonts(with: traitCollection) + super.setNeedsLayout() + } + + override open func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + setNeedsLayout() + } + + var contentSizeCategory: UIContentSizeCategory? + fileprivate func maybeUpdateFonts(with traitCollection: UITraitCollection) { + guard contentSizeCategory == nil || contentSizeCategory != traitCollection.wmf_preferredContentSizeCategory else { + return + } + contentSizeCategory = traitCollection.wmf_preferredContentSizeCategory + updateFonts(with: traitCollection) + } + + func updateFonts(with traitCollection: UITraitCollection) { + headerLabel.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection) + titleLabel.font = UIFont.wmf_font(.mediumTitle1, compatibleWithTraitCollection: traitCollection) + summaryLabel.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection) + viewFullHistoryButton.updateFonts(with: traitCollection) + } +} + +extension ArticleAsLivingDocHeaderView { + func apply(theme: Theme) { + self.theme = theme + backgroundColor = theme.colors.paperBackground + headerLabel.textColor = theme.colors.secondaryText + titleLabel.textColor = theme.colors.primaryText + summaryLabel.textColor = theme.colors.accent + sparklineView.apply(theme: theme) + viewFullHistoryButton.apply(theme: theme) + dividerView.backgroundColor = theme.colors.border + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHeaderView.xib b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHeaderView.xib new file mode 100644 index 0000000..732e900 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHeaderView.xib @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHintViewController.swift b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHintViewController.swift new file mode 100644 index 0000000..7e7c4f3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHintViewController.swift @@ -0,0 +1,15 @@ +import Foundation + +@objc(WMFArticleAsLivingDocHintViewController) +class ArticleAsLivingDocHintViewController: HintViewController { + + override var extendsUnderSafeArea: Bool { + return true + } + + override func configureSubviews() { + viewType = .warning + warningLabel.text = CommonStrings.articleAsLivingDocErrorTitle + warningSubtitleLabel.text = CommonStrings.articleAsLivingDocErrorSubtitle + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHorizontallyScrollingCell.swift b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHorizontallyScrollingCell.swift new file mode 100644 index 0000000..77b269d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocHorizontallyScrollingCell.swift @@ -0,0 +1,77 @@ +import UIKit + +protocol ArticleAsLivingDocHorizontallyScrollingCellDelegate: AnyObject { + func tappedLink(_ url: URL) +} + +class ArticleAsLivingDocHorizontallyScrollingCell: CollectionViewCell { + let descriptionTextView = UITextView() + private var theme: Theme? + + weak var delegate: ArticleAsLivingDocHorizontallyScrollingCellDelegate? + + override func reset() { + super.reset() + descriptionTextView.attributedText = nil + } + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + fatalError("Must override sizeThatFits in subclass") + } + + func configure(change: ArticleAsLivingDocViewModel.Event.Large.ChangeDetail, theme: Theme, delegate: ArticleAsLivingDocHorizontallyScrollingCellDelegate) { + + setupDescription(for: change) + updateFonts(with: traitCollection) + + setNeedsLayout() + + apply(theme: theme) + self.delegate = delegate + } + + func setupDescription(for change: ArticleAsLivingDocViewModel.Event.Large.ChangeDetail) { + switch change { + case .snippet(let snippet): + descriptionTextView.attributedText = snippet.description + case .reference(let reference): + descriptionTextView.attributedText = reference.description + } + } + + override func setup() { + + backgroundView?.layer.cornerRadius = 3 + backgroundView?.layer.masksToBounds = true + selectedBackgroundView?.layer.cornerRadius = 3 + selectedBackgroundView?.layer.masksToBounds = true + layer.shadowOffset = CGSize(width: 0, height: 2) + layer.shadowOpacity = 0.3 + layer.shadowRadius = 3 + + descriptionTextView.isEditable = false + descriptionTextView.isScrollEnabled = false + descriptionTextView.delegate = self + descriptionTextView.textContainer.lineBreakMode = .byTruncatingTail + descriptionTextView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) + contentView.addSubview(descriptionTextView) + super.setup() + } +} + +extension ArticleAsLivingDocHorizontallyScrollingCell: UITextViewDelegate { + func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { + delegate?.tappedLink(URL) + return false + } +} + +extension ArticleAsLivingDocHorizontallyScrollingCell: Themeable { + func apply(theme: Theme) { + self.theme = theme + backgroundColor = .clear + descriptionTextView.backgroundColor = .clear + setBackgroundColors(theme.colors.subCellBackground, selected: theme.colors.midBackground) + layer.shadowColor = theme.colors.cardShadow.cgColor + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocLargeEventCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocLargeEventCollectionViewCell.swift new file mode 100644 index 0000000..6ee7a50 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocLargeEventCollectionViewCell.swift @@ -0,0 +1,421 @@ +import UIKit +import WMF + +// Note: this is an amalgamation of both SideScrollingCollectionViewCell and OnThisDayCollectionViewCell +// We are purposely not repurposing those classes to limit risk +// However if experiment succeeds we should consider reworking SideScrollingCollectionViewCell to accept a generic side scrolling cell to work with, and have this class subclass from there instead. +// Also note, as experiment is EN-only, this class doesn't support RTL +class ArticleAsLivingDocLargeEventCollectionViewCell: CollectionViewCell { + + private var theme: Theme? + + private let descriptionLabel = UILabel() + private let timestampLabel = UILabel() + private let userInfoTextView = UITextView() + private lazy var thankButton: AlignedImageButton = { + let image = UIImage(named: "thank-unfilled") + return actionButton(with: image, text: WMFLocalizedString("aaald-events-thank-title", value: "Thank", comment: "Button title that thanks users for their edit in article as a living document screen")) + }() + private lazy var viewChangesButton: AlignedImageButton = { + let image = UIImage(named: "document") + return actionButton(with: image, text: WMFLocalizedString("aaald-view-changes", value: "View changes", comment: "Button title on a article as a living document cell that sends user to the revision history screen.")) + }() + private lazy var viewDiscussionButton: AlignedImageButton = { + let image = UIImage(named: "document") + return actionButton(with: image, text: WMFLocalizedString("aaald-view-discussion", value: "View discussion", comment: "Button title on an article as a living document cell that sends a user to the event's talk page topic.")) + }() + + let timelineView = TimelineView() + + weak var delegate: ArticleAsLivingDocHorizontallyScrollingCellDelegate? + weak var articleDelegate: ArticleDetailsShowing? + + private var largeEvent: ArticleAsLivingDocViewModel.Event.Large? + private var changeDetails: [ArticleAsLivingDocViewModel.Event.Large.ChangeDetail] = [] + + private var flowLayout: UICollectionViewFlowLayout? { + return collectionView.collectionViewLayout as? UICollectionViewFlowLayout + } + private let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + + private var collectionViewHeight: CGFloat = 0 + + private var isLoggedIn: Bool { + return MWKDataStore.shared().authenticationManager.isLoggedIn + } + + override func setup() { + contentView.addSubview(descriptionLabel) + contentView.addSubview(userInfoTextView) + contentView.addSubview(timestampLabel) + contentView.addSubview(collectionView) + + userInfoTextView.delegate = self + userInfoTextView.isEditable = false + + timelineView.decoration = .singleDot + contentView.insertSubview(timelineView, belowSubview: collectionView) + + wmf_configureSubviewsForDynamicType() + + descriptionLabel.numberOfLines = 0 + flowLayout?.scrollDirection = .horizontal + collectionView.register(ArticleAsLivingDocSnippetCollectionViewCell.self, forCellWithReuseIdentifier: ArticleAsLivingDocSnippetCollectionViewCell.identifier) + collectionView.register(ArticleAsLivingDocReferenceCollectionViewCell.self, forCellWithReuseIdentifier: ArticleAsLivingDocReferenceCollectionViewCell.identifier) + collectionView.dataSource = self + collectionView.delegate = self + collectionView.showsHorizontalScrollIndicator = false + collectionView.showsVerticalScrollIndicator = false + collectionView.alwaysBounceHorizontal = true + + super.setup() + } + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + if traitCollection.horizontalSizeClass == .compact { + layoutMarginsAdditions = UIEdgeInsets(top: 0, left: -5, bottom: 20, right: 0) + } else { + layoutMarginsAdditions = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0) + } + + let layoutMargins = calculatedLayoutMargins + + let timelineTextSpacing: CGFloat = 7 + let timelineWidth: CGFloat = 15 + let x = layoutMargins.left + timelineWidth + timelineTextSpacing + let widthToFit = size.width - layoutMargins.right - x + + if apply { + timelineView.frame = CGRect(x: layoutMargins.left, y: 0, width: timelineWidth, height: size.height) + } + + let timestampOrigin = CGPoint(x: x, y: layoutMargins.top) + let timestampFrame = timestampLabel.wmf_preferredFrame(at: timestampOrigin, maximumSize: CGSize(width: widthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: .forceLeftToRight, apply: apply) + + let timestampDescriptionSpacing: CGFloat = 8 + + let descriptionOrigin = CGPoint(x: x, y: timestampFrame.maxY + timestampDescriptionSpacing) + let descriptionFrame = descriptionLabel.wmf_preferredFrame(at: descriptionOrigin, maximumSize: CGSize(width: widthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: .forceLeftToRight, apply: apply) + + let descriptionCollectionViewSpacing: CGFloat = 10 + + let collectionViewOrigin = CGPoint(x: x, y: descriptionFrame.maxY + descriptionCollectionViewSpacing) + + if let theme = theme, + let largestChangeDetailHeight = largeEvent?.calculateSideScrollingCollectionViewHeightForTraitCollection(traitCollection, theme: theme) { + collectionViewHeight = largestChangeDetailHeight + } else { + collectionViewHeight = 0 + } + let collectionViewItemSpacing: CGFloat = 10 + + if apply { + flowLayout?.minimumInteritemSpacing = collectionViewItemSpacing + flowLayout?.minimumLineSpacing = 15 + flowLayout?.sectionInset = UIEdgeInsets(top: 0, left: x, bottom: 0, right: layoutMargins.right) + collectionView.frame = CGRect(x: 0, y: collectionViewOrigin.y, width: size.width, height: collectionViewHeight) + collectionView.reloadData() + collectionView.layoutIfNeeded() + } + + let collectionViewUserInfoLabelSpacing: CGFloat = 0 + + let userInfoOrigin = CGPoint(x: x, y: descriptionFrame.maxY + collectionViewHeight + descriptionCollectionViewSpacing + collectionViewUserInfoLabelSpacing) + let userInfoFrame = + userInfoTextView.wmf_preferredFrame(at: userInfoOrigin, maximumWidth: widthToFit, alignedBy: .forceLeftToRight, apply: apply) + + guard let largeEvent = largeEvent else { + let finalHeight = userInfoFrame.maxY + layoutMargins.bottom + return CGSize(width: size.width, height: finalHeight) + } + + let userInfoButtonsSpacing: CGFloat = 6 + let buttonsSpacing: CGFloat = 20 + var finalHeight = userInfoFrame.maxY + switch largeEvent.buttonsToDisplay { + case .thankAndViewChanges: + + // Note: AlignedImageButton overrides wmf_preferredFrame and adjusts the frame origin x value by the leftPadding amount. I'm not sure why it does this but to prevent risk with tinkering on a widespread view we are resetting that offset here. + let thankXOffset = thankButton.leftPadding + let thankOrigin = CGPoint(x: x + thankXOffset, y: userInfoFrame.maxY + userInfoButtonsSpacing) + + let thankFrame = thankButton.wmf_preferredFrame(at: thankOrigin, maximumWidth: widthToFit, horizontalAlignment: .left, apply: apply) + + let viewChangesOrigin = CGPoint(x: thankFrame.maxX + buttonsSpacing, y: userInfoFrame.maxY + userInfoButtonsSpacing) + viewChangesButton.wmf_preferredFrame(at: viewChangesOrigin, maximumWidth: widthToFit, horizontalAlignment: .left, apply: apply) + finalHeight = thankFrame.maxY + thankButton.cornerRadius = thankFrame.height / 2 + viewChangesButton.cornerRadius = thankFrame.height / 2 + case .viewDiscussion: + + // Note: See above note on thankXOffset for reasons for this offset. + let viewDiscussionXOffset = viewDiscussionButton.leftPadding + let viewDiscussionOrigin = CGPoint(x: x + viewDiscussionXOffset, y: userInfoFrame.maxY + userInfoButtonsSpacing) + let viewDiscussionFrame = viewDiscussionButton.wmf_preferredFrame(at: viewDiscussionOrigin, maximumWidth: widthToFit, horizontalAlignment: .left, apply: apply) + finalHeight = viewDiscussionFrame.maxY + viewDiscussionButton.cornerRadius = viewDiscussionFrame.height / 2 + } + + let finalFinalHeight = finalHeight + layoutMargins.bottom + return CGSize(width: size.width, height: finalFinalHeight) + } + + private func actionButton(with image: UIImage?, text: String) -> AlignedImageButton { + let button = AlignedImageButton() + button.setImage(image, for: .normal) + button.titleLabel?.numberOfLines = 1 + button.titleLabel?.textAlignment = .left + button.horizontalSpacing = 6 + button.verticalPadding = 2 + button.leftPadding = 10 + button.rightPadding = 10 + button.titleLabel?.setContentCompressionResistancePriority(.required, for: .horizontal) + button.setTitle(text, for: .normal) + return button + } + + override func layoutSublayers(of layer: CALayer) { + super.layoutSublayers(of: layer) + timelineView.dotsY = timestampLabel.convert(timestampLabel.bounds, to: timelineView).midY + } + + func setAttributedStringViews() { + + guard let largeEvent = largeEvent, + let theme = theme else { + return + } + + userInfoTextView.attributedText = largeEvent.userInfoForTraitCollection(traitCollection, theme: theme) + + changeDetails = largeEvent.changeDetailsForTraitCollection(traitCollection, theme: theme) + collectionView.reloadData() + } + + override public func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + + if let theme = theme { + largeEvent?.resetAttributedStringsIfNeededWithTraitCollection(traitCollection, theme: theme) + } + + setAttributedStringViews() + + timestampLabel.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection) + thankButton.titleLabel?.font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + viewChangesButton.titleLabel?.font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + viewDiscussionButton.titleLabel?.font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + } + + func apply(theme: Theme) { + self.theme = theme + + largeEvent?.resetAttributedStringsIfNeededWithTraitCollection(traitCollection, theme: theme) + + timelineView.backgroundColor = theme.colors.paperBackground + timelineView.tintColor = theme.colors.accent + timestampLabel.textColor = theme.colors.accent + userInfoTextView.backgroundColor = theme.colors.paperBackground + descriptionLabel.textColor = theme.colors.primaryText + + if let largeEvent = largeEvent { + switch largeEvent.buttonsToDisplay { + case .thankAndViewChanges: + thankButton.backgroundColor = largeEvent.wereThanksSent ? theme.colors.cardButtonSelectedBackground : theme.colors.cardButtonBackground + thankButton.setTitleColor(!isLoggedIn ? theme.colors.disabledLink : theme.colors.link, for: .normal) + thankButton.tintColor = (!isLoggedIn ? theme.colors.disabledLink : theme.colors.link) + + viewChangesButton.backgroundColor = theme.colors.cardButtonBackground + viewChangesButton.setTitleColor(theme.colors.link, for: .normal) + case .viewDiscussion: + viewDiscussionButton.backgroundColor = theme.colors.cardButtonBackground + viewDiscussionButton.setTitleColor(theme.colors.link, for: .normal) + } + } + + collectionView.backgroundColor = .clear + + setAttributedStringViews() + } + + override func reset() { + super.reset() + + largeEvent = nil + changeDetails.removeAll() + collectionView.reloadData() + descriptionLabel.attributedText = nil + userInfoTextView.attributedText = nil + timestampLabel.text = nil + thankButton.removeFromSuperview() + viewChangesButton.removeFromSuperview() + viewDiscussionButton.removeFromSuperview() + collectionViewHeight = 0 + } + + func resetContentOffset() { + let x: CGFloat = -collectionView.contentInset.left + collectionView.contentOffset = CGPoint(x: x, y: 0) + } + + func configure(with largeEvent: ArticleAsLivingDocViewModel.Event.Large, theme: Theme, extendTimelineAboveDot: Bool? = nil) { + + self.largeEvent = largeEvent + self.largeEvent?.resetAttributedStringsIfNeededWithTraitCollection(traitCollection, theme: theme) + apply(theme: theme) + + timestampLabel.text = largeEvent.timestampForDisplay() + + if let extendTimelineAboveDot = extendTimelineAboveDot { + timelineView.extendTimelineAboveDot = extendTimelineAboveDot + } + + switch largeEvent.buttonsToDisplay { + case .thankAndViewChanges: + contentView.addSubview(thankButton) + contentView.addSubview(viewChangesButton) + + thankButton.titleLabel?.font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + viewChangesButton.titleLabel?.font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + + if largeEvent.wereThanksSent { + thankButton.setImage(UIImage(named: "thank"), for: .normal) + thankButton.setTitle(WMFLocalizedString("aaald-events-thanked-title", value: "Thanked", comment: "Button title after a user thanks an editor - past tense of 'thank'"), for: .normal) + } else { + thankButton.setImage(UIImage(named: "thank-unfilled"), for: .normal) + thankButton.setTitle(WMFLocalizedString("aaald-events-thank-title", value: "Thank", comment: "Button title that thanks users for their edit in article as a living document screen"), for: .normal) + } + + thankButton.setNeedsLayout() + thankButton.layoutIfNeeded() + viewChangesButton.setNeedsLayout() + viewChangesButton.layoutIfNeeded() + + thankButton.removeTarget(nil, action: nil, for: .allEvents) + thankButton.addTarget(self, action: #selector(thankButtonTapped), for: .touchUpInside) + + viewChangesButton.removeTarget(nil, action: nil, for: .allEvents) + viewChangesButton.addTarget(self, action: #selector(viewChangesTapped), for: .touchUpInside) + case .viewDiscussion: + + contentView.addSubview(viewDiscussionButton) + + viewDiscussionButton.setNeedsLayout() + viewDiscussionButton.layoutIfNeeded() + + viewDiscussionButton.removeTarget(nil, action: nil, for: .allEvents) + viewDiscussionButton.addTarget(self, action: #selector(viewDiscussionTapped), for: .touchUpInside) + } + + setAttributedStringViews() + resetContentOffset() + setNeedsLayout() + } + + @objc private func thankButtonTapped() { + guard let largeEvent = largeEvent else { + return + } + let isUserAnonymous = (largeEvent.userType == .anonymous) + + articleDelegate?.thankButtonTapped(for: Int(largeEvent.revId), isUserAnonymous: isUserAnonymous) + } + + @objc private func viewChangesTapped() { + + guard let largeEvent = largeEvent else { + return + } + + articleDelegate?.goToDiff(revisionId: largeEvent.revId, parentId: largeEvent.parentId, diffType: .single) + + } + + @objc private func viewDiscussionTapped() { + + guard let largeEvent = largeEvent else { + return + } + + switch largeEvent.buttonsToDisplay { + case .viewDiscussion(let sectionName): + articleDelegate?.showTalkPageWithSectionName(sectionName) + default: + assertionFailure("Unexpected button type") + articleDelegate?.showTalkPageWithSectionName(nil) + } + } +} + +extension ArticleAsLivingDocLargeEventCollectionViewCell: UICollectionViewDataSource { + public func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } + + public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return changeDetails.count + } + + public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard indexPath.item < changeDetails.count, + let theme = theme else { + return UICollectionViewCell() + } + + let changeDetailForCell = changeDetails[indexPath.item] + + switch changeDetailForCell { + case .snippet: + + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ArticleAsLivingDocSnippetCollectionViewCell.identifier, for: indexPath) + + guard let snippetCell = cell as? ArticleAsLivingDocSnippetCollectionViewCell else { + return cell + } + + snippetCell.configure(change: changeDetailForCell, theme: theme, delegate: self) + return snippetCell + + case .reference: + + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ArticleAsLivingDocReferenceCollectionViewCell.identifier, for: indexPath) + + guard let referenceCell = cell as? ArticleAsLivingDocReferenceCollectionViewCell else { + return cell + } + + referenceCell.configure(change: changeDetailForCell, theme: theme, delegate: self) + return referenceCell + } + } +} + +extension ArticleAsLivingDocLargeEventCollectionViewCell: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + + return CGSize(width: ArticleAsLivingDocViewModel.Event.Large.sideScrollingCellWidth, height: collectionViewHeight - ArticleAsLivingDocViewModel.Event.Large.additionalPointsForShadow) + } +} + +extension ArticleAsLivingDocLargeEventCollectionViewCell: ArticleAsLivingDocHorizontallyScrollingCellDelegate { + func tappedLink(_ url: URL) { + + guard largeEvent != nil else { + return + } + + delegate?.tappedLink(url) + } +} + +extension ArticleAsLivingDocLargeEventCollectionViewCell: UITextViewDelegate { + func textView(_ textView: UITextView, shouldInteractWith url: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { + + guard largeEvent != nil else { + return false + } + + delegate?.tappedLink(url) + return false + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocReferenceCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocReferenceCollectionViewCell.swift new file mode 100644 index 0000000..cc38045 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocReferenceCollectionViewCell.swift @@ -0,0 +1,98 @@ +import UIKit + +class ArticleAsLivingDocReferenceCollectionViewCell: ArticleAsLivingDocHorizontallyScrollingCell { + + private let titleLabel = UILabel() + private var reference: ArticleAsLivingDocViewModel.Event.Large.Reference? = nil + private var iconTitleBadge: IconTitleBadge? + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + + guard apply else { + return size + } + + let adjustedMargins = ArticleAsLivingDocViewModel.Event.Large.sideScrollingCellPadding + + var availableTitleWidth = size.width - adjustedMargins.left - adjustedMargins.right + let availableDescriptionWidth = availableTitleWidth + + if let iconTitleBadge = iconTitleBadge { + let maximumBadgeWidth = min(size.width - adjustedMargins.right - adjustedMargins.left, size.width / 3) + let iconBadgeOrigin = CGPoint(x: size.width - adjustedMargins.right, y: adjustedMargins.top) + let iconBadgeFrame = iconTitleBadge.wmf_preferredFrame(at: iconBadgeOrigin, maximumWidth: maximumBadgeWidth, alignedBy: .forceLeftToRight, apply: false) + let iconTitleBadgeX = size.width - adjustedMargins.right - iconBadgeFrame.width + iconTitleBadge.frame = CGRect(x: iconTitleBadgeX, y: iconBadgeFrame.minY, width: iconBadgeFrame.width, height: iconBadgeFrame.height) + let titleBadgeSpacing: CGFloat = 10 + availableTitleWidth -= iconBadgeFrame.width + titleBadgeSpacing + } + + let titleOrigin = CGPoint(x: adjustedMargins.left, y: adjustedMargins.top) + let titleFrame = titleLabel.wmf_preferredFrame(at: titleOrigin, maximumWidth: availableTitleWidth, alignedBy: .forceLeftToRight, apply: apply) + + let titleDescriptionSpacing = ArticleAsLivingDocViewModel.Event.Large.changeDetailReferenceTitleDescriptionSpacing + let descriptionOrigin = CGPoint(x: adjustedMargins.left, y: titleFrame.maxY + titleDescriptionSpacing) + + descriptionTextView.wmf_preferredFrame(at: descriptionOrigin, maximumWidth: availableDescriptionWidth, alignedBy: .forceLeftToRight, apply: apply) + + layer.shadowPath = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: backgroundView?.layer.cornerRadius ?? 0).cgPath + + return size + } + + override func setup() { + + super.setup() + descriptionTextView.textContainer.maximumNumberOfLines = 0 + contentView.addSubview(titleLabel) + // adding icon badge in configure + } + + override func reset() { + super.reset() + iconTitleBadge?.removeFromSuperview() + iconTitleBadge = nil + titleLabel.text = nil + } + + private func createIconTitleBadgeForReference(reference: ArticleAsLivingDocViewModel.Event.Large.Reference) { + guard let year = reference.accessDateYearDisplay else { + return + } + + let configuration = IconTitleBadge.Configuration(title: year, icon: .sfSymbol(name: "clock.fill")) + let iconTitleBadge = IconTitleBadge(configuration: configuration, frame: .zero) + contentView.addSubview(iconTitleBadge) + self.iconTitleBadge = iconTitleBadge + } + + override func configure(change: ArticleAsLivingDocViewModel.Event.Large.ChangeDetail, theme: Theme, delegate: ArticleAsLivingDocHorizontallyScrollingCellDelegate) { + + super.configure(change: change, theme: theme, delegate: delegate) + + switch change { + case .reference(let reference): + self.reference = reference + createIconTitleBadgeForReference(reference: reference) + titleLabel.text = reference.type + default: + assertionFailure("ArticleAsLivingDocReferenceCollectionViewCell configured with unexpected type") + return + } + + updateFonts(with: traitCollection) + apply(theme: theme) + } + + override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + titleLabel.font = UIFont.wmf_font(ArticleAsLivingDocViewModel.Event.Large.changeDetailReferenceTitleStyle, compatibleWithTraitCollection: traitCollection) + iconTitleBadge?.updateFonts(with: traitCollection) + } + + override func apply(theme: Theme) { + super.apply(theme: theme) + titleLabel.textColor = theme.colors.secondaryText + iconTitleBadge?.apply(theme: theme) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocSectionHeaderView.swift b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocSectionHeaderView.swift new file mode 100644 index 0000000..ce2a55c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocSectionHeaderView.swift @@ -0,0 +1,62 @@ +import UIKit + +class ArticleAsLivingDocSectionHeaderView: SizeThatFitsReusableView { + private let titleLabel = UILabel() + private let subtitleLabel = UILabel() + private var theme: Theme? + + override func setup() { + addSubview(titleLabel) + titleLabel.numberOfLines = 1 + addSubview(subtitleLabel) + subtitleLabel.numberOfLines = 1 + super.setup() + } + + override func reset() { + super.reset() + titleLabel.text = nil + subtitleLabel.text = nil + } + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let adjustedMargins = UIEdgeInsets(top: layoutMargins.top + 24, left: layoutMargins.left, bottom: layoutMargins.bottom + 12, right: layoutMargins.right) + + let maximumWidth = size.width - adjustedMargins.left - adjustedMargins.right + + let titleOrigin = CGPoint(x: adjustedMargins.left, y: adjustedMargins.top) + let titleFrame = titleLabel.wmf_preferredFrame(at: titleOrigin, maximumSize: CGSize(width: maximumWidth, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: .forceLeftToRight, apply: apply) + + let titleSubtitleSpacing: CGFloat = 7 + let subtitleOrigin = CGPoint(x: adjustedMargins.left, y: titleFrame.maxY + titleSubtitleSpacing) + let subtitleFrame = subtitleLabel.wmf_preferredFrame(at: subtitleOrigin, maximumSize: CGSize(width: maximumWidth, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: .forceLeftToRight, apply: apply) + + let finalHeight = subtitleFrame.maxY + adjustedMargins.bottom + + return CGSize(width: size.width, height: finalHeight) + } + + func configure(viewModel: ArticleAsLivingDocViewModel.SectionHeader, theme: Theme) { + + self.titleLabel.text = viewModel.title + self.subtitleLabel.text = viewModel.subtitleTimestampDisplay + setNeedsLayout() + + apply(theme: theme) + } + + override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + titleLabel.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection) + subtitleLabel.font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection) + setNeedsLayout() + } +} + +extension ArticleAsLivingDocSectionHeaderView: Themeable { + func apply(theme: Theme) { + self.theme = theme + titleLabel.textColor = theme.colors.primaryText + subtitleLabel.textColor = theme.colors.secondaryText + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocSmallEventCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocSmallEventCollectionViewCell.swift new file mode 100644 index 0000000..8134d46 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocSmallEventCollectionViewCell.swift @@ -0,0 +1,103 @@ +import UIKit + +class ArticleAsLivingDocSmallEventCollectionViewCell: CollectionViewCell { + private let descriptionLabel = UILabel() + let timelineView = TimelineView() + + private var theme: Theme? + + private var smallEvent: ArticleAsLivingDocViewModel.Event.Small? + + weak var delegate: ArticleDetailsShowing? + + override func reset() { + super.reset() + descriptionLabel.text = nil + } + + override func setup() { + super.setup() + contentView.addSubview(descriptionLabel) + timelineView.decoration = .squiggle + contentView.addSubview(timelineView) + + descriptionLabel.numberOfLines = 1 + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tappedSmallChanges)) + descriptionLabel.addGestureRecognizer(tapGestureRecognizer) + descriptionLabel.isUserInteractionEnabled = true + } + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + + if traitCollection.horizontalSizeClass == .compact { + layoutMarginsAdditions = UIEdgeInsets(top: 0, left: -5, bottom: 20, right: 0) + } else { + layoutMarginsAdditions = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0) + } + + let layoutMargins = calculatedLayoutMargins + + let timelineTextSpacing: CGFloat = 5 + let timelineWidth: CGFloat = 15 + let x = layoutMargins.left + timelineWidth + timelineTextSpacing + let widthToFit = size.width - layoutMargins.right - x + + if apply { + timelineView.frame = CGRect(x: layoutMargins.left, y: 0, width: timelineWidth, height: size.height) + } + + let descriptionOrigin = CGPoint(x: x + 3, y: layoutMargins.top) + + let descriptionFrame = descriptionLabel.wmf_preferredFrame(at: descriptionOrigin, maximumSize: CGSize(width: widthToFit, height: UIView.noIntrinsicMetric), minimumSize: NoIntrinsicSize, alignedBy: .forceLeftToRight, apply: apply) + + let finalHeight = descriptionFrame.maxY + layoutMargins.bottom + + return CGSize(width: size.width, height: finalHeight) + } + + func configure(viewModel: ArticleAsLivingDocViewModel.Event.Small, theme: Theme) { + + self.smallEvent = viewModel + descriptionLabel.text = viewModel.eventDescription + apply(theme: theme) + setNeedsLayout() + } + + override func layoutSublayers(of layer: CALayer) { + super.layoutSublayers(of: layer) + timelineView.dotsY = descriptionLabel.convert(descriptionLabel.bounds, to: timelineView).midY + } + + override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + + descriptionLabel.font = UIFont.wmf_font(.italicSubheadline, compatibleWithTraitCollection: traitCollection) + } + + @objc private func tappedSmallChanges() { + guard let revisionID = smallEvent?.smallChanges.first?.revId, + let parentId = smallEvent?.smallChanges.last?.parentId else { + return + } + + let diffType: DiffContainerViewModel.DiffType = (smallEvent?.smallChanges.count ?? 0) > 1 ? .compare : .single + + delegate?.goToDiff(revisionId: revisionID, parentId: parentId, diffType: diffType) + } +} + +extension ArticleAsLivingDocSmallEventCollectionViewCell: Themeable { + func apply(theme: Theme) { + + if let oldTheme = self.theme, + theme.webName == oldTheme.webName { + return + } + + self.theme = theme + + descriptionLabel.textColor = theme.colors.link + timelineView.backgroundColor = theme.colors.paperBackground + timelineView.tintColor = theme.colors.accent + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocSnippetCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocSnippetCollectionViewCell.swift new file mode 100644 index 0000000..0e1d860 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocSnippetCollectionViewCell.swift @@ -0,0 +1,23 @@ +import UIKit + +class ArticleAsLivingDocSnippetCollectionViewCell: ArticleAsLivingDocHorizontallyScrollingCell { + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + + guard apply else { + return size + } + + let adjustedMargins = ArticleAsLivingDocViewModel.Event.Large.sideScrollingCellPadding + + let descriptionOrigin = CGPoint(x: adjustedMargins.left, y: adjustedMargins.top) + let descriptionMaximumWidth: CGFloat = (size.width - adjustedMargins.right) - descriptionOrigin.x + let descriptionMaximumHeight: CGFloat = size.height - adjustedMargins.top - adjustedMargins.bottom + 5 // little bit of extra space needed for snippet height here so snippets aren't inexplicably cut off. + + descriptionTextView.wmf_preferredFrame(at: descriptionOrigin, maximumSize: CGSize(width: descriptionMaximumWidth, height: descriptionMaximumHeight), alignedBy: .forceLeftToRight, apply: apply) + + layer.shadowPath = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: backgroundView?.layer.cornerRadius ?? 0).cgPath + + return size + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocViewController.swift b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocViewController.swift new file mode 100644 index 0000000..6943bdb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Article as a Living Document/ArticleAsLivingDocViewController.swift @@ -0,0 +1,532 @@ +import UIKit +import WMF + +protocol ArticleAsLivingDocViewControllerDelegate: AnyObject { + var articleAsLivingDocViewModel: ArticleAsLivingDocViewModel? { get } + var articleURL: URL { get } + var isFetchingAdditionalPages: Bool { get } + func fetchNextPage(nextRvStartId: UInt, theme: Theme) + func showEditHistory() + func handleLink(with href: String) + func livingDocViewWillAppear() + func livingDocViewWillPush() +} + +protocol ArticleDetailsShowing: AnyObject { + func goToHistory() + func goToDiff(revisionId: UInt, parentId: UInt, diffType: DiffContainerViewModel.DiffType) + func showTalkPageWithSectionName(_ sectionName: String?) + func thankButtonTapped(for revisionID: Int, isUserAnonymous: Bool) +} + +class ArticleAsLivingDocViewController: ColumnarCollectionViewController { + + private let articleTitle: String? + private var headerView: ArticleAsLivingDocHeaderView? + private let headerText = WMFLocalizedString("aaald-header-text", value: "Recent Changes", comment: "Header text of article as a living document view.") + private let editMetrics: [NSNumber]? + private weak var delegate: ArticleAsLivingDocViewControllerDelegate? + + private lazy var dataSource: UICollectionViewDiffableDataSource = createDataSource() + private var initialIndexPath: IndexPath? + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) not supported") + } + + required init?(articleTitle: String?, editMetrics: [NSNumber]?, theme: Theme, locale: Locale = Locale.current, delegate: ArticleAsLivingDocViewControllerDelegate, scrollToInitialIndexPath initialIndexPath: IndexPath?) { + + guard delegate.articleAsLivingDocViewModel != nil else { + return nil + } + + self.articleTitle = articleTitle + self.editMetrics = editMetrics + super.init() + self.theme = theme + self.delegate = delegate + self.initialIndexPath = initialIndexPath + footerButtonTitle = CommonStrings.viewFullHistoryText + } + + override func viewDidLoad() { + super.viewDidLoad() + + layoutManager.register(ArticleAsLivingDocLargeEventCollectionViewCell.self, forCellWithReuseIdentifier: ArticleAsLivingDocLargeEventCollectionViewCell.identifier, addPlaceholder: true) + layoutManager.register(ArticleAsLivingDocSmallEventCollectionViewCell.self, forCellWithReuseIdentifier: ArticleAsLivingDocSmallEventCollectionViewCell.identifier, addPlaceholder: true) + layoutManager.register(ArticleAsLivingDocSectionHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: ArticleAsLivingDocSectionHeaderView.identifier, addPlaceholder: true) + layoutManager.register(ActivityIndicatorCollectionViewFooter.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: ActivityIndicatorCollectionViewFooter.identifier, addPlaceholder: false) + + self.title = headerText + + setupNavigationBar() + + if let viewModel = delegate?.articleAsLivingDocViewModel { + addInitialSections(sections: viewModel.sections) + } + } + + override func viewWillAppear(_ animated: Bool) { + + // for some reason the initial calls to metrics(with size: CGSize...) (triggered from viewDidLoad) have an incorrect view size passed in. + // this retriggers that method with the correct size, so that we have correct layout margins on load + if isFirstAppearance { + collectionView.reloadData() + } + super.viewWillAppear(animated) + + delegate?.livingDocViewWillAppear() + } + + private func createDataSource() -> UICollectionViewDiffableDataSource { + let dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { (collectionView: UICollectionView, indexPath: IndexPath, event: ArticleAsLivingDocViewModel.TypedEvent) -> UICollectionViewCell? in + + let theme = self.theme + let cell: CollectionViewCell + switch event { + case .large(let largeEvent): + guard let largeEventCell = collectionView.dequeueReusableCell(withReuseIdentifier: ArticleAsLivingDocLargeEventCollectionViewCell.identifier, for: indexPath) as? ArticleAsLivingDocLargeEventCollectionViewCell else { + return nil + } + + largeEventCell.configure(with: largeEvent, theme: theme, extendTimelineAboveDot: indexPath.item != 0) + largeEventCell.delegate = self + largeEventCell.articleDelegate = self + cell = largeEventCell + case .small(let smallEvent): + guard let smallEventCell = collectionView.dequeueReusableCell(withReuseIdentifier: ArticleAsLivingDocSmallEventCollectionViewCell.identifier, for: indexPath) as? ArticleAsLivingDocSmallEventCollectionViewCell else { + return nil + } + + smallEventCell.configure(viewModel: smallEvent, theme: theme) + smallEventCell.delegate = self + cell = smallEventCell + } + + if let layout = collectionView.collectionViewLayout as? ColumnarCollectionViewLayout { + cell.layoutMargins = layout.itemLayoutMargins + } + + return cell + + } + + + dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in + + guard kind == UICollectionView.elementKindSectionHeader || kind == UICollectionView.elementKindSectionFooter else { + return UICollectionReusableView() + } + + let theme = self.theme + + if kind == UICollectionView.elementKindSectionHeader { + let section = self.dataSource.snapshot() + .sectionIdentifiers[indexPath.section] + + guard let sectionHeaderView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: ArticleAsLivingDocSectionHeaderView.identifier, for: indexPath) as? ArticleAsLivingDocSectionHeaderView else { + return UICollectionReusableView() + } + + sectionHeaderView.layoutMargins = self.layout.itemLayoutMargins + sectionHeaderView.configure(viewModel: section, theme: theme) + return sectionHeaderView + } else if kind == UICollectionView.elementKindSectionFooter { + if self.delegate?.isFetchingAdditionalPages == true, + let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: ActivityIndicatorCollectionViewFooter.identifier, for: indexPath) as? ActivityIndicatorCollectionViewFooter { + footer.apply(theme: theme) + return footer + } + guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CollectionViewFooter.identifier, for: indexPath) as? CollectionViewFooter else { + return UICollectionReusableView() + } + self.configure(footer: footer, forSectionAt: indexPath.section, layoutOnly: false) + return footer + } + + return UICollectionReusableView() + } + + return dataSource + } + + private func updateLoggingPositionsForItemsInSections(_ sections: [ArticleAsLivingDocViewModel.SectionHeader]) { + var currentPosition: Int = 0 + for section in sections { + for item in section.typedEvents { + switch item { + case .large(let largeEvent): + + if largeEvent.loggingPosition == 0 { + largeEvent.loggingPosition = currentPosition + } + + case .small(let smallEvent): + if smallEvent.loggingPosition == 0 { + smallEvent.loggingPosition = currentPosition + } + } + + currentPosition = currentPosition + 1 + } + } + } + + func addInitialSections(sections: [ArticleAsLivingDocViewModel.SectionHeader]) { + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections(sections) + for section in sections { + snapshot.appendItems(section.typedEvents, toSection: section) + } + + updateLoggingPositionsForItemsInSections(snapshot.sectionIdentifiers) + dataSource.apply(snapshot, animatingDifferences: true) { + self.scrollToInitialIndexPathIfNeeded() + } + } + + func scrollToInitialIndexPathIfNeeded() { + guard let initialIndexPath = initialIndexPath else { + return + } + + collectionView.scrollToItem(at: initialIndexPath, at: .top, animated: true) + } + + func appendSections(_ sections: [ArticleAsLivingDocViewModel.SectionHeader]) { + guard let dayMonthNumberYearDateFormatter = DateFormatter.wmf_monthNameDayOfMonthNumberYear(), let isoDateFormatter = DateFormatter.wmf_iso8601() else { + return + } + + var currentSnapshot = dataSource.snapshot() + var existingSections: [ArticleAsLivingDocViewModel.SectionHeader] = [] + + for currentSection in currentSnapshot.sectionIdentifiers { + for proposedSection in sections { + if currentSection == proposedSection { + if + let lastCurrentEvent = currentSection.typedEvents.last, lastCurrentEvent.isSmall, + let firstProposedEvent = proposedSection.typedEvents.first, firstProposedEvent.isSmall { + // Collapse sequential small events if appending to the same section + let smallChanges = lastCurrentEvent.smallChanges + firstProposedEvent.smallChanges + let collapsedSmallEvent = ArticleAsLivingDocViewModel.TypedEvent.small(.init(smallChanges: smallChanges)) + + let proposedSectionCollapsed = proposedSection + let currentSectionCollapsed = currentSection + + proposedSectionCollapsed.typedEvents.removeFirst() + currentSectionCollapsed.typedEvents.removeLast() + + currentSnapshot.deleteItems([lastCurrentEvent]) + currentSnapshot.appendItems([collapsedSmallEvent], toSection: currentSection) + currentSnapshot.appendItems(proposedSectionCollapsed.typedEvents, toSection: currentSection) + existingSections.append(proposedSectionCollapsed) + } else { + currentSnapshot.appendItems(proposedSection.typedEvents, toSection: currentSection) + existingSections.append(proposedSection) + } + } + } + } + + // Collapse first proposed section into last current section if both only contain small events + if + let lastCurrentSection = currentSnapshot.sectionIdentifiers.last, lastCurrentSection.containsOnlySmallEvents, + let firstProposedSection = sections.first, firstProposedSection.containsOnlySmallEvents { + + let smallChanges = lastCurrentSection.typedEvents.flatMap { $0.smallChanges } + firstProposedSection.typedEvents.flatMap { $0.smallChanges } + let collapsedSmallEvent = ArticleAsLivingDocViewModel.Event.Small(smallChanges: smallChanges) + let smallTypedEvent = ArticleAsLivingDocViewModel.TypedEvent.small(collapsedSmallEvent) + + let smallChangeDates = smallChanges.compactMap { isoDateFormatter.date(from: $0.timestampString) } + var dateRange: DateInterval? + if let minDate = smallChangeDates.min(), let maxDate = smallChangeDates.max() { + dateRange = DateInterval(start: minDate, end: maxDate) + } + + let collapsedSection = ArticleAsLivingDocViewModel.SectionHeader(timestamp: lastCurrentSection.timestamp, typedEvents: [smallTypedEvent], subtitleDateFormatter: dayMonthNumberYearDateFormatter, dateRange: dateRange) + + currentSnapshot.deleteItems(lastCurrentSection.typedEvents) + currentSnapshot.insertSections([collapsedSection], afterSection: lastCurrentSection) + currentSnapshot.deleteSections([lastCurrentSection]) + currentSnapshot.appendItems(collapsedSection.typedEvents, toSection: collapsedSection) + existingSections.append(lastCurrentSection) + existingSections.append(firstProposedSection) + } + + // Appending remaining new sections to the snapshot + for section in sections { + if !existingSections.contains(section) { + currentSnapshot.appendSections([section]) + currentSnapshot.appendItems(section.typedEvents, toSection: section) + } + } + + updateLoggingPositionsForItemsInSections(currentSnapshot.sectionIdentifiers) + dataSource.apply(currentSnapshot, animatingDifferences: true) + } + + private func setupNavigationBar() { + navigationItem.rightBarButtonItem = UIBarButtonItem(title: WMFLocalizedString("close-button", value: "Close", comment: "Close button used in navigation bar that closes out a presented modal screen."), style: .done, target: self, action: #selector(closeButtonPressed)) + + navigationMode = .forceBar + if let headerView = ArticleAsLivingDocHeaderView.wmf_viewFromClassNib() { + self.headerView = headerView + configureHeaderView(headerView) + navigationBar.isBarHidingEnabled = false + navigationBar.isUnderBarViewHidingEnabled = true + navigationBar.isUnderBarFadingEnabled = false + navigationBar.addUnderNavigationBarView(headerView) + navigationBar.needsUnderBarHack = true + navigationBar.underBarViewPercentHiddenForShowingTitle = 0.6 + navigationBar.title = headerText + navigationBar.setNeedsLayout() + navigationBar.layoutIfNeeded() + updateScrollViewInsets() + } + } + + @objc private func closeButtonPressed() { + dismiss(animated: true, completion: nil) + } + + override func metrics(with size: CGSize, readableWidth: CGFloat, layoutMargins: UIEdgeInsets) -> ColumnarCollectionViewLayoutMetrics { + return ColumnarCollectionViewLayoutMetrics.tableViewMetrics(with: size, readableWidth: readableWidth, layoutMargins: layoutMargins) + } + + private func configureHeaderView(_ headerView: ArticleAsLivingDocHeaderView) { + + guard let articleAsLivingDocViewModel = delegate?.articleAsLivingDocViewModel else { + return + } + + let headerText = self.headerText.uppercased(with: NSLocale.current) + headerView.configure(headerText: headerText, titleText: articleTitle, summaryText: articleAsLivingDocViewModel.summaryText, editMetrics: editMetrics, theme: theme) + headerView.apply(theme: theme) + + headerView.viewFullHistoryButton.addTarget(self, action: #selector(tappedViewFullHistoryButtonInHeader), for: .touchUpInside) + } + + override func apply(theme: Theme) { + guard isViewLoaded else { + return + } + + super.apply(theme: theme) + navigationItem.rightBarButtonItem?.tintColor = theme.colors.link + navigationController?.navigationBar.barTintColor = theme.colors.cardButtonBackground + headerView?.apply(theme: theme) + } + + @objc func tappedViewFullHistoryButtonInFooter() { + + tappedViewFullHistoryButton() + } + + @objc func tappedViewFullHistoryButtonInHeader() { + + tappedViewFullHistoryButton() + } + + private func tappedViewFullHistoryButton() { + self.goToHistory() + } + + // MARK: - CollectionView functions + override func collectionView(_ collectionView: UICollectionView, estimatedHeightForHeaderInSection section: Int, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + + var estimate = ColumnarCollectionViewLayoutHeightEstimate(precalculated: false, height: 70) + + let section = self.dataSource.snapshot() + .sectionIdentifiers[section] + + guard let sectionHeaderView = layoutManager.placeholder(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: ArticleAsLivingDocSectionHeaderView.identifier) as? ArticleAsLivingDocSectionHeaderView else { + return estimate + } + + sectionHeaderView.configure(viewModel: section, theme: theme) + + estimate.height = sectionHeaderView.sizeThatFits(CGSize(width: columnWidth, height: UIView.noIntrinsicMetric), apply: false).height + estimate.precalculated = true + return estimate + } + + override func collectionView(_ collectionView: UICollectionView, estimatedHeightForItemAt indexPath: IndexPath, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + var estimate = ColumnarCollectionViewLayoutHeightEstimate(precalculated: false, height: 350) + + guard let event = dataSource.itemIdentifier(for: indexPath) else { + return estimate + } + + let cell: CollectionViewCell + switch event { + case .large(let largeEvent): + guard let largeEventCell = layoutManager.placeholder(forCellWithReuseIdentifier: ArticleAsLivingDocLargeEventCollectionViewCell.identifier) as? ArticleAsLivingDocLargeEventCollectionViewCell else { + return estimate + } + + + largeEventCell.configure(with: largeEvent, theme: theme) + cell = largeEventCell + case .small(let smallEvent): + guard let smallEventCell = layoutManager.placeholder(forCellWithReuseIdentifier: ArticleAsLivingDocSmallEventCollectionViewCell.identifier) as? ArticleAsLivingDocSmallEventCollectionViewCell else { + return estimate + } + + smallEventCell.configure(viewModel: smallEvent, theme: theme) + cell = smallEventCell + } + + cell.layoutMargins = layout.itemLayoutMargins + estimate.height = cell.sizeThatFits(CGSize(width: columnWidth, height: UIView.noIntrinsicMetric), apply: false).height + estimate.precalculated = true + + return estimate + } + + func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + + guard let articleAsLivingDocViewModel = delegate?.articleAsLivingDocViewModel else { + return + } + + let numSections = dataSource.numberOfSections(in: collectionView) + let numEvents = dataSource.collectionView(collectionView, numberOfItemsInSection: indexPath.section) + + if indexPath.section == numSections - 1 && + indexPath.item == numEvents - 1 { + guard let nextRvStartId = articleAsLivingDocViewModel.nextRvStartId, + nextRvStartId != 0 else { + return + } + + delegate?.fetchNextPage(nextRvStartId: nextRvStartId, theme: theme) + } + } + + @objc func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool { + return false + } + + @objc func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { + return false + } + + override func collectionViewFooterButtonWasPressed(_ collectionViewFooter: CollectionViewFooter) { + tappedViewFullHistoryButtonInFooter() + } +} + +// MARK: - ArticleAsLivingDocHorizontallyScrollingCellDelegate +extension ArticleAsLivingDocViewController: ArticleAsLivingDocHorizontallyScrollingCellDelegate, InternalLinkPreviewing { + func tappedLink(_ url: URL) { + guard let fullURL = delegate?.articleURL.resolvingRelativeWikiHref(url.absoluteString) else { + return + } + let loggedInUsername = MWKDataStore.shared().authenticationManager.loggedInUsername + switch Configuration.current.router.destination(for: fullURL, loggedInUsername: loggedInUsername) { + case .article(let articleURL): showInternalLink(url: articleURL) + default: navigate(to: fullURL) + } + } +} + +extension ArticleAsLivingDocViewController: ArticleDetailsShowing { + func showTalkPageWithSectionName(_ sectionName: String?) { + + var maybeTalkPageURL = delegate?.articleURL.articleTalkPage + if let convertedSectionName = sectionName?.asTalkPageFragment, + let talkPageURL = maybeTalkPageURL { + maybeTalkPageURL = URL(string: talkPageURL.absoluteString + "#" + convertedSectionName) + } + + guard let talkPageURL = maybeTalkPageURL else { + showGenericError() + return + } + + navigate(to: talkPageURL) + } + + func goToDiff(revisionId: UInt, parentId: UInt, diffType: DiffContainerViewModel.DiffType) { + + guard let title = delegate?.articleURL.wmf_title, + let siteURL = delegate?.articleURL.wmf_site else { + return + } + + let diffContainerVC = DiffContainerViewController(siteURL: siteURL, theme: theme, fromRevisionID: Int(parentId), toRevisionID: Int(revisionId), type: diffType, articleTitle: title, needsSetNavDelegate: true) + + delegate?.livingDocViewWillPush() + push(diffContainerVC) + } + + func goToHistory() { + guard let articleURL = delegate?.articleURL, let title = articleURL.wmf_title else { + showGenericError() + return + } + + let historyVC = PageHistoryViewController(pageTitle: title, pageURL: articleURL) + historyVC.apply(theme: theme) + + delegate?.livingDocViewWillPush() + push(historyVC) + } + + func thankButtonTapped(for revisionID: Int, isUserAnonymous: Bool) { + self.tappedThank(for: revisionID, isUserAnonymous: isUserAnonymous) + } +} + +extension ArticleAsLivingDocViewController: ThanksGiving { + var url: URL? { + return self.delegate?.articleURL.wmf_site + } + + var bottomSpacing: CGFloat? { + return 0 + } + + func didLogIn() { + self.apply(theme: theme) + } + + func wereThanksSent(for revisionID: Int) -> Bool { + let currentSnapshot = dataSource.snapshot() + + for section in currentSnapshot.sectionIdentifiers { + for event in section.typedEvents { + switch event { + case .large(let largeEvent): + if largeEvent.revId == revisionID { + return largeEvent.wereThanksSent + } + default: continue + } + } + } + + return false + } + + func thanksWereSent(for revisionID: Int) { + var currentSnapshot = dataSource.snapshot() + + for section in currentSnapshot.sectionIdentifiers { + for event in section.typedEvents { + switch event { + case .large(let largeEvent): + if largeEvent.revId == revisionID { + largeEvent.wereThanksSent = true + currentSnapshot.reloadSections([section]) + break + } + default: continue + } + } + } + + dataSource.apply(currentSnapshot, animatingDifferences: true) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleCacheDBWriter.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleCacheDBWriter.swift new file mode 100644 index 0000000..758aff7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleCacheDBWriter.swift @@ -0,0 +1,323 @@ +import Foundation + +public enum ArticleCacheDBWriterError: Error { + case unableToDetermineDatabaseKey + case missingListURLInRequest + case failureFetchingMediaList(Error) + case failureFetchingOfflineResourceList(Error) + case failureFetchOrCreateCacheGroup + case failureFetchOrCreateMustHaveCacheItem + case unableToDetermineItemKey + case unableToDetermineBundledOfflineURLs + case oneOrMoreItemsFailedToMarkDownloaded([Error]) + case failureMakingRequestFromMustHaveResource +} + +final class ArticleCacheDBWriter: ArticleCacheResourceDBWriting { + + let articleFetcher: ArticleFetcher + let context: NSManagedObjectContext + let imageController: ImageCacheController + let imageInfoFetcher: MWKImageInfoFetcher + + + var fetcher: CacheFetching { + return articleFetcher + } + + var groupedTasks: [String : [IdentifiedTask]] = [:] + + init(articleFetcher: ArticleFetcher, cacheBackgroundContext: NSManagedObjectContext, imageController: ImageCacheController, imageInfoFetcher: MWKImageInfoFetcher) { + + self.articleFetcher = articleFetcher + self.context = cacheBackgroundContext + self.imageController = imageController + self.imageInfoFetcher = imageInfoFetcher + } + + func add(urls: [URL], groupKey: String, completion: CacheDBWritingCompletionWithURLRequests) { + assertionFailure("ArticleCacheDBWriter not setup for batch url inserts.") + } + + // note, this comes in as desktopArticleURL via WMFArticle's key + func add(url: URL, groupKey: CacheController.GroupKey, completion: @escaping CacheDBWritingCompletionWithURLRequests) { + + fetchImageAndResourceURLsForArticleURL(url, groupKey: groupKey) { [weak self] (result) in + + guard let self = self else { + return + } + + switch result { + case .success(let urls): + let languageVariantCode = url.wmf_languageVariantCode + var mustHaveURLRequests: [URLRequest] = [] + + let mobileHTMLRequest: URLRequest + let mobileHTMLMediaListRequest: URLRequest + do { + mobileHTMLRequest = try self.articleFetcher.mobileHTMLRequest(articleURL: url) + mobileHTMLMediaListRequest = try self.articleFetcher.mobileHTMLMediaListRequest(articleURL: url) + } catch let error { + completion(.failure(error)) + return + } + + mustHaveURLRequests.append(mobileHTMLRequest) + mustHaveURLRequests.append(mobileHTMLMediaListRequest) + + // append mobile-html-offline-resource URLRequests + for var url in urls.offlineResourcesURLs { + // We're OK with any Content-Type here because we don't use them directly, they're the related files that mobile-html might request + let acceptAnyContentType = ["Accept": "*/*"] + + // Temporary shim until ArticleCache is completely variant-aware + url.wmf_languageVariantCode = languageVariantCode + guard let urlRequest = self.articleFetcher.urlRequest(from: url, headers: acceptAnyContentType) else { + continue + } + + mustHaveURLRequests.append(urlRequest) + } + + // append image info URLRequests + for url in urls.imageInfoURLs { + guard let urlRequest = self.imageInfoFetcher.urlRequestFor(from: url) else { + completion(.failure(ArticleCacheDBWriterError.failureMakingRequestFromMustHaveResource)) + return + } + + mustHaveURLRequests.append(urlRequest) + } + + // send image urls straight to imageController to deal with + self.imageController.add(urls: urls.mediaListURLs, groupKey: groupKey, individualCompletion: { (result) in + + }) { (result) in + + } + + // write URLs to database + self.cacheURLs(groupKey: groupKey, mustHaveURLRequests: mustHaveURLRequests, niceToHaveURLRequests: []) { (result) in + switch result { + case .success: + let result = CacheDBWritingResultWithURLRequests.success(mustHaveURLRequests) + completion(result) + case .failure(let error): + let result = CacheDBWritingResultWithURLRequests.failure(error) + completion(result) + } + } + + case .failure(let error): + completion(.failure(error)) + return + } + } + } + + func markDownloaded(urlRequest: URLRequest, response: HTTPURLResponse?, completion: @escaping (CacheDBWritingResult) -> Void) { + + guard let itemKey = fetcher.itemKeyForURLRequest(urlRequest) else { + completion(.failure(CacheDBWritingMarkDownloadedError.unableToDetermineItemKey)) + return + } + + let variant = fetcher.variantForURLRequest(urlRequest) + + context.perform { + guard let cacheItem = CacheDBWriterHelper.cacheItem(with: itemKey, variant: nil, in: self.context) else { + completion(.failure(CacheDBWritingMarkDownloadedError.cannotFindCacheItem)) + return + } + cacheItem.isDownloaded = true + + let varyHeaderValue = response?.allHeaderFields[HTTPURLResponse.varyHeaderKey] as? String ?? nil + let variesOnLanguage = varyHeaderValue?.contains(HTTPURLResponse.acceptLanguageHeaderValue) ?? false + if variesOnLanguage { + cacheItem.variant = variant + } + + CacheDBWriterHelper.save(moc: self.context) { (result) in + switch result { + case .success: + completion(.success) + case .failure(let error): + completion(.failure(error)) + } + } + } + } + + func shouldDownloadVariant(itemKey: CacheController.ItemKey, variant: String?) -> Bool { + // maybe tonitodo: if we reach a point where we add all language variation keys to db, we should limit this based on their NSLocale language preferences. + return true + } + + func shouldDownloadVariantForAllVariantItems(variant: String?, _ allVariantItems: [CacheController.ItemKeyAndVariant]) -> Bool { + return true + } +} + +// Migration + +extension ArticleCacheDBWriter { + + func addMobileHtmlURLForMigration(desktopArticleURL: URL, success: @escaping (URLRequest) -> Void, failure: @escaping (Error) -> Void) { // articleURL should be desktopURL + + guard let groupKey = desktopArticleURL.wmf_databaseKey else { + failure(ArticleCacheDBWriterError.unableToDetermineDatabaseKey) + return + } + + let mobileHTMLRequest: URLRequest + do { + + mobileHTMLRequest = try articleFetcher.mobileHTMLRequest(articleURL: desktopArticleURL) + } catch let error { + failure(error) + return + } + + cacheURLs(groupKey: groupKey, mustHaveURLRequests: [mobileHTMLRequest], niceToHaveURLRequests: []) { (result) in + switch result { + case .success: + success(mobileHTMLRequest) + case .failure(let error): + failure(error) + } + } + } + + func addBundledResourcesForMigration(desktopArticleURL: URL, completion: @escaping CacheDBWritingCompletionWithURLRequests) { + context.perform { + + + guard let offlineResources = self.articleFetcher.bundledOfflineResourceURLs() else { + completion(.failure(ArticleCacheDBWriterError.unableToDetermineBundledOfflineURLs)) + return + } + + guard let groupKey = desktopArticleURL.wmf_databaseKey else { + completion(.failure(ArticleCacheDBWriterError.unableToDetermineDatabaseKey)) + return + } + var baseCSSURL = offlineResources.baseCSS + baseCSSURL.wmf_languageVariantCode = desktopArticleURL.wmf_languageVariantCode + let baseCSSRequest = self.articleFetcher.urlRequest(from: baseCSSURL) + + var pcsCSSURL = offlineResources.pcsCSS + pcsCSSURL.wmf_languageVariantCode = desktopArticleURL.wmf_languageVariantCode + let pcsCSSRequest = self.articleFetcher.urlRequest(from: pcsCSSURL) + + var pcsJSURL = offlineResources.pcsJS + pcsJSURL.wmf_languageVariantCode = desktopArticleURL.wmf_languageVariantCode + let pcsJSRequest = self.articleFetcher.urlRequest(from: pcsJSURL) + + let bundledURLRequests = [baseCSSRequest, pcsCSSRequest, pcsJSRequest].compactMap { $0 } + + self.cacheURLs(groupKey: groupKey, mustHaveURLRequests: bundledURLRequests, niceToHaveURLRequests: []) { (result) in + switch result { + case .success: + let result = CacheDBWritingResultWithURLRequests.success(bundledURLRequests) + completion(result) + case .failure(let error): + let result = CacheDBWritingResultWithURLRequests.failure(error) + completion(result) + } + } + } + } + + func bundledResourcesAreCached() -> Bool { + + var result: Bool = false + context.performAndWait { + + var bundledOfflineResourceKeys: [String] = [] + guard let offlineResources = articleFetcher.bundledOfflineResourceURLs() else { + result = false + return + } + + if let baseCSSKey = offlineResources.baseCSS.wmf_databaseKey { + bundledOfflineResourceKeys.append(baseCSSKey) + } + + if let pcsCSSKey = offlineResources.pcsCSS.wmf_databaseKey { + bundledOfflineResourceKeys.append(pcsCSSKey) + } + + if let pcsJSKey = offlineResources.pcsJS.wmf_databaseKey { + bundledOfflineResourceKeys.append(pcsJSKey) + } + + guard bundledOfflineResourceKeys.count == articleFetcher.expectedNumberOfBundledOfflineResources else { + result = false + return + } + + let fetchRequest: NSFetchRequest = CacheItem.fetchRequest() + + fetchRequest.predicate = NSPredicate(format: "key IN %@", bundledOfflineResourceKeys) + do { + let items = try context.fetch(fetchRequest) + + guard items.count == articleFetcher.expectedNumberOfBundledOfflineResources else { + result = false + return + } + + for item in items { + if item.isDownloaded == false { + result = false + } + } + + result = true + + } catch { + result = false + } + } + + return result + } + + struct BulkMarkDownloadRequest { + let urlRequest: URLRequest + let response: HTTPURLResponse? + } + + func markDownloaded(requests: [BulkMarkDownloadRequest], completion: @escaping (CacheDBWritingResult) -> Void) { + + var markDownloadedErrors: [Error] = [] + + let group = DispatchGroup() + + for request in requests { + group.enter() + markDownloaded(urlRequest: request.urlRequest, response: request.response) { (result) in + + defer { + group.leave() + } + + switch result { + case .success: + break + case .failure(let error): + markDownloadedErrors.append(error) + } + } + } + + group.notify(queue: DispatchQueue.global(qos: .userInitiated)) { + if markDownloadedErrors.count > 0 { + completion(.failure(ArticleCacheDBWriterError.oneOrMoreItemsFailedToMarkDownloaded(markDownloadedErrors))) + } else { + completion(.success) + } + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewCell+ListDisplay.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewCell+ListDisplay.swift new file mode 100644 index 0000000..8c2470d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewCell+ListDisplay.swift @@ -0,0 +1,35 @@ +import Foundation + +extension ArticleCollectionViewCell { + @objc open func configureForCompactList(at index: Int) { + layoutMargins = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15) + imageViewDimension = 40 + } + + @objc public var imageURL: URL? { + get { + return imageView.wmf_imageURLToFetch + } + set { + guard let newURL = newValue else { + isImageViewHidden = true + imageView.wmf_reset() + return + } + isImageViewHidden = false + imageView.wmf_setImage(with: newURL, detectFaces: true, onGPU: true, failure: WMFIgnoreErrorHandler, success: WMFIgnoreSuccessHandler) + } + } +} + +extension ArticleRightAlignedImageCollectionViewCell { + @objc open func configureSeparators(for index: Int) { + topSeparator.isHidden = index > 0 + bottomSeparator.isHidden = false + } + + open override func configureForCompactList(at index: Int) { + super.configureForCompactList(at: index) + configureSeparators(for: index) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewCell+WMFFeedContentDisplaying.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewCell+WMFFeedContentDisplaying.swift new file mode 100644 index 0000000..c5af364 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewCell+WMFFeedContentDisplaying.swift @@ -0,0 +1,122 @@ +import Foundation + +public extension ArticleCollectionViewCell { + @objc(configureWithArticle:displayType:index:theme:layoutOnly:completion:) + func configure(article: WMFArticle, displayType: WMFFeedDisplayType, index: Int, theme: Theme, layoutOnly: Bool, completion:(() -> Void)? = nil) { + apply(theme: theme) + + let group = DispatchGroup() + + let imageWidthToRequest = displayType.imageWidthCompatibleWithTraitCollection(traitCollection) + if displayType != .mainPage, let imageURL = article.imageURL(forWidth: imageWidthToRequest) { + isImageViewHidden = false + if !layoutOnly { + group.enter() + imageView.wmf_setImage(with: imageURL, detectFaces: true, onGPU: true, failure: { (error) in group.leave() }, success: { group.leave() }) + } + } else { + isImageViewHidden = true + } + + let articleLanguageCode = article.url?.wmf_languageCode + + titleHTML = article.displayTitleHTML + + switch displayType { + case .random: + imageViewDimension = 196 + descriptionLabel.text = article.capitalizedWikidataDescription + extractLabel?.text = article.snippet + case .pageWithPreview: + imageViewDimension = 196 + descriptionLabel.text = article.capitalizedWikidataDescription + extractLabel?.text = article.snippet + case .continueReading: + imageViewDimension = 130 + extractLabel?.text = nil + descriptionLabel.text = article.capitalizedWikidataDescriptionOrSnippet + extractLabel?.text = nil + case .relatedPagesSourceArticle: + setBackgroundColors(theme.colors.midBackground, selected: theme.colors.baseBackground) + updateSelectedOrHighlighted() + imageViewDimension = 130 + extractLabel?.text = nil + descriptionLabel.text = article.capitalizedWikidataDescriptionOrSnippet + extractLabel?.text = nil + case .mainPage: + titleTextStyle = .georgiaTitle3 + descriptionTextStyle = .subheadline + updateFonts(with: traitCollection) + descriptionLabel.text = article.capitalizedWikidataDescription ?? WMFLocalizedString("explore-main-page-description", value: "Main page of Wikimedia projects", comment: "Main page description that shows when the main page lacks a Wikidata description.") + extractLabel?.text = nil + case .pageWithLocationPlaceholder: + fallthrough + case .pageWithLocation: + isImageViewHidden = false + descriptionLabel.text = article.capitalizedWikidataDescriptionOrSnippet + extractLabel?.text = nil + break + case .compactList, .relatedPages: + configureForCompactList(at: index) + if displayType == .relatedPages, let cell = self as? ArticleRightAlignedImageCollectionViewCell { + cell.topSeparator.isHidden = true + cell.bottomSeparator.isHidden = true + } + fallthrough + case .page: + fallthrough + default: + imageViewDimension = 40 + descriptionLabel.text = article.capitalizedWikidataDescriptionOrSnippet + extractLabel?.text = nil + } + + titleLabel.accessibilityLanguage = articleLanguageCode + descriptionLabel.accessibilityLanguage = articleLanguageCode + extractLabel?.accessibilityLanguage = articleLanguageCode + articleSemanticContentAttribute = MWKLanguageLinkController.semanticContentAttribute(forContentLanguageCode: article.url?.wmf_contentLanguageCode) + setNeedsLayout() + group.notify(queue: .main) { + completion?() + } + } +} + +public extension ArticleRightAlignedImageCollectionViewCell { + @objc func configure(article: WMFArticle, displayType: WMFFeedDisplayType, index: Int, shouldShowSeparators: Bool = false, theme: Theme, layoutOnly: Bool, completion:(() -> Void)? = nil) { + if shouldShowSeparators { + self.topSeparator.isHidden = index != 0 + self.bottomSeparator.isHidden = false + } else { + self.bottomSeparator.isHidden = true + } + super.configure(article: article, displayType: displayType, index: index, theme: theme, layoutOnly: layoutOnly, completion: completion) + } +} + +public extension RankedArticleCollectionViewCell { + override func configure(article: WMFArticle, displayType: WMFFeedDisplayType, index: Int, shouldShowSeparators: Bool = false, theme: Theme, layoutOnly: Bool, completion:(() -> Void)? = nil) { + rankView.rank = index + 1 + let percent = CGFloat(index + 1) / CGFloat(5) + rankView.rankColor = theme.colors.rankGradient.color(at: percent) + super.configure(article: article, displayType: displayType, index: index, shouldShowSeparators: shouldShowSeparators, theme: theme, layoutOnly: layoutOnly, completion: completion) + } + + override func configure(article: WMFArticle, displayType: WMFFeedDisplayType, index: Int, theme: Theme, layoutOnly: Bool, completion:(() -> Void)? = nil) { + configure(article: article, displayType: displayType, index: index, shouldShowSeparators: false, theme: theme, layoutOnly: layoutOnly, completion: completion) + } +} + +public extension ArticleFullWidthImageCollectionViewCell { + override func configure(article: WMFArticle, displayType: WMFFeedDisplayType, index: Int, theme: Theme, layoutOnly: Bool, completion:(() -> Void)? = nil) { + switch displayType { + case .random: + fallthrough + case .pageWithPreview: + isSaveButtonHidden = false + default: + isSaveButtonHidden = true + } + super.configure(article: article, displayType: displayType, index: index, theme: theme, layoutOnly: layoutOnly, completion: completion) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewCell.swift new file mode 100644 index 0000000..782ba22 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewCell.swift @@ -0,0 +1,413 @@ +import UIKit + +open class ArticleCollectionViewCell: CollectionViewCell, SwipeableCell, BatchEditableCell { + public let titleLabel = UILabel() + public let descriptionLabel = UILabel() + public let imageView = UIImageView() + public var extractLabel: UILabel? + public let actionsView = ActionsView() + public var alertButton = AlignedImageButton() + open var alertType: ReadingListAlertType? + public var alertButtonCallback: (() -> Void)? + + public var statusView = UIImageView() // the circle that appears next to the article name to indicate the article's status + + private var _titleHTML: String? = nil + private var _titleBoldedString: String? = nil + + private func updateTitleLabel() { + if let titleHTML = _titleHTML { + let attributedTitle = titleHTML.byAttributingHTML(with: titleTextStyle, matching: traitCollection) + if let boldString = _titleBoldedString { + attributedTitle.applyBoldFont(to: boldString, textStyle: titleTextStyle, matching: traitCollection) + } + titleLabel.attributedText = attributedTitle + } else { + let titleFont = UIFont.wmf_font(titleTextStyle, compatibleWithTraitCollection: traitCollection) + titleLabel.font = titleFont + } + } + + public var titleHTML: String? { + get { + return _titleHTML + } + set { + _titleHTML = newValue + updateTitleLabel() + } + } + + public func setTitleHTML(_ titleHTML: String?, boldedString: String?) { + _titleHTML = titleHTML + _titleBoldedString = boldedString + updateTitleLabel() + } + + public var actions: [Action] { + get { + return actionsView.actions + } + set { + actionsView.actions = newValue + updateAccessibilityElements() + } + } + + open override func setup() { + titleTextStyle = .georgiaTitle3 + imageView.contentMode = .scaleAspectFill + imageView.clipsToBounds = true + statusView.clipsToBounds = true + + imageView.accessibilityIgnoresInvertColors = true + + titleLabel.isOpaque = true + descriptionLabel.isOpaque = true + imageView.isOpaque = true + + + contentView.addSubview(alertButton) + alertButton.addTarget(self, action: #selector(alertButtonTapped), for: .touchUpInside) + alertButton.verticalPadding = spacing + alertButton.leftPadding = spacing + alertButton.rightPadding = spacing + alertButton.horizontalSpacing = spacing + contentView.addSubview(statusView) + + contentView.addSubview(imageView) + contentView.addSubview(titleLabel) + contentView.addSubview(descriptionLabel) + + super.setup() + } + + // This method is called to reset the cell to the default configuration. It is called on initial setup and prepareForReuse. Subclassers should call super. + override open func reset() { + super.reset() + _titleHTML = nil + _titleBoldedString = nil + titleTextStyle = .georgiaTitle3 + descriptionTextStyle = .subheadline + extractTextStyle = .subheadline + saveButtonTextStyle = .mediumFootnote + spacing = 3 + imageViewDimension = 70 + statusViewDimension = 6 + imageView.wmf_reset() + resetSwipeable() + isBatchEditing = false + isBatchEditable = false + actions = [] + isAlertButtonHidden = true + isStatusViewHidden = true + updateFonts(with: traitCollection) + } + + override open func updateBackgroundColorOfLabels() { + super.updateBackgroundColorOfLabels() + titleLabel.backgroundColor = labelBackgroundColor + descriptionLabel.backgroundColor = labelBackgroundColor + extractLabel?.backgroundColor = labelBackgroundColor + alertButton.titleLabel?.backgroundColor = labelBackgroundColor + } + + open override func safeAreaInsetsDidChange() { + super.safeAreaInsetsDidChange() + if swipeState == .open { + swipeTranslation = swipeTranslationWhenOpen + } + setNeedsLayout() + } + + var actionsViewInsets: UIEdgeInsets { + return safeAreaInsets + } + + public final var statusViewDimension: CGFloat = 0 { + didSet { + setNeedsLayout() + } + } + + public final var alertIconDimension: CGFloat = 0 { + didSet { + setNeedsLayout() + } + } + + public var isStatusViewHidden: Bool = true { + didSet { + statusView.isHidden = isStatusViewHidden + setNeedsLayout() + } + } + + public var isAlertButtonHidden: Bool = true { + didSet { + alertButton.isHidden = isAlertButtonHidden + setNeedsLayout() + } + } + + public var isDeviceRTL: Bool { + return effectiveUserInterfaceLayoutDirection == .rightToLeft + } + + public var isArticleRTL: Bool { + return articleSemanticContentAttribute == .forceRightToLeft + } + + open override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let size = super.sizeThatFits(size, apply: apply) + if apply { + let layoutMargins = calculatedLayoutMargins + let isBatchEditOnRight = isDeviceRTL + var batchEditSelectViewWidth: CGFloat = 0 + var batchEditX: CGFloat = 0 + + if isBatchEditingPaneOpen { + if isArticleRTL { + batchEditSelectViewWidth = isBatchEditOnRight ? layoutMargins.left : layoutMargins.right // left and and right here are really leading and trailing, should change to UIDirectionalEdgeInsets when available + } else { + batchEditSelectViewWidth = isBatchEditOnRight ? layoutMargins.right : layoutMargins.left + } + if isBatchEditOnRight { + batchEditX = size.width - batchEditSelectViewWidth + } else { + batchEditX = 0 + } + } else { + if isBatchEditOnRight { + batchEditX = size.width + } else { + batchEditX = 0 - batchEditSelectViewWidth + } + } + + let safeX = isBatchEditOnRight ? safeAreaInsets.right : safeAreaInsets.left + batchEditSelectViewWidth -= safeX + if !isBatchEditOnRight && isBatchEditingPaneOpen { + batchEditX += safeX + } + if isBatchEditOnRight && !isBatchEditingPaneOpen { + batchEditX -= batchEditSelectViewWidth + } + + batchEditSelectView?.frame = CGRect(x: batchEditX, y: 0, width: batchEditSelectViewWidth, height: size.height) + batchEditSelectView?.layoutIfNeeded() + + let actionsViewWidth = isDeviceRTL ? max(0, swipeTranslation) : -1 * min(0, swipeTranslation) + let x = isDeviceRTL ? 0 : size.width - actionsViewWidth + actionsView.frame = CGRect(x: x, y: 0, width: actionsViewWidth, height: size.height) + actionsView.layoutIfNeeded() + } + return size + } + + // MARK: - View configuration + // These properties can mutate with each use of the cell. They should be reset by the `reset` function. Call setsNeedLayout after adjusting any of these properties + + public var titleTextStyle: DynamicTextStyle! + public var descriptionTextStyle: DynamicTextStyle! + public var extractTextStyle: DynamicTextStyle! + public var saveButtonTextStyle: DynamicTextStyle! + + public var imageViewDimension: CGFloat = 0 // used as height on full width cell, width & height on right aligned + public var spacing: CGFloat = 3 + + public var isImageViewHidden = false { + didSet { + imageView.isHidden = isImageViewHidden + setNeedsLayout() + } + } + + + open override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + + updateTitleLabel() + + descriptionLabel.font = UIFont.wmf_font(descriptionTextStyle, compatibleWithTraitCollection: traitCollection) + extractLabel?.font = UIFont.wmf_font(extractTextStyle, compatibleWithTraitCollection: traitCollection) + alertButton.titleLabel?.font = UIFont.wmf_font(.semiboldCaption2, compatibleWithTraitCollection: traitCollection) + } + + // MARK: - Semantic content + + fileprivate var _articleSemanticContentAttribute: UISemanticContentAttribute = .unspecified + fileprivate var _effectiveArticleSemanticContentAttribute: UISemanticContentAttribute = .unspecified + open var articleSemanticContentAttribute: UISemanticContentAttribute { + get { + return _effectiveArticleSemanticContentAttribute + } + set { + _articleSemanticContentAttribute = newValue + updateEffectiveArticleSemanticContentAttribute() + setNeedsLayout() + } + } + + // for items like the Save Button that are localized and should match the UI direction + public var userInterfaceSemanticContentAttribute: UISemanticContentAttribute { + return traitCollection.layoutDirection == .rightToLeft ? .forceRightToLeft : .forceLeftToRight + } + + fileprivate func updateEffectiveArticleSemanticContentAttribute() { + if _articleSemanticContentAttribute == .unspecified { + let isRTL = effectiveUserInterfaceLayoutDirection == .rightToLeft + _effectiveArticleSemanticContentAttribute = isRTL ? .forceRightToLeft : .forceLeftToRight + } else { + _effectiveArticleSemanticContentAttribute = _articleSemanticContentAttribute + } + let alignment = _effectiveArticleSemanticContentAttribute == .forceRightToLeft ? NSTextAlignment.right : NSTextAlignment.left + titleLabel.textAlignment = alignment + titleLabel.semanticContentAttribute = _effectiveArticleSemanticContentAttribute + descriptionLabel.semanticContentAttribute = _effectiveArticleSemanticContentAttribute + descriptionLabel.textAlignment = alignment + extractLabel?.semanticContentAttribute = _effectiveArticleSemanticContentAttribute + extractLabel?.textAlignment = alignment + } + + open override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + updateEffectiveArticleSemanticContentAttribute() + super.traitCollectionDidChange(previousTraitCollection) + } + + // MARK: - Accessibility + + open override func updateAccessibilityElements() { + var updatedAccessibilityElements: [Any] = [] + var groupedLabels = [titleLabel, descriptionLabel] + if let extract = extractLabel { + groupedLabels.append(extract) + } + + updatedAccessibilityElements.append(LabelGroupAccessibilityElement(view: self, labels: groupedLabels, actions: actions)) + + accessibilityElements = updatedAccessibilityElements + } + + // MARK: - Swipeable + var swipeState: SwipeState = .closed { + didSet { + if swipeState != .closed && actionsView.superview == nil { + contentView.addSubview(actionsView) + contentView.backgroundColor = backgroundView?.backgroundColor + clipsToBounds = true + } else if swipeState == .closed && actionsView.superview != nil { + actionsView.removeFromSuperview() + contentView.backgroundColor = .clear + clipsToBounds = false + } + } + } + + public var swipeTranslation: CGFloat = 0 { + didSet { + assert(!swipeTranslation.isNaN && swipeTranslation.isFinite) + let isArticleRTL = articleSemanticContentAttribute == .forceRightToLeft + if isArticleRTL { + layoutMarginsInteractiveAdditions.left = 0 - swipeTranslation + layoutMarginsInteractiveAdditions.right = swipeTranslation + } else { + layoutMarginsInteractiveAdditions.right = 0 - swipeTranslation + layoutMarginsInteractiveAdditions.left = swipeTranslation + } + setNeedsLayout() + } + } + + open var isSwipeEnabled: Bool { + return true + } + + private var isBatchEditingPaneOpen: Bool { + return batchEditingTranslation > 0 + } + + private var batchEditingTranslation: CGFloat = 0 { + didSet { + let marginAddition = batchEditingTranslation / 1.5 + + if isArticleRTL { + if isDeviceRTL { + layoutMarginsInteractiveAdditions.left = marginAddition + } else { + layoutMarginsInteractiveAdditions.right = marginAddition + } + } else { + if isDeviceRTL { + layoutMarginsInteractiveAdditions.right = marginAddition + } else { + layoutMarginsInteractiveAdditions.left = marginAddition + } + } + + if isBatchEditingPaneOpen, let batchEditSelectView = batchEditSelectView { + contentView.addSubview(batchEditSelectView) + batchEditSelectView.clipsToBounds = true + } + setNeedsLayout() + } + } + + public override func layoutWidth(for size: CGSize) -> CGFloat { + let layoutWidth = super.layoutWidth(for: size) - layoutMarginsInteractiveAdditions.left - layoutMarginsInteractiveAdditions.right + return layoutWidth + } + + public var swipeTranslationWhenOpen: CGFloat { + let maxWidth = actionsView.maximumWidth + let isRTL = effectiveUserInterfaceLayoutDirection == .rightToLeft + return isRTL ? actionsViewInsets.left + maxWidth : 0 - maxWidth - actionsViewInsets.right + } + + // MARK: Prepare for reuse + + func resetSwipeable() { + swipeTranslation = 0 + swipeState = .closed + } + + // MARK: - BatchEditableCell + + public var batchEditSelectView: BatchEditSelectView? + + public var isBatchEditable: Bool = false { + didSet { + if isBatchEditable && batchEditSelectView == nil { + batchEditSelectView = BatchEditSelectView() + batchEditSelectView?.isSelected = isSelected + } else if !isBatchEditable && batchEditSelectView != nil { + batchEditSelectView?.removeFromSuperview() + batchEditSelectView = nil + } + } + } + + public var isBatchEditing: Bool = false { + didSet { + if isBatchEditing { + isBatchEditable = true + batchEditingTranslation = BatchEditSelectView.fixedWidth + batchEditSelectView?.isSelected = isSelected + } else { + batchEditingTranslation = 0 + } + } + } + + override open var isSelected: Bool { + didSet { + batchEditSelectView?.isSelected = isSelected + } + } + + // MARK: - Actions + + @objc func alertButtonTapped() { + alertButtonCallback?() + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewController.swift new file mode 100644 index 0000000..4c39c62 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleCollectionViewController.swift @@ -0,0 +1,281 @@ +import UIKit + +@objc(WMFArticleCollectionViewControllerDelegate) +protocol ArticleCollectionViewControllerDelegate: NSObjectProtocol { + func articleCollectionViewController(_ articleCollectionViewController: ArticleCollectionViewController, didSelectArticleWith articleURL: URL, at indexPath: IndexPath) +} + +@objc(WMFArticleCollectionViewController) +class ArticleCollectionViewController: ColumnarCollectionViewController, EditableCollection, EventLoggingEventValuesProviding, CollectionViewContextMenuShowing { + @objc var dataStore: MWKDataStore! + var cellLayoutEstimate: ColumnarCollectionViewLayoutHeightEstimate? + + var editController: CollectionViewEditController! + + @objc weak var delegate: ArticleCollectionViewControllerDelegate? + + var feedFunnelContext: FeedFunnelContext? + + override func viewDidLoad() { + super.viewDidLoad() + layoutManager.register(ArticleRightAlignedImageCollectionViewCell.self, forCellWithReuseIdentifier: ArticleRightAlignedImageCollectionViewCell.identifier, addPlaceholder: true) + setupEditController() + } + + open func configure(cell: ArticleRightAlignedImageCollectionViewCell, forItemAt indexPath: IndexPath, layoutOnly: Bool) { + guard let article = article(at: indexPath) else { + return + } + cell.configure(article: article, displayType: .compactList, index: indexPath.item, shouldShowSeparators: true, theme: theme, layoutOnly: layoutOnly) + cell.topSeparator.isHidden = indexPath.item == 0 + cell.bottomSeparator.isHidden = indexPath.item == self.collectionView(collectionView, numberOfItemsInSection: indexPath.section) - 1 + cell.layoutMargins = layout.itemLayoutMargins + editController.configureSwipeableCell(cell, forItemAt: indexPath, layoutOnly: layoutOnly) + } + + open func articleURL(at indexPath: IndexPath) -> URL? { + assert(false, "Subclassers should override this function") + return nil + } + + open func imageURL(at indexPath: IndexPath) -> URL? { + guard let article = article(at: indexPath) else { + return nil + } + return article.imageURL(forWidth: traitCollection.wmf_nearbyThumbnailWidth) + } + + override func imageURLsForItemAt(_ indexPath: IndexPath) -> Set? { + guard let imageURL = imageURL(at: indexPath) else { + return nil + } + return [imageURL] + } + + open func article(at indexPath: IndexPath) -> WMFArticle? { + assert(false, "Subclassers should override this function") + return nil + } + + open func delete(at indexPath: IndexPath) { + assert(false, "Subclassers should override this function") + } + + open func canDelete(at indexPath: IndexPath) -> Bool { + return false + } + + open func canSave(at indexPath: IndexPath) -> Bool { + guard let articleURL = articleURL(at: indexPath) else { + return false + } + guard + let ns = articleURL.namespace, + ns == .main + else { + return false + } + return !dataStore.savedPageList.isAnyVariantSaved(articleURL) + } + + open func canUnsave(at indexPath: IndexPath) -> Bool { + guard let articleURL = articleURL(at: indexPath) else { + return false + } + return dataStore.savedPageList.isAnyVariantSaved(articleURL) + } + + open func canShare(at indexPath: IndexPath) -> Bool { + return articleURL(at: indexPath) != nil + } + + override func contentSizeCategoryDidChange(_ notification: Notification?) { + cellLayoutEstimate = nil + super.contentSizeCategoryDidChange(notification) + } + + // MARK: - EventLoggingEventValuesProviding + + var eventLoggingCategory: EventLoggingCategory { + assertionFailure("Subclassers should override this property") + return .unknown + } + + var eventLoggingLabel: EventLoggingLabel? { + return nil + } + + var eventLoggingIndex: NSNumber? { + guard let index = previewedIndexPath?.item else { + return nil + } + return NSNumber(value: index) + } + + var previewedIndexPath: IndexPath? + + // MARK: - Layout + + override func collectionView(_ collectionView: UICollectionView, estimatedHeightForItemAt indexPath: IndexPath, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + // The layout estimate can be re-used in this case because both labels are one line, meaning the cell + // size only varies with font size. The layout estimate is nil'd when the font size changes on trait collection change + if let estimate = cellLayoutEstimate { + return estimate + } + var estimate = ColumnarCollectionViewLayoutHeightEstimate(precalculated: false, height: 60) + guard let placeholderCell = layoutManager.placeholder(forCellWithReuseIdentifier: ArticleRightAlignedImageCollectionViewCell.identifier) as? ArticleRightAlignedImageCollectionViewCell else { + return estimate + } + configure(cell: placeholderCell, forItemAt: indexPath, layoutOnly: true) + // intentionally set all text and unhide image view to get largest possible size + placeholderCell.isImageViewHidden = false + placeholderCell.titleLabel.text = "any" + placeholderCell.descriptionLabel.text = "any" + estimate.height = placeholderCell.sizeThatFits(CGSize(width: columnWidth, height: UIView.noIntrinsicMetric), apply: false).height + estimate.precalculated = true + cellLayoutEstimate = estimate + return estimate + } + + override func metrics(with size: CGSize, readableWidth: CGFloat, layoutMargins: UIEdgeInsets) -> ColumnarCollectionViewLayoutMetrics { + return ColumnarCollectionViewLayoutMetrics.tableViewMetrics(with: size, readableWidth: readableWidth, layoutMargins: layoutMargins) + } + + // MARK: - CollectionViewContextMenuShowing + func previewingViewController(for indexPath: IndexPath, at location: CGPoint) -> UIViewController? { + guard !editController.isActive, // don't allow previewing when swipe actions are active + let articleURL = articleURL(at: indexPath) else { + return nil + } + + previewedIndexPath = indexPath + + guard let articleViewController = ArticleViewController(articleURL: articleURL, dataStore: dataStore, theme: self.theme) else { + return nil + } + articleViewController.articlePreviewingDelegate = self + articleViewController.wmf_addPeekableChildViewController(for: articleURL, dataStore: dataStore, theme: theme) + return articleViewController + } + + var poppingIntoVCCompletion: () -> Void { + // Nothing special needs to be run for this VC. + return {} + } +} + +// MARK: - UICollectionViewDataSource +extension ArticleCollectionViewController { + override func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } + + override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + assert(false, "Subclassers should override this function") + return 0 + } + + // Override configure(cell: instead to ensure height calculations are accurate + override open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ArticleRightAlignedImageCollectionViewCell.identifier, for: indexPath) + guard let articleCell = cell as? ArticleRightAlignedImageCollectionViewCell else { + return cell + } + configure(cell: articleCell, forItemAt: indexPath, layoutOnly: false) + return cell + } +} + +// MARK: - UICollectionViewDelegate +extension ArticleCollectionViewController { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + guard let articleURL = articleURL(at: indexPath) else { + collectionView.deselectItem(at: indexPath, animated: true) + return + } + + delegate?.articleCollectionViewController(self, didSelectArticleWith: articleURL, at: indexPath) + + navigate(to: articleURL) + } + + func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + editController.deconfigureSwipeableCell(cell, forItemAt: indexPath) + } +} + +extension ArticleCollectionViewController: ActionDelegate { + + func didPerformBatchEditToolbarAction(_ action: BatchEditToolbarAction, completion: @escaping (Bool) -> Void) { + assert(false, "Subclassers should override this function") + } + + func willPerformAction(_ action: Action) -> Bool { + guard let article = article(at: action.indexPath) else { + return false + } + guard action.type == .unsave else { + return self.editController.didPerformAction(action) + } + let alertController = ReadingListsAlertController() + let cancel = ReadingListsAlertActionType.cancel.action() + let delete = ReadingListsAlertActionType.unsave.action { _ = self.editController.didPerformAction(action) } + let actions = [cancel, delete] + alertController.showAlertIfNeeded(presenter: self, for: [article], with: actions) { showed in + if !showed { + _ = self.editController.didPerformAction(action) + } + } + return true + } + + func didPerformAction(_ action: Action) -> Bool { + let indexPath = action.indexPath + let sourceView = collectionView.cellForItem(at: indexPath) + switch action.type { + case .delete: + delete(at: indexPath) + UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: CommonStrings.articleDeletedNotification(articleCount: 1)) + return true + case .save: + if let articleURL = articleURL(at: indexPath) { + dataStore.savedPageList.addSavedPage(with: articleURL) + UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: CommonStrings.accessibilitySavedNotification) + ReadingListsFunnel.shared.logSave(category: eventLoggingCategory, label: eventLoggingLabel, articleURL: articleURL, date: feedFunnelContext?.midnightUTCDate, measurePosition: indexPath.item) + return true + } + case .unsave: + if let articleURL = articleURL(at: indexPath) { + dataStore.savedPageList.removeEntry(with: articleURL) + UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: CommonStrings.accessibilityUnsavedNotification) + ReadingListsFunnel.shared.logUnsave(category: eventLoggingCategory, label: eventLoggingLabel, articleURL: articleURL, date: feedFunnelContext?.midnightUTCDate, measurePosition: indexPath.item) + return true + } + case .share: + return share(article: article(at: indexPath), articleURL: articleURL(at: indexPath), at: indexPath, dataStore: dataStore, theme: theme, eventLoggingCategory: eventLoggingCategory, eventLoggingLabel: eventLoggingLabel, sourceView: sourceView) + } + return false + } + + func availableActions(at indexPath: IndexPath) -> [Action] { + var actions: [Action] = [] + + if canSave(at: indexPath) { + actions.append(ActionType.save.action(with: self, indexPath: indexPath)) + } else if canUnsave(at: indexPath) { + actions.append(ActionType.unsave.action(with: self, indexPath: indexPath)) + } + + if canShare(at: indexPath) { + actions.append(ActionType.share.action(with: self, indexPath: indexPath)) + } + + if canDelete(at: indexPath) { + actions.append(ActionType.delete.action(with: self, indexPath: indexPath)) + } + + return actions + } +} + +extension ArticleCollectionViewController: ShareableArticlesProvider {} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleContextMenuPresenting.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleContextMenuPresenting.swift new file mode 100644 index 0000000..db05250 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleContextMenuPresenting.swift @@ -0,0 +1,113 @@ +import Foundation +import UIKit + +protocol ArticleContextMenuPresenting { + func getPeekViewControllerAsync(for destination: Router.Destination, completion: @escaping (UIViewController?) -> Void) + + func hideFindInPage(_ completion: (() -> Void)?) + var configuration: Configuration { get } + var previewMenuItems: [UIMenuElement]? { get } +} + +enum ContextMenuCompletionType { + case bail + case timeout + case success +} + +// MARK: - Context Menu for Protocol +// All functions in this extension are for Context Menus +/// The ArticleContextMenuPresenting protocol extension has functions that are called by various classes' WKUIDelegate functions, but the WKUIDelegate functions themselves +/// reside within the actual classes. This is because in testing, the delegate methods were never called when they lived in the protocol extension - there would just be a silent failure. +/// Thus, there is some duplicated code in the actual classes for receiving the delegate calls, and those functions in turn call the shared code within this protocol extension. +/// More details: https://phabricator.wikimedia.org/T253891#6173598 +extension ArticleContextMenuPresenting { + func contextMenuConfigurationForElement(_ elementInfo: WKContextMenuElementInfo, completionHandler: @escaping (UIContextMenuConfiguration?) -> Void) { + let nullCompletion = { + completionHandler(nil) + } + + // This "N/A" part is pretty hacky. But we don't want to do a preview for "view article in browser", and this is the URL that is sent + // for "view article in browser". Without this "N/A" check, "View article in browser"'s preview and context menu shows the one for the + // article "N/A", which seems more wrong. The trade off: previews for the actual article "N/A" don't work, either. + guard let linkURL = elementInfo.linkURL, linkURL.wmf_title != "N/A" else { + nullCompletion() + return + } + + // moving into separate function for easier testability + contextMenuConfigurationForLinkURL(linkURL) { (completionType, menuConfig) in + guard completionType != .bail && completionType != .timeout else { + nullCompletion() + return + } + + completionHandler(menuConfig) + } + } + + func contextMenuConfigurationForLinkURL(_ linkURL: URL, ignoreTimeout: Bool = false, completionHandler: @escaping (ContextMenuCompletionType, UIContextMenuConfiguration?) -> Void) { + + // It's helpful if we can fetch the article before calling the completion + // However, we need to timeout if it takes too long + var didCallCompletion = false + + if !ignoreTimeout { + dispatchAfterDelayInSeconds(1.0, DispatchQueue.main) { + if !didCallCompletion { + completionHandler(.timeout, nil) + didCallCompletion = true + } + } + } + + getPeekViewControllerAsync(for: linkURL) { (peekParentVC) in + assert(Thread.isMainThread) + guard let peekParentVC = peekParentVC else { + if !didCallCompletion { + completionHandler(.bail, nil) + didCallCompletion = true + } + return + } + + let peekVC = peekParentVC.wmf_PeekableChildViewController + + self.hideFindInPage(nil) + let config = UIContextMenuConfiguration(identifier: linkURL as NSURL, previewProvider: { () -> UIViewController? in + return peekParentVC + }) { (suggestedActions) -> UIMenu? in + return (peekParentVC as? ArticleContextMenuPresenting)?.previewMenu + } + + if let articlePeekVC = peekVC as? ArticlePeekPreviewViewController { + articlePeekVC.fetchArticle { + assert(Thread.isMainThread) + if !didCallCompletion { + completionHandler(.success, config) + didCallCompletion = true + } + } + } else { + if !didCallCompletion { + completionHandler(.success, config) + didCallCompletion = true + } + } + } + } + + func getPeekViewControllerAsync(for linkURL: URL, completion: @escaping (UIViewController?) -> Void) { + let loggedInUsername = MWKDataStore.shared().authenticationManager.loggedInUsername + let destination = configuration.router.destination(for: linkURL, loggedInUsername: loggedInUsername) + getPeekViewControllerAsync(for: destination, completion: completion) + } + + var previewMenu: UIMenu? { + guard let previewMenuItems = previewMenuItems else { + return nil + } + + return UIMenu(title: "", image: nil, identifier: nil, options: [], children: previewMenuItems) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleData.xcdatamodeld/.xccurrentversion b/Apps/Wikipedia/Wikipedia/Code/ArticleData.xcdatamodeld/.xccurrentversion new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleData.xcdatamodeld/.xccurrentversion @@ -0,0 +1,5 @@ + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleData.xcdatamodeld/ArticleData 2.xcdatamodel/contents b/Apps/Wikipedia/Wikipedia/Code/ArticleData.xcdatamodeld/ArticleData 2.xcdatamodel/contents new file mode 100644 index 0000000..16915cf --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleData.xcdatamodeld/ArticleData 2.xcdatamodel/contents @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleData.xcdatamodeld/ArticleData.xcdatamodel/contents b/Apps/Wikipedia/Wikipedia/Code/ArticleData.xcdatamodeld/ArticleData.xcdatamodel/contents new file mode 100644 index 0000000..9c8ffe3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleData.xcdatamodeld/ArticleData.xcdatamodel/contents @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleFetchedResultsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleFetchedResultsViewController.swift new file mode 100644 index 0000000..1a825bc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleFetchedResultsViewController.swift @@ -0,0 +1,135 @@ +import UIKit +import WMF + +@objc(WMFArticleFetchedResultsViewController) +class ArticleFetchedResultsViewController: ArticleCollectionViewController, CollectionViewUpdaterDelegate { + var fetchedResultsController: NSFetchedResultsController! + var collectionViewUpdater: CollectionViewUpdater! + + open func setupFetchedResultsController(with dataStore: MWKDataStore) { + assert(false, "Subclassers should override this method") + } + + @objc override var dataStore: MWKDataStore! { + didSet { + setupFetchedResultsController(with: dataStore) + collectionViewUpdater = CollectionViewUpdater(fetchedResultsController: fetchedResultsController, collectionView: collectionView) + collectionViewUpdater?.delegate = self + } + } + + override func article(at indexPath: IndexPath) -> WMFArticle? { + guard fetchedResultsController.isValidIndexPath(indexPath) else { + return nil + } + return fetchedResultsController.object(at: indexPath) + } + + override func articleURL(at indexPath: IndexPath) -> URL? { + return article(at: indexPath)?.url + } + + override func delete(at indexPath: IndexPath) { + guard let article = article(at: indexPath) else { + return + } + do { + try article.removeFromReadHistory() + } catch let error { + showError(error) + } + } + + override func canDelete(at indexPath: IndexPath) -> Bool { + return true + } + + var deleteAllButtonText: String? = nil + var deleteAllConfirmationText: String? = nil + var deleteAllCancelText: String? = nil + var deleteAllText: String? = nil + var isDeleteAllVisible: Bool = false + + open func deleteAll() { + + } + + fileprivate final func updateDeleteButton() { + guard isDeleteAllVisible else { + navigationItem.rightBarButtonItem = nil + return + } + + if navigationItem.rightBarButtonItem == nil { + navigationItem.rightBarButtonItem = UIBarButtonItem(title: deleteAllButtonText, style: .plain, target: self, action: #selector(deleteButtonPressed(_:))) + } + + navigationItem.rightBarButtonItem?.isEnabled = !isEmpty + navigationBar.updateNavigationItems() + } + + @objc fileprivate final func deleteButtonPressed(_ sender: UIBarButtonItem) { + let alertController = UIAlertController(title: deleteAllConfirmationText, message: nil, preferredStyle: .actionSheet) + alertController.addAction(UIAlertAction(title: deleteAllText, style: .destructive, handler: { (action) in + self.deleteAll() + })) + alertController.addAction(UIAlertAction(title: deleteAllCancelText, style: .cancel, handler: nil)) + alertController.popoverPresentationController?.barButtonItem = sender + alertController.popoverPresentationController?.permittedArrowDirections = .any + present(alertController, animated: true, completion: nil) + } + + open func collectionViewUpdater(_ updater: CollectionViewUpdater, didUpdate collectionView: UICollectionView) { + for indexPath in collectionView.indexPathsForVisibleItems { + guard let cell = collectionView.cellForItem(at: indexPath) as? ArticleRightAlignedImageCollectionViewCell else { + continue + } + configure(cell: cell, forItemAt: indexPath, layoutOnly: false) + } + updateEmptyState() + } + + func collectionViewUpdater(_ updater: CollectionViewUpdater, updateItemAtIndexPath indexPath: IndexPath, in collectionView: UICollectionView) { + + } + + override func isEmptyDidChange() { + super.isEmptyDidChange() + updateDeleteButton() + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + editController.close() + } + + override func viewWillHaveFirstAppearance(_ animated: Bool) { + collectionViewUpdater.performFetch() + super.viewWillHaveFirstAppearance(animated) + } + + + func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool { + guard let translation = editController.swipeTranslationForItem(at: indexPath) else { + return true + } + return translation == 0 + } +} + +// MARK: UICollectionViewDataSource +extension ArticleFetchedResultsViewController { + override func numberOfSections(in collectionView: UICollectionView) -> Int { + guard let sectionsCount = self.fetchedResultsController.sections?.count else { + return 0 + } + return sectionsCount + } + + override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + guard let sections = self.fetchedResultsController.sections, section < sections.count else { + return 0 + } + return sections[section].numberOfObjects + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleFetcher.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleFetcher.swift new file mode 100644 index 0000000..23b1f2a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleFetcher.swift @@ -0,0 +1,544 @@ +import UIKit +import CocoaLumberjackSwift + +enum ArticleFetcherError: LocalizedError { + case doesNotExist + case failureToGenerateURL + case missingData + case invalidEndpointType + case unableToGenerateURLRequest + case updatedContentRequestTimeout + + public var errorDescription: String? { + switch self { + case .updatedContentRequestTimeout: + return WMFLocalizedString("article-fetcher-error-updated-content-timeout", value: "The app wasn't able to retrieve the updated content in time. Please refresh this page later to see your changes reflected.", comment: "Error shown to the user when the content doesn't update in a reasonable amount of time.") + default: + return CommonStrings.genericErrorDescription + } + } +} + +@objc(WMFArticleFetcher) +final public class ArticleFetcher: Fetcher, CacheFetching { + + public enum EndpointType: String { + case summary + case mediaList = "media-list" + case mobileHtmlOfflineResources = "mobile-html-offline-resources" + case mobileHTML = "mobile-html" + case references = "references" + } + + public enum MobileHTMLType: String { + case contentAndReferences = "contentAndReferences" + case content = "content" + case references = "references" + case editPreview = "editPreview" + } + + private static let mobileHTMLOutputHeaderKey = "output-mode" + private static let acceptHeaderKey = "Accept" + private static let acceptHTMLValue = "text/html; charset=utf-8" + + struct MediaListItem { + let imageURL: URL + let imageTitle: String + } + + @discardableResult func fetchMediaListURLs(with request: URLRequest, completion: @escaping (Result<[MediaListItem], Error>) -> Void) -> URLSessionTask? { + return fetchMediaList(with: request) { (result, response) in + if let statusCode = response?.statusCode, + statusCode == 404 { + completion(.failure(ArticleFetcherError.doesNotExist)) + return + } + + struct SourceAndTitle { + let source: MediaListItemSource + let title: String? + } + + switch result { + case .failure(let error): + completion(.failure(error)) + case .success(let mediaList): + + var sourceAndTitles: [SourceAndTitle] = [] + for item in mediaList.items { + + guard let sources = item.sources else { + continue + } + + for source in sources { + sourceAndTitles.append(SourceAndTitle(source: source, title: item.title)) + } + } + + let result = sourceAndTitles.map { (sourceAndTitle) -> MediaListItem? in + let scheme = request.url?.scheme ?? "https" + let finalString = "\(scheme):\(sourceAndTitle.source.urlString)" + + guard let title = sourceAndTitle.title, + let url = URL(string: finalString) else { + return nil + } + + return MediaListItem(imageURL: url, imageTitle: title) + + }.compactMap { $0 } + + completion(.success(result)) + } + } + } + + @discardableResult func fetchOfflineResourceURLs(with request: URLRequest, completion: @escaping (Result<[URL], Error>) -> Void) -> URLSessionTask? { + return trackedJSONDecodableTask(with: request) { (result: Result<[String]?, Error>, response) in + if let statusCode = response?.statusCode, + statusCode == 404 { + completion(.failure(ArticleFetcherError.doesNotExist)) + return + } + + switch result { + case .failure(let error): + completion(.failure(error)) + case .success(let urlStrings): + + guard let urlStrings = urlStrings else { + completion(.failure(ArticleFetcherError.missingData)) + return + } + + let result = urlStrings.map { (urlString) -> URL? in + let scheme = request.url?.scheme ?? "https" + let finalString = "\(scheme):\(urlString)" + return URL(string: finalString) + }.compactMap { $0 } + + completion(.success(result)) + } + + } + } + + @discardableResult public func fetchMediaList(with request: URLRequest, completion: @escaping (Result, HTTPURLResponse?) -> Void) -> URLSessionTask? { + return trackedJSONDecodableTask(with: request) { (result: Result, response) in + switch result { + case .success(let result): + guard let mediaList = result else { + completion(.failure(ArticleFetcherError.missingData), response) + return + } + + completion(.success(mediaList), response) + + case .failure(let error): + completion(.failure(error), response) + } + } + } + + private func previewHeaders(with articleURL: URL, mobileHTMLOutput: MobileHTMLType) -> [String: String] { + var headers = configuration.pageContentServiceHeaders(for: articleURL) + headers[ArticleFetcher.mobileHTMLOutputHeaderKey] = mobileHTMLOutput.rawValue + headers[ArticleFetcher.acceptHeaderKey] = ArticleFetcher.acceptHTMLValue + return headers + } + + public func wikitextToMobileHTMLPreviewRequest(articleURL: URL, wikitext: String, mobileHTMLOutput: MobileHTMLType = .contentAndReferences) throws -> URLRequest { + guard + let articleTitle = articleURL.wmf_title, + let percentEncodedTitle = articleTitle.percentEncodedPageTitleForPathComponents, + let url = configuration.pageContentServiceAPIURLForURL(articleURL, appending: ["transform", "wikitext", "to", "mobile-html", percentEncodedTitle]) + else { + throw RequestError.invalidParameters + } + let params: [String: String] = ["wikitext": wikitext] + let headers = previewHeaders(with: articleURL, mobileHTMLOutput: mobileHTMLOutput) + return session.request(with: url, method: .post, bodyParameters: params, bodyEncoding: .json, headers: headers) + } + + public func wikitextToHTMLRequest(articleURL: URL, wikitext: String, mobileHTMLOutput: MobileHTMLType) throws -> URLRequest { + guard + let articleTitle = articleURL.wmf_title, + let percentEncodedTitle = articleTitle.percentEncodedPageTitleForPathComponents + else { + throw RequestError.invalidParameters + } + + let localAndStagingUrlBlock: () throws -> URL = { + + // As of April 2020, the /transform/wikitext/to/html/{article} endpoint is only available on production, not local or staging PCS. + guard let url = Configuration.production.pageContentServiceAPIURLForURL(articleURL, appending: ["transform", "wikitext", "to", "html", percentEncodedTitle]) else { + throw RequestError.invalidParameters + } + + return url + } + + let prodUrlBlock: () throws -> URL = { [weak self] in + + guard let self = self else { + throw RequestError.invalidParameters + } + + guard let url = self.configuration.pageContentServiceAPIURLForURL(articleURL, appending: ["transform", "wikitext", "to", "html", percentEncodedTitle]) else { + throw RequestError.invalidParameters + } + + return url + } + + let url: URL + switch Configuration.current.environment { + case .local(let options): + if options.contains(.localPCS) { + url = try localAndStagingUrlBlock() + break + } + + url = try prodUrlBlock() + case .staging(let options): + if options.contains(.appsLabsforPCS) { + url = try localAndStagingUrlBlock() + break + } + + url = try prodUrlBlock() + default: + url = try prodUrlBlock() + } + + let params: [String: String] = ["wikitext": wikitext] + let headers = previewHeaders(with: articleURL, mobileHTMLOutput: mobileHTMLOutput) + return session.request(with: url, method: .post, bodyParameters: params, bodyEncoding: .json, headers: headers) + } + + public func htmlToMobileHTMLRequest(articleURL: URL, html: String, mobileHTMLOutput: MobileHTMLType) throws -> URLRequest { + guard + let articleTitle = articleURL.wmf_title, + let percentEncodedTitle = articleTitle.percentEncodedPageTitleForPathComponents, + let url = configuration.pageContentServiceAPIURLForURL(articleURL, appending: ["transform", "html", "to", "mobile-html", percentEncodedTitle]) + else { + throw RequestError.invalidParameters + } + let headers = previewHeaders(with: articleURL, mobileHTMLOutput: mobileHTMLOutput) + return session.request(with: url, method: .post, bodyParameters: html, bodyEncoding: .html, headers: headers) + } + + public func fetchMobileHTMLFromWikitext(articleURL: URL, wikitext: String, mobileHTMLOutput: MobileHTMLType = .contentAndReferences, completion: @escaping ((String?, URL?) -> Void)) throws { + let mobileHtmlCompletionHandler = { (data: Data?, response: URLResponse?, error: Error?) in + guard let data = data, let html = String(data: data, encoding: .utf8) else { + completion(nil, nil) + return + } + completion(html, response?.url) + } + let htmlRequestCompletionHandler = { (data: Data?, response: URLResponse?, error: Error?) in + guard let data = data, let html = String(data: data, encoding: .utf8) else { + completion(nil, nil) + return + } + do { + let mobileHtmlRequest = try self.htmlToMobileHTMLRequest(articleURL: articleURL, html: html, mobileHTMLOutput: mobileHTMLOutput) + let mobileHtml = self.session.dataTask(with: mobileHtmlRequest, completionHandler: mobileHtmlCompletionHandler) + mobileHtml?.resume() + } catch { + completion(nil, nil) + } + } + let htmlRequest = try wikitextToHTMLRequest(articleURL: articleURL, wikitext: wikitext, mobileHTMLOutput: mobileHTMLOutput) + let htmlTask = self.session.dataTask(with: htmlRequest, completionHandler: htmlRequestCompletionHandler) + htmlTask?.resume() + } + + public func mobileHTMLURL(articleURL: URL, revisionID: UInt64? = nil) throws -> URL { + guard + let articleTitle = articleURL.wmf_title, + let percentEncodedTitle = articleTitle.percentEncodedPageTitleForPathComponents + else { + throw RequestError.invalidParameters + } + + var pathComponents = ["page", "mobile-html", percentEncodedTitle] + + if let revisionID = revisionID { + pathComponents.append("\(revisionID)") + } + + guard let mobileHTMLURL = configuration.pageContentServiceAPIURLForURL(articleURL, appending: pathComponents) else { + throw RequestError.invalidParameters + } + + return mobileHTMLURL + } + + public func mediaListURL(articleURL: URL) throws -> URL { + guard + let articleTitle = articleURL.wmf_title, + let percentEncodedTitle = articleTitle.percentEncodedPageTitleForPathComponents, + let url = configuration.pageContentServiceAPIURLForURL(articleURL, appending: ["page", "media-list", percentEncodedTitle]) + else { + throw RequestError.invalidParameters + } + + return url + } + + public func mobileHTMLMediaListRequest(articleURL: URL, cachePolicy: WMFCachePolicy? = nil) throws -> URLRequest { + + let url = try mediaListURL(articleURL: articleURL) + + if let urlRequest = urlRequest(from: url, cachePolicy: cachePolicy) { + return urlRequest + } else { + throw ArticleFetcherError.unableToGenerateURLRequest + } + } + + public func mobileHTMLOfflineResourcesRequest(articleURL: URL, cachePolicy: WMFCachePolicy? = nil) throws -> URLRequest { + guard + let articleTitle = articleURL.wmf_title, + let percentEncodedTitle = articleTitle.percentEncodedPageTitleForPathComponents, + let url = configuration.pageContentServiceAPIURLForURL(articleURL, appending: ["page", "mobile-html-offline-resources", percentEncodedTitle]) + else { + throw RequestError.invalidParameters + } + + if let urlRequest = urlRequest(from: url, cachePolicy: cachePolicy) { + return urlRequest + } else { + throw ArticleFetcherError.unableToGenerateURLRequest + } + } + + public func urlRequest(from url: URL, cachePolicy: WMFCachePolicy? = nil, headers: [String: String] = [:]) -> URLRequest? { + var requestHeaders = configuration.pageContentServiceHeaders(for: url) + requestHeaders.merge(headers) { (_, updated) in updated } + let request = urlRequestFromPersistence(with: url, persistType: .article, cachePolicy: cachePolicy, headers: requestHeaders) + + return request + } + + public func mobileHTMLRequest(articleURL: URL, revisionID: UInt64? = nil, scheme: String? = nil, cachePolicy: WMFCachePolicy? = nil, isPageView: Bool = false) throws -> URLRequest { + + var url = try mobileHTMLURL(articleURL: articleURL, revisionID: revisionID) + + if let scheme = scheme { + var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) + urlComponents?.scheme = scheme + url = urlComponents?.wmf_URLWithLanguageVariantCode(url.wmf_languageVariantCode) ?? url + } + let acceptUTF8HTML = [ArticleFetcher.acceptHeaderKey: ArticleFetcher.acceptHTMLValue] + if var urlRequest = urlRequest(from: url, cachePolicy: cachePolicy, headers: acceptUTF8HTML) { + if revisionID != nil { + // Enables the caching system to update the revisionless url cache when this call goes through + urlRequest.customCacheUpdatingURL = try mobileHTMLURL(articleURL: articleURL) + } + if isPageView { + // https://phabricator.wikimedia.org/T256507 + urlRequest.setValue("pageview=1", forHTTPHeaderField: "X-Analytics") + } + return urlRequest + } else { + throw ArticleFetcherError.unableToGenerateURLRequest + } + } + + /// Makes periodic HEAD requests to the mobile-html endpoint until the etag no longer matches the one provided. + @discardableResult public func waitForMobileHTMLChange(articleURL: URL, eTag: String, attempt: Int = 0, maxAttempts: Int, cancellationKey: CancellationKey? = nil, completion: @escaping (Result) -> Void) -> CancellationKey? { + guard attempt < maxAttempts else { + completion(.failure(ArticleFetcherError.updatedContentRequestTimeout)) + return nil + } + let requestURL: URL + do { + requestURL = try mobileHTMLURL(articleURL: articleURL) + } catch let error { + completion(.failure(error)) + return nil + } + let key = cancellationKey ?? UUID().uuidString + let maybeTask = session.dataTask(with: requestURL, method: .head, headers: [URLRequest.ifNoneMatchHeaderKey: eTag], cachePolicy: .reloadIgnoringLocalCacheData) { (_, response, error) in + defer { + self.untrack(taskFor: key) + } + if let error = error { + completion(.failure(error)) + return + } + let bail = { + completion(.failure(RequestError.unexpectedResponse)) + } + guard let httpURLResponse = response as? HTTPURLResponse else { + bail() + return + } + + let retry = { + // Exponential backoff + let delayTime = 0.25 * pow(2, Double(attempt)) + dispatchOnMainQueueAfterDelayInSeconds(delayTime) { + self.waitForMobileHTMLChange(articleURL: articleURL, eTag: eTag, attempt: attempt + 1, maxAttempts: maxAttempts, cancellationKey: key, completion: completion) + } + } + + // Check for 200. The server returns 304 when the ETag matches the value we provided for `If-None-Match` above + switch httpURLResponse.statusCode { + case 200: + break + case 304: + retry() + return + default: + bail() + return + } + + guard + let updatedETag = httpURLResponse.allHeaderFields[HTTPURLResponse.etagHeaderKey] as? String, + updatedETag != eTag // Technically redundant. With If-None-Match provided, we should only get a 200 response if the ETag has changed. Included here as an extra check against a server behaving incorrectly + else { + assert(false, "A 200 response from the server should indicate that the ETag has changed") + retry() + return + } + + DDLogDebug("ETag for \(requestURL) changed from \(eTag) to \(updatedETag)") + completion(.success(updatedETag)) + } + guard let task = maybeTask else { + completion(.failure(RequestError.unknown)) + return nil + } + track(task: task, for: key) + task.resume() + return key + } + + public func isCached(articleURL: URL, scheme: String? = nil, completion: @escaping (Bool) -> Void) { + + guard let request = try? mobileHTMLRequest(articleURL: articleURL, scheme: scheme) else { + completion(false) + return + } + + return session.isCachedWithURLRequest(request, completion: completion) + } + + // MARK: Bundled offline resources + + struct BundledOfflineResources { + let baseCSS: URL + let pcsCSS: URL + let pcsJS: URL + } + + let expectedNumberOfBundledOfflineResources = 3 + + static var pcsBaseURI: String = { + let prodUri = "//\(Configuration.Domain.metaWiki)/api/rest_v1/" + + switch Configuration.current.environment { + case .local(let options): + if options.contains(.localPCS) { + return "//\(Configuration.Domain.localhost):8888/api/v1/" + } + return prodUri + case .staging(let options): + if options.contains(.appsLabsforPCS) { + return "//\(Configuration.Domain.appsLabs)/api/v1/" + } + + return prodUri + + default: + return prodUri + + } + }() + + func bundledOfflineResourceURLs() -> BundledOfflineResources? { + guard + let baseCSS = URL(string: "https:\(ArticleFetcher.pcsBaseURI)data/css/mobile/base"), + let pcsCSS = URL(string: "https:\(ArticleFetcher.pcsBaseURI)data/css/mobile/pcs"), + let pcsJS = URL(string: "https:\(ArticleFetcher.pcsBaseURI)data/javascript/mobile/pcs") + else { + return nil + } + return BundledOfflineResources(baseCSS: baseCSS, pcsCSS: pcsCSS, pcsJS: pcsJS) + } + + // MARK: - Article Summaries from /api/rest_v1/page/summary + + /// Returns the API URL to fetch an article summary for the given canonical article URL + private func summaryURL(articleURL: URL) throws -> URL { + guard + let articleTitle = articleURL.wmf_title, + let percentEncodedTitle = articleTitle.percentEncodedPageTitleForPathComponents, + let url = configuration.pageContentServiceAPIURLForURL(articleURL, appending: ["page", "summary", percentEncodedTitle]) + else { + throw RequestError.invalidParameters + } + return url + } + + /// Returns the API request to fetch an article summary for the given canonical article URL + private func summaryRequest(articleURL: URL, cachePolicy: WMFCachePolicy? = nil) throws -> URLRequest { + let url = try summaryURL(articleURL: articleURL) + + if let urlRequest = urlRequest(from: url, cachePolicy: cachePolicy) { + return urlRequest + } else { + throw ArticleFetcherError.unableToGenerateURLRequest + } + } + + /// Fetches ArticleSummaries from the Page Content Service for the given articleKeys + @discardableResult public func fetchArticleSummaryResponsesForArticles(withKeys articleKeys: [WMFInMemoryURLKey], cachePolicy: URLRequest.CachePolicy? = nil, completion: @escaping ([WMFInMemoryURLKey: ArticleSummary]) -> Void) -> [URLSessionTask] { + + var tasks: [URLSessionTask] = [] + articleKeys.asyncMapToDictionary(block: { (articleKey, asyncMapCompletion) in + let task = fetchSummaryForArticle(with: articleKey, cachePolicy: cachePolicy, completion: { (responseObject, response, error) in + asyncMapCompletion(articleKey, responseObject) + }) + if let task = task { + tasks.append(task) + } + }, completion: completion) + + return tasks + } + + /// Fetches a single ArticleSummary or the given articleKey from the Page Content Service + @discardableResult public func fetchSummaryForArticle(with articleKey: WMFInMemoryURLKey, cachePolicy: URLRequest.CachePolicy? = nil, completion: @escaping (ArticleSummary?, URLResponse?, Error?) -> Swift.Void) -> URLSessionTask? { + do { + guard let articleURL = articleKey.url else { + throw Fetcher.invalidParametersError + } + + let request: URLRequest + if let cachePolicy = cachePolicy { + request = try summaryRequest(articleURL: articleURL, cachePolicy: .foundation(cachePolicy)) + } else { + request = try summaryRequest(articleURL: articleURL) + } + + return trackedJSONDecodableTask(with: request) { (result: Result, response) in + switch result { + case .success(let summary): + summary.languageVariantCode = articleKey.languageVariantCode + completion(summary, response, nil) + case .failure(let error): + completion(nil, response, error) + } + } + } catch let error { + completion(nil, nil, error) + return nil + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleFullWidthImageCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleFullWidthImageCollectionViewCell.swift new file mode 100644 index 0000000..8035317 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleFullWidthImageCollectionViewCell.swift @@ -0,0 +1,158 @@ +import UIKit + +@objc(WMFArticleFullWidthImageCollectionViewCell) +open class ArticleFullWidthImageCollectionViewCell: ArticleCollectionViewCell { + public let saveButton = SaveButton() + + fileprivate let headerBackgroundView = UIView() + + public var headerBackgroundColor: UIColor? { + get { + return headerBackgroundView.backgroundColor + } + set { + headerBackgroundView.backgroundColor = newValue + titleLabel.backgroundColor = newValue + descriptionLabel.backgroundColor = newValue + } + } + + public var isHeaderBackgroundViewHidden: Bool { + get { + return headerBackgroundView.superview == nil + } + set { + if newValue { + headerBackgroundView.removeFromSuperview() + } else { + contentView.insertSubview(headerBackgroundView, at: 0) + } + } + } + + var saveButtonObservation: NSKeyValueObservation? + + override open func setup() { + let extractLabel = UILabel() + extractLabel.isOpaque = true + extractLabel.numberOfLines = 4 + addSubview(extractLabel) + self.extractLabel = extractLabel + super.setup() + descriptionLabel.numberOfLines = 2 + titleLabel.numberOfLines = 0 + + saveButton.isOpaque = true + + contentView.addSubview(saveButton) + + saveButton.verticalPadding = 8 + saveButton.rightPadding = 16 + saveButton.leftPadding = 12 + saveButton.saveButtonState = .longSave + saveButton.titleLabel?.numberOfLines = 0 + + saveButtonObservation = saveButton.observe(\.titleLabel?.text) { [weak self] (saveButton, change) in + self?.setNeedsLayout() + } + } + + deinit { + saveButtonObservation?.invalidate() + } + + open override func reset() { + super.reset() + spacing = 6 + imageViewDimension = 150 + } + + open override func updateBackgroundColorOfLabels() { + super.updateBackgroundColorOfLabels() + if !isHeaderBackgroundViewHidden { + titleLabel.backgroundColor = headerBackgroundColor + descriptionLabel.backgroundColor = headerBackgroundColor + } + saveButton.backgroundColor = labelBackgroundColor + saveButton.titleLabel?.backgroundColor = labelBackgroundColor + } + + open override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + saveButton.titleLabel?.font = UIFont.wmf_font(saveButtonTextStyle, compatibleWithTraitCollection: traitCollection) + } + + public var isSaveButtonHidden = false { + didSet { + saveButton.isHidden = isSaveButtonHidden + setNeedsLayout() + } + } + + open override var isSwipeEnabled: Bool { + return isSaveButtonHidden + } + + open override func updateAccessibilityElements() { + super.updateAccessibilityElements() + if !isSaveButtonHidden { + var updatedAccessibilityElements = accessibilityElements ?? [] + updatedAccessibilityElements.append(saveButton) + accessibilityElements = updatedAccessibilityElements + } + } + + open override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let widthMinusMargins = layoutWidth(for: size) + var origin = CGPoint(x: layoutMargins.left, y: 0) + + if !isImageViewHidden { + if apply { + imageView.frame = CGRect(x: 0, y: 0, width: size.width, height: imageViewDimension) + } + origin.y += imageViewDimension + } + + origin.y += layoutMargins.top + spacing + + origin.y += titleLabel.wmf_preferredHeight(at: origin, maximumWidth: widthMinusMargins, alignedBy: articleSemanticContentAttribute, spacing: spacing, apply: apply) + origin.y += descriptionLabel.wmf_preferredHeight(at: origin, maximumWidth: widthMinusMargins, alignedBy: articleSemanticContentAttribute, spacing: spacing, apply: apply) + + if apply { + titleLabel.isHidden = !titleLabel.wmf_hasText + descriptionLabel.isHidden = !descriptionLabel.wmf_hasText + } + + if !isHeaderBackgroundViewHidden && apply { + headerBackgroundView.frame = CGRect(x: 0, y: 0, width: size.width, height: origin.y) + } + + if let extractLabel = extractLabel, extractLabel.wmf_hasText { + origin.y += spacing // double spacing before extract + origin.y += extractLabel.wmf_preferredHeight(at: origin, maximumWidth: widthMinusMargins, alignedBy: articleSemanticContentAttribute, spacing: spacing, apply: apply) + if apply { + extractLabel.isHidden = false + } + } else if apply { + extractLabel?.isHidden = true + } + + if !isSaveButtonHidden { + origin.y += spacing - 1 + let saveButtonFrame = saveButton.wmf_preferredFrame(at: origin, maximumWidth: widthMinusMargins, horizontalAlignment: isDeviceRTL ? .right : .left, apply: apply) + origin.y += saveButtonFrame.height - 2 * saveButton.verticalPadding + } else { + origin.y += spacing + } + + origin.y += layoutMargins.bottom + return CGSize(width: size.width, height: origin.y) + } +} + +public class ArticleFullWidthImageExploreCollectionViewCell: ArticleFullWidthImageCollectionViewCell { + override open func apply(theme: Theme) { + super.apply(theme: theme) + setBackgroundColors(theme.colors.cardBackground, selected: theme.colors.selectedCardBackground) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleLocationAuthorizationCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleLocationAuthorizationCollectionViewCell.swift new file mode 100644 index 0000000..0640413 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleLocationAuthorizationCollectionViewCell.swift @@ -0,0 +1,65 @@ +import UIKit + +protocol ArticleLocationAuthorizationCollectionViewCellDelegate: AnyObject { + func articleLocationAuthorizationCollectionViewCellDidTapAuthorize(_ cell: ArticleLocationAuthorizationCollectionViewCell) +} + +class ArticleLocationAuthorizationCollectionViewCell: ArticleLocationExploreCollectionViewCell { + let authorizeButton: UIButton = UIButton() + let authorizeDescriptionLabel: UILabel = UILabel() + weak var authorizationDelegate: ArticleLocationAuthorizationCollectionViewCellDelegate? + + override func setup() { + super.setup() + authorizeButton.layer.cornerRadius = 5 + authorizeButton.titleLabel?.numberOfLines = 2 + authorizeButton.titleLabel?.textAlignment = .center + authorizeButton.contentEdgeInsets = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15) + authorizeButton.addTarget(self, action: #selector(authorizeButtonPressed(_:)), for: .touchUpInside) + addSubview(authorizeButton) + + authorizeDescriptionLabel.textAlignment = .natural + authorizeDescriptionLabel.numberOfLines = 0 + addSubview(authorizeDescriptionLabel) + } + + override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + authorizeButton.titleLabel?.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection) + authorizeDescriptionLabel.font = UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + } + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let size = super.sizeThatFits(size, apply: apply) + var origin = CGPoint(x: layoutMargins.left, y: size.height + (spacing * 3)) + let widthForLabels = size.width - layoutMargins.left - layoutMargins.right + let authorizeSpacing = 3 * spacing + let horizontalAlignment: HorizontalAlignment = isDeviceRTL ? .right : .left + origin.y += authorizeButton.wmf_preferredHeight(at: origin, maximumWidth: widthForLabels, minimumWidth: widthForLabels, horizontalAlignment: .center, spacing: authorizeSpacing, apply: apply) + origin.y += authorizeDescriptionLabel.wmf_preferredHeight(at: origin, maximumWidth: widthForLabels, horizontalAlignment: horizontalAlignment, spacing: authorizeSpacing, apply: apply) + return CGSize(width: size.width, height: origin.y + layoutMargins.bottom) + } + + public func updateForLocationEnabled() { + authorizeButton.setTitle(WMFLocalizedString("places-location-enabled", value: "Location enabled", comment: "Title for button informing user that they successfully enabled location services"), for: .normal) + authorizeButton.removeTarget(self, action: #selector(authorizeButtonPressed(_:)), for: .touchUpInside) + authorizeButton.isEnabled = false + } + + override func apply(theme: Theme) { + super.apply(theme: theme) + authorizeButton.backgroundColor = theme.colors.cardButtonBackground + authorizeButton.setTitleColor(theme.colors.link, for: .normal) + authorizeButton.setTitleColor(theme.colors.secondaryText, for: .disabled) + authorizeDescriptionLabel.textColor = theme.colors.secondaryText + backgroundView?.backgroundColor = theme.colors.cardBackground + } + + @objc public func authorizeButtonPressed(_ sender: Any?) { + authorizationDelegate?.articleLocationAuthorizationCollectionViewCellDidTapAuthorize(self) + } + + override var isSwipeEnabled: Bool { + return false + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleLocationCellUpdating.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleLocationCellUpdating.swift new file mode 100644 index 0000000..4232815 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleLocationCellUpdating.swift @@ -0,0 +1,18 @@ +import Foundation + +extension ArticleLocationCollectionViewCell { + func update(userLocation: CLLocation?, heading: CLHeading?) { + guard let articleLocation = articleLocation, let userLocation = userLocation else { + configureForUnknownDistance() + return + } + + distance = articleLocation.distance(from: userLocation) + + if let heading = heading { + bearing = userLocation.wmf_bearing(to: articleLocation, forCurrentHeading: heading) + } else { + bearing = userLocation.wmf_bearing(to: articleLocation) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleLocationCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleLocationCollectionViewCell.swift new file mode 100644 index 0000000..29f7ea3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleLocationCollectionViewCell.swift @@ -0,0 +1,137 @@ +import UIKit + +class ArticleLocationCollectionViewCell: ArticleCollectionViewCell { + let compassView: WMFCompassView = WMFCompassView() + let compassViewDimension: CGFloat = 104 + let distanceLabel: UILabel = UILabel() + let distanceLabelBackground: UIView = UIView() + let distanceTextStyle: DynamicTextStyle = .caption1 + var articleLocation: CLLocation? + var userLocation: CLLocation? + + override func setup() { + super.setup() + insertSubview(compassView, belowSubview: imageView) + isImageViewHidden = false + imageView.layer.cornerRadius = round(0.5*imageViewDimension) + imageView.layer.masksToBounds = true + addSubview(distanceLabelBackground) + addSubview(distanceLabel) + distanceLabelBackground.layer.cornerRadius = 2.0 + titleLabel.numberOfLines = 0 + descriptionLabel.numberOfLines = 2 + updateDistanceLabelBackgroundBorder() + } + + override func reset() { + super.reset() + titleTextStyle = .georgiaTitle3 + descriptionTextStyle = .subheadline + imageViewDimension = 72 + imageView.image = #imageLiteral(resourceName: "compass-w") + } + + override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + distanceLabel.font = UIFont.wmf_font(distanceTextStyle, compatibleWithTraitCollection: traitCollection) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateDistanceLabelBackgroundBorder() + } + + private func updateDistanceLabelBackgroundBorder() { + let displayScale = max(1, traitCollection.displayScale) + distanceLabelBackground.layer.borderWidth = 1.0 / displayScale + } + + override open func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let size = super.sizeThatFits(size, apply: apply) + let isLTR = articleSemanticContentAttribute != .forceRightToLeft + + let layoutMargins = calculatedLayoutMargins + + let minHeight = compassViewDimension + let hSpaceBetweenCompassAndLabels: CGFloat = 10 + let widthForLabels = size.width - layoutMargins.left - compassViewDimension - hSpaceBetweenCompassAndLabels - layoutMargins.right + + let x = isLTR ? layoutMargins.left + compassViewDimension + hSpaceBetweenCompassAndLabels : layoutMargins.left + + var origin = CGPoint(x: x, y: layoutMargins.top) + + let titleLabelFrame = titleLabel.wmf_preferredFrame(at: origin, maximumWidth: widthForLabels, alignedBy: articleSemanticContentAttribute, apply: apply) + origin.y += titleLabelFrame.layoutHeight(with: spacing) + + + if descriptionLabel.wmf_hasAnyNonWhitespaceText { + let descriptionLabelFrame = descriptionLabel.wmf_preferredFrame(at: origin, maximumWidth: widthForLabels, alignedBy: articleSemanticContentAttribute, apply: apply) + origin.y += descriptionLabelFrame.layoutHeight(with: spacing) + descriptionLabel.isHidden = false + } else { + descriptionLabel.isHidden = true + } + + let distanceLabelHorizontalPadding: CGFloat = 5 + let distanceLabelVerticalPadding: CGFloat = 5 + let distanceLabelExtraTopMargin: CGFloat = 3 + let distanceLabelFrame = distanceLabel.wmf_preferredFrame(at: CGPoint(x: origin.x + distanceLabelHorizontalPadding, y: origin.y + distanceLabelVerticalPadding + distanceLabelExtraTopMargin), maximumWidth: widthForLabels - 2 * distanceLabelHorizontalPadding, alignedBy: articleSemanticContentAttribute, apply: apply) + + let distanceLabelBackgroundFrame = distanceLabelFrame.inset(by: UIEdgeInsets(top: 0 - distanceLabelVerticalPadding, left: 0 - distanceLabelHorizontalPadding, bottom: 0 - distanceLabelVerticalPadding, right: 0 - distanceLabelHorizontalPadding)) + + origin.y += distanceLabelBackgroundFrame.height + distanceLabelExtraTopMargin + spacing + + if apply { + distanceLabelBackground.frame = distanceLabelBackgroundFrame + } + + origin.y += layoutMargins.bottom + let height = max(origin.y, minHeight) + + if apply && !isImageViewHidden { + let compassViewY = floor(0.5 * (height - compassViewDimension)) + let compassViewX = isLTR ? layoutMargins.left : size.width - layoutMargins.right - compassViewDimension + compassView.frame = CGRect(x: compassViewX, y: compassViewY, width: compassViewDimension, height: compassViewDimension) + + let imageViewY = floor(0.5 * (height - imageViewDimension)) + let imageViewDelta = floor(0.5 * (compassViewDimension - imageViewDimension)) + let imageViewX = isLTR ? layoutMargins.left + imageViewDelta : size.width - layoutMargins.right - compassViewDimension + imageViewDelta + imageView.frame = CGRect(x: imageViewX, y: imageViewY, width: imageViewDimension, height: imageViewDimension) + } + + return CGSize(width: size.width, height: height) + } + + public func configureForUnknownDistance() { + distanceLabel.text = WMFLocalizedString("places-unknown-distance", value: "unknown distance", comment: "Indicates that a place is an unknown distance away").lowercased() + setNeedsLayout() + } + + var distance: CLLocationDistance = 0 { + didSet { + distanceLabel.text = NSString.wmf_localizedString(forDistance: distance) + setNeedsLayout() + } + } + + var bearing: CLLocationDegrees = 0 { + didSet { + compassView.angleRadians = bearing * .pi / 180 + } + } + + override func apply(theme: Theme) { + super.apply(theme: theme) + imageView.backgroundColor = .green600 + distanceLabel.textColor = theme.colors.secondaryText + distanceLabelBackground.layer.borderColor = theme.colors.distanceBorder.cgColor + compassView.lineColor = theme.colors.accent + } +} + +class ArticleLocationExploreCollectionViewCell: ArticleLocationCollectionViewCell { + override open func apply(theme: Theme) { + super.apply(theme: theme) + setBackgroundColors(theme.colors.cardBackground, selected: theme.colors.selectedCardBackground) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleLocationCollectionViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleLocationCollectionViewController.swift new file mode 100644 index 0000000..3bf94eb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleLocationCollectionViewController.swift @@ -0,0 +1,233 @@ +import UIKit + +class ArticleLocationCollectionViewController: ColumnarCollectionViewController, DetailPresentingFromContentGroup { + var articleURLs: [URL] { + didSet { + collectionView.reloadData() + } + } + let dataStore: MWKDataStore + fileprivate let locationManager = LocationManager() + private var feedFunnelContext: FeedFunnelContext? + private var previewedIndexPath: IndexPath? + + let contentGroupIDURIString: String? + + required init(articleURLs: [URL], dataStore: MWKDataStore, contentGroup: WMFContentGroup?, theme: Theme) { + self.articleURLs = articleURLs + self.dataStore = dataStore + contentGroupIDURIString = contentGroup?.objectID.uriRepresentation().absoluteString + super.init() + self.theme = theme + if contentGroup != nil { + self.feedFunnelContext = FeedFunnelContext(contentGroup) + } + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) not supported") + } + + override func viewDidLoad() { + super.viewDidLoad() + layoutManager.register(ArticleLocationCollectionViewCell.self, forCellWithReuseIdentifier: ArticleLocationCollectionViewCell.identifier, addPlaceholder: true) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + locationManager.delegate = self + if locationManager.isAuthorized { + locationManager.startMonitoringLocation() + } + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + locationManager.delegate = nil + locationManager.stopMonitoringLocation() + if isMovingFromParent, let context = feedFunnelContext { + FeedFunnel.shared.logFeedCardClosed(for: context, maxViewed: maxViewed) + } + } + + func articleURL(at indexPath: IndexPath) -> URL { + return articleURLs[indexPath.item] + } + + override func collectionView(_ collectionView: UICollectionView, estimatedHeightForItemAt indexPath: IndexPath, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + var estimate = ColumnarCollectionViewLayoutHeightEstimate(precalculated: false, height: 150) + guard let placeholderCell = layoutManager.placeholder(forCellWithReuseIdentifier: ArticleLocationCollectionViewCell.identifier) as? ArticleLocationCollectionViewCell else { + return estimate + } + placeholderCell.layoutMargins = layout.itemLayoutMargins + configure(cell: placeholderCell, forItemAt: indexPath, layoutOnly: true) + estimate.height = placeholderCell.sizeThatFits(CGSize(width: columnWidth, height: UIView.noIntrinsicMetric), apply: false).height + estimate.precalculated = true + return estimate + } + + override func metrics(with size: CGSize, readableWidth: CGFloat, layoutMargins: UIEdgeInsets) -> ColumnarCollectionViewLayoutMetrics { + return ColumnarCollectionViewLayoutMetrics.tableViewMetrics(with: size, readableWidth: readableWidth, layoutMargins: layoutMargins) + } + + // MARK: - CollectionViewFooterDelegate + + override func collectionViewFooterButtonWasPressed(_ collectionViewFooter: CollectionViewFooter) { + navigationController?.popViewController(animated: true) + } + + // MARK: ArticlePreviewingDelegate + + override func shareArticlePreviewActionSelected(with articleController: ArticleViewController, shareActivityController: UIActivityViewController) { + guard let context = feedFunnelContext else { + super.shareArticlePreviewActionSelected(with: articleController, shareActivityController: shareActivityController) + return + } + super.shareArticlePreviewActionSelected(with: articleController, shareActivityController: shareActivityController) + FeedFunnel.shared.logFeedDetailShareTapped(for: context, index: previewedIndexPath?.item, midnightUTCDate: context.midnightUTCDate) + } + + override func readMoreArticlePreviewActionSelected(with articleController: ArticleViewController) { + guard let context = feedFunnelContext else { + super.readMoreArticlePreviewActionSelected(with: articleController) + return + } + articleController.wmf_removePeekableChildViewControllers() + push(articleController, context: context, index: previewedIndexPath?.item, animated: true) + } + + override func saveArticlePreviewActionSelected(with articleController: ArticleViewController, didSave: Bool, articleURL: URL) { + guard let context = feedFunnelContext else { + super.saveArticlePreviewActionSelected(with: articleController, didSave: didSave, articleURL: articleURL) + return + } + if didSave { + ReadingListsFunnel.shared.logSaveInFeed(context: context, articleURL: articleURL, index: previewedIndexPath?.item) + } else { + ReadingListsFunnel.shared.logUnsaveInFeed(context: context, articleURL: articleURL, index: previewedIndexPath?.item) + } + } + + func updateLocationOnVisibleCells() { + for cell in collectionView.visibleCells { + guard let locationCell = cell as? ArticleLocationCollectionViewCell else { + continue + } + locationCell.update(userLocation: locationManager.location, heading: locationManager.heading) + } + } +} + +// MARK: - UICollectionViewDataSource +extension ArticleLocationCollectionViewController { + override func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } + + var numberOfItems: Int { + return articleURLs.count + } + + override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return numberOfItems + } + + private func configure(cell: UICollectionViewCell, forItemAt indexPath: IndexPath, layoutOnly: Bool) { + guard let cell = cell as? ArticleLocationCollectionViewCell else { + return + } + + let url = articleURL(at: indexPath) + guard let article = dataStore.fetchArticle(with: url) else { + return + } + + var userLocation: CLLocation? + var userHeading: CLHeading? + + if locationManager.isUpdating { + userLocation = locationManager.location + userHeading = locationManager.heading + } + + cell.articleLocation = article.location + cell.update(userLocation: userLocation, heading: userHeading) + + cell.configure(article: article, displayType: .pageWithLocation, index: indexPath.row, theme: theme, layoutOnly: layoutOnly) + } + + override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ArticleLocationCollectionViewCell.identifier, for: indexPath) + configure(cell: cell, forItemAt: indexPath, layoutOnly: false) + return cell + } +} + +// MARK: - LocationManagerDelegate +extension ArticleLocationCollectionViewController: LocationManagerDelegate { + func locationManager(_ locationManager: LocationManagerProtocol, didUpdate location: CLLocation) { + updateLocationOnVisibleCells() + } + + func locationManager(_ locationManager: LocationManagerProtocol, didUpdate heading: CLHeading) { + updateLocationOnVisibleCells() + } + + func locationManager(_ locationManager: LocationManagerProtocol, didUpdateAuthorized authorized: Bool) { + if authorized { + locationManager.startMonitoringLocation() + } + } +} + +// MARK: - UICollectionViewDelegate +extension ArticleLocationCollectionViewController { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + if let context = feedFunnelContext { + FeedFunnel.shared.logArticleInFeedDetailReadingStarted(for: context, index: indexPath.item, maxViewed: maxViewed) + } + navigate(to: articleURLs[indexPath.item]) + } +} + +// MARK: - CollectionViewContextMenuShowing +extension ArticleLocationCollectionViewController: CollectionViewContextMenuShowing { + func articleViewController(for indexPath: IndexPath) -> ArticleViewController? { + let articleURL = articleURL(at: indexPath) + let articleViewController = ArticleViewController(articleURL: articleURL, dataStore: dataStore, theme: theme) + return articleViewController + } + + func previewingViewController(for indexPath: IndexPath, at location: CGPoint) -> UIViewController? { + guard let articleViewController = articleViewController(for: indexPath) else { + return nil + } + let articleURL = articleViewController.articleURL + articleViewController.articlePreviewingDelegate = self + articleViewController.wmf_addPeekableChildViewController(for: articleURL, dataStore: dataStore, theme: theme) + if let context = feedFunnelContext { + FeedFunnel.shared.logArticleInFeedDetailPreviewed(for: context, index: indexPath.item) + } + previewedIndexPath = indexPath + return articleViewController + } + + var poppingIntoVCCompletion: () -> Void { + return { + if let context = self.feedFunnelContext { + FeedFunnel.shared.logArticleInFeedDetailReadingStarted(for: context, index: self.previewedIndexPath?.item, maxViewed: self.maxViewed) + } + } + } +} + +// MARK: - Reading lists event logging +extension ArticleLocationCollectionViewController: EventLoggingEventValuesProviding { + var eventLoggingCategory: EventLoggingCategory { + return .places + } + + var eventLoggingLabel: EventLoggingLabel? { + return nil + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticlePeekPreviewViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticlePeekPreviewViewController.swift new file mode 100644 index 0000000..13ae193 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticlePeekPreviewViewController.swift @@ -0,0 +1,99 @@ +import UIKit +import WMF + +@objc(WMFArticlePeekPreviewViewController) +class ArticlePeekPreviewViewController: UIViewController, Peekable { + + let articleURL: URL + fileprivate let dataStore: MWKDataStore + fileprivate var theme: Theme + fileprivate let activityIndicatorView: UIActivityIndicatorView = UIActivityIndicatorView(style: .large) + fileprivate let expandedArticleView = ArticleFullWidthImageCollectionViewCell() + + @objc required init(articleURL: URL, dataStore: MWKDataStore, theme: Theme) { + self.articleURL = articleURL + self.dataStore = dataStore + self.theme = theme + super.init(nibName: nil, bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + return nil + } + + private var isFetched = false + @objc func fetchArticle(_ completion:(() -> Void)? = nil ) { + assert(Thread.isMainThread) + guard !isFetched else { + completion?() + return + } + isFetched = true + guard let key = articleURL.wmf_inMemoryKey else { + completion?() + return + } + dataStore.articleSummaryController.updateOrCreateArticleSummaryForArticle(withKey: key) { (article, _) in + defer { + completion?() + } + guard let article = article else { + self.activityIndicatorView.stopAnimating() + return + } + self.updateView(with: article) + } + } + + func updatePreferredContentSize(for contentWidth: CGFloat) { + var updatedContentSize = expandedArticleView.sizeThatFits(CGSize(width: contentWidth, height: UIView.noIntrinsicMetric), apply: true) + updatedContentSize.width = contentWidth // extra protection to ensure this stays == width + parent?.preferredContentSize = updatedContentSize + preferredContentSize = updatedContentSize + } + + fileprivate func updateView(with article: WMFArticle) { + expandedArticleView.configure(article: article, displayType: .pageWithPreview, index: 0, theme: theme, layoutOnly: false) + expandedArticleView.isSaveButtonHidden = true + expandedArticleView.extractLabel?.numberOfLines = 5 + expandedArticleView.frame = view.bounds + expandedArticleView.isHeaderBackgroundViewHidden = false + expandedArticleView.headerBackgroundColor = theme.colors.midBackground + expandedArticleView.isHidden = false + + activityIndicatorView.stopAnimating() + } + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = theme.colors.paperBackground + activityIndicatorView.color = theme.isDark ? .white : .gray + activityIndicatorView.startAnimating() + view.addSubview(activityIndicatorView) + expandedArticleView.isHidden = true + view.addSubview(expandedArticleView) + expandedArticleView.updateFonts(with: traitCollection) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + fetchArticle { + self.updatePreferredContentSize(for: self.view.bounds.width) + } + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + expandedArticleView.frame = view.bounds + activityIndicatorView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + guard viewIfLoaded != nil else { + return + } + expandedArticleView.updateFonts(with: traitCollection) + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticlePlace.swift b/Apps/Wikipedia/Wikipedia/Code/ArticlePlace.swift new file mode 100644 index 0000000..adb24d3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticlePlace.swift @@ -0,0 +1,40 @@ +import UIKit +import WMF + +class DebugAnnotation: MapAnnotation { + public var title: String? + public var subtitle: String? + + override func setup() { + self.title = nil + self.subtitle = nil + } +} + +class ArticlePlace: MapAnnotation { + public var nextCoordinate: CLLocationCoordinate2D? + public let title: String? + public let subtitle: String? + public let articles: [WMFArticle] + public let identifier: Int + + init?(coordinate: CLLocationCoordinate2D, nextCoordinate: CLLocationCoordinate2D?, articles: [WMFArticle], identifier: Int) { + self.title = nil + self.subtitle = nil + self.nextCoordinate = nextCoordinate + self.articles = articles + self.identifier = identifier + super.init(coordinate: coordinate) + } + + public static func identifierForArticles(articles: [WMFArticle]) -> Int { + var hash = 0 + for article in articles { + guard let key = article.key else { + continue + } + hash ^= key.hash + } + return hash + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticlePlaceView.swift b/Apps/Wikipedia/Wikipedia/Code/ArticlePlaceView.swift new file mode 100644 index 0000000..22db627 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticlePlaceView.swift @@ -0,0 +1,527 @@ +import UIKit +import WMF +import MapKit + +protocol ArticlePlaceViewDelegate: NSObjectProtocol { + func articlePlaceViewWasTapped(_ articlePlaceView: ArticlePlaceView) +} + +class ArticlePlaceView: MapAnnotationView { + static let smallDotImage = #imageLiteral(resourceName: "places-dot-small") + static let mediumDotImage = #imageLiteral(resourceName: "places-dot-medium") + + static let mediumOpaqueDotImage = #imageLiteral(resourceName: "places-dot-medium-opaque") + static let mediumOpaqueDotOutlineImage = #imageLiteral(resourceName: "places-dot-outline-medium") + + static let extraMediumOpaqueDotImage = #imageLiteral(resourceName: "places-dot-extra-medium-opaque") + static let extraMediumOpaqueDotOutlineImage = #imageLiteral(resourceName: "places-dot-outline-extra-medium") + + static let largeOpaqueDotImage = #imageLiteral(resourceName: "places-dot-large-opaque") + static let largeOpaqueDotOutlineImage = #imageLiteral(resourceName: "places-dot-outline-large") + + static let extraLargeOpaqueDotImage = #imageLiteral(resourceName: "places-dot-extra-large-opaque") + static let extraLargeOpaqueDotOutlineImage = #imageLiteral(resourceName: "places-dot-outline-extra-large ") + + static let mediumPlaceholderImage = #imageLiteral(resourceName: "places-w-medium") + static let largePlaceholderImage = #imageLiteral(resourceName: "places-w-large") + + static let extraMediumPlaceholderImage = #imageLiteral(resourceName: "places-w-extra-medium") + static let extraLargePlaceholderImage = #imageLiteral(resourceName: "places-w-extra-large") + + public weak var delegate: ArticlePlaceViewDelegate? + + var imageView: UIView! + private var imageImageView: UIImageView! + private var imageImagePlaceholderView: UIImageView! + private var imageOutlineView: UIView! + private var imageBackgroundView: UIView! + private var selectedImageView: UIView! + private var selectedImageImageView: UIImageView! + private var selectedImageImagePlaceholderView: UIImageView! + private var selectedImageOutlineView: UIView! + private var selectedImageBackgroundView: UIView! + private var dotView: UIView! + private var groupView: UIView! + private var countLabel: UILabel! + private var dimension: CGFloat! + private var collapsedDimension: CGFloat! + var groupDimension: CGFloat! + var imageDimension: CGFloat! + var selectedImageButton: UIButton! + + private var alwaysShowImage = false + + private let selectionAnimationDuration = 0.3 + private let springDamping: CGFloat = 0.5 + private let crossFadeRelativeHalfDuration: TimeInterval = 0.1 + private let alwaysRasterize = true // set this or rasterize on animations, not both + private let rasterizeOnAnimations = false + + override func setup() { + selectedImageView = UIView() + imageView = UIView() + selectedImageImageView = UIImageView() + imageImageView = UIImageView() + selectedImageImageView.accessibilityIgnoresInvertColors = true + imageImageView.accessibilityIgnoresInvertColors = true + countLabel = UILabel() + dotView = UIView() + groupView = UIView() + imageOutlineView = UIView() + selectedImageOutlineView = UIView() + imageBackgroundView = UIView() + selectedImageBackgroundView = UIView() + selectedImageButton = UIButton() + imageImagePlaceholderView = UIImageView() + selectedImageImagePlaceholderView = UIImageView() + + let scale = ArticlePlaceView.mediumDotImage.scale + let mediumOpaqueDotImage = ArticlePlaceView.mediumOpaqueDotImage + let mediumOpaqueDotOutlineImage = ArticlePlaceView.mediumOpaqueDotOutlineImage + let largeOpaqueDotImage = ArticlePlaceView.largeOpaqueDotImage + let largeOpaqueDotOutlineImage = ArticlePlaceView.largeOpaqueDotOutlineImage + + let mediumPlaceholderImage = ArticlePlaceView.mediumPlaceholderImage + let largePlaceholderImage = ArticlePlaceView.largePlaceholderImage + + collapsedDimension = ArticlePlaceView.smallDotImage.size.width + groupDimension = ArticlePlaceView.mediumDotImage.size.width + dimension = largeOpaqueDotOutlineImage.size.width + imageDimension = mediumOpaqueDotOutlineImage.size.width + + let gravity = CALayerContentsGravity.bottomRight + + isPlaceholderHidden = false + + frame = CGRect(x: 0, y: 0, width: dimension, height: dimension) + + dotView.bounds = CGRect(x: 0, y: 0, width: collapsedDimension, height: collapsedDimension) + dotView.layer.contentsGravity = gravity + dotView.layer.contentsScale = scale + dotView.layer.contents = ArticlePlaceView.smallDotImage.cgImage + dotView.center = CGPoint(x: 0.5*bounds.size.width, y: 0.5*bounds.size.height) + addSubview(dotView) + + groupView.bounds = CGRect(x: 0, y: 0, width: groupDimension, height: groupDimension) + groupView.layer.contentsGravity = gravity + groupView.layer.contentsScale = scale + groupView.layer.contents = ArticlePlaceView.mediumDotImage.cgImage + addSubview(groupView) + + imageView.bounds = CGRect(x: 0, y: 0, width: imageDimension, height: imageDimension) + imageView.layer.rasterizationScale = scale + addSubview(imageView) + + imageBackgroundView.frame = imageView.bounds + imageBackgroundView.layer.contentsGravity = gravity + imageBackgroundView.layer.contentsScale = scale + imageBackgroundView.layer.contents = mediumOpaqueDotImage.cgImage + imageView.addSubview(imageBackgroundView) + + imageImagePlaceholderView.frame = imageView.bounds + imageImagePlaceholderView.contentMode = .center + imageImagePlaceholderView.image = mediumPlaceholderImage + imageView.addSubview(imageImagePlaceholderView) + + var inset: CGFloat = 3.5 + var imageViewFrame = imageView.bounds.inset(by: UIEdgeInsets(top: inset, left: inset, bottom: inset, right: inset)) + imageViewFrame.origin = CGPoint(x: frame.origin.x + inset, y: frame.origin.y + inset) + imageImageView.frame = imageViewFrame + imageImageView.contentMode = .scaleAspectFill + imageImageView.layer.masksToBounds = true + imageImageView.layer.cornerRadius = imageImageView.bounds.size.width * 0.5 + imageImageView.backgroundColor = UIColor.white + imageView.addSubview(imageImageView) + + imageOutlineView.frame = imageView.bounds + imageOutlineView.layer.contentsGravity = gravity + imageOutlineView.layer.contentsScale = scale + imageOutlineView.layer.contents = mediumOpaqueDotOutlineImage.cgImage + imageView.addSubview(imageOutlineView) + + selectedImageView.bounds = bounds + selectedImageView.layer.rasterizationScale = scale + addSubview(selectedImageView) + + selectedImageBackgroundView.frame = selectedImageView.bounds + selectedImageBackgroundView.layer.contentsGravity = gravity + selectedImageBackgroundView.layer.contentsScale = scale + selectedImageBackgroundView.layer.contents = largeOpaqueDotImage.cgImage + selectedImageView.addSubview(selectedImageBackgroundView) + + selectedImageImagePlaceholderView.frame = selectedImageView.bounds + selectedImageImagePlaceholderView.contentMode = .center + selectedImageImagePlaceholderView.image = largePlaceholderImage + selectedImageView.addSubview(selectedImageImagePlaceholderView) + + inset = imageDimension > 40 ? 3.5 : 5.5 + imageViewFrame = selectedImageView.bounds.inset(by: UIEdgeInsets(top: inset, left: inset, bottom: inset, right: inset)) + imageViewFrame.origin = CGPoint(x: frame.origin.x + inset, y: frame.origin.y + inset) + selectedImageImageView.frame = imageViewFrame + selectedImageImageView.contentMode = .scaleAspectFill + selectedImageImageView.layer.cornerRadius = selectedImageImageView.bounds.size.width * 0.5 + selectedImageImageView.layer.masksToBounds = true + selectedImageImageView.backgroundColor = UIColor.white + selectedImageView.addSubview(selectedImageImageView) + + selectedImageOutlineView.frame = selectedImageView.bounds + selectedImageOutlineView.layer.contentsGravity = gravity + selectedImageOutlineView.layer.contentsScale = scale + selectedImageOutlineView.layer.contents = largeOpaqueDotOutlineImage.cgImage + selectedImageView.addSubview(selectedImageOutlineView) + + selectedImageButton.frame = selectedImageView.bounds + selectedImageButton.accessibilityTraits = UIAccessibilityTraits.none + selectedImageView.addSubview(selectedImageButton) + + countLabel.frame = groupView.bounds + countLabel.textColor = UIColor.white + countLabel.textAlignment = .center + countLabel.font = UIFont.boldSystemFont(ofSize: 16) + groupView.addSubview(countLabel) + + prepareForReuse() + super.setup() + + updateLayout() + update(withArticlePlace: annotation as? ArticlePlace) + } + + func set(alwaysShowImage: Bool, animated: Bool) { + self.alwaysShowImage = alwaysShowImage + let scale = collapsedDimension/imageDimension + let imageViewScaleDownTransform = CGAffineTransform(scaleX: scale, y: scale) + let dotViewScaleUpTransform = CGAffineTransform(scaleX: 1.0/scale, y: 1.0/scale) + if alwaysShowImage { + loadImage() + imageView.alpha = 0 + imageView.isHidden = false + dotView.alpha = 1 + dotView.isHidden = false + imageView.transform = imageViewScaleDownTransform + dotView.transform = CGAffineTransform.identity + } else { + dotView.transform = dotViewScaleUpTransform + imageView.transform = CGAffineTransform.identity + imageView.alpha = 1 + imageView.isHidden = false + dotView.alpha = 0 + dotView.isHidden = false + } + + let transforms = { + if alwaysShowImage { + self.imageView.transform = CGAffineTransform.identity + self.dotView.transform = dotViewScaleUpTransform + + } else { + self.imageView.transform = imageViewScaleDownTransform + self.dotView.transform = CGAffineTransform.identity + } + } + let fadesIn = { + if alwaysShowImage { + self.imageView.alpha = 1 + } else { + self.dotView.alpha = 1 + } + } + let fadesOut = { + if alwaysShowImage { + self.dotView.alpha = 0 + } else { + self.imageView.alpha = 0 + } + } + + if animated && rasterizeOnAnimations { + self.imageView.layer.shouldRasterize = true + } + let done = { + if animated && self.rasterizeOnAnimations { + self.imageView.layer.shouldRasterize = false + } + guard let articlePlace = self.annotation as? ArticlePlace else { + return + } + self.updateDotAndImageHiddenState(with: articlePlace.articles.count) + } + if animated { + if alwaysShowImage { + UIView.animate(withDuration: 2*selectionAnimationDuration, delay: 0, usingSpringWithDamping: springDamping, initialSpringVelocity: 0, options: [.allowUserInteraction], animations: transforms, completion:nil) + UIView.animateKeyframes(withDuration: 2*selectionAnimationDuration, delay: 0, options: [.allowUserInteraction], animations: { + UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: self.crossFadeRelativeHalfDuration, animations:fadesIn) + UIView.addKeyframe(withRelativeStartTime: self.crossFadeRelativeHalfDuration, relativeDuration: self.crossFadeRelativeHalfDuration, animations:fadesOut) + }) { (didFinish) in + done() + } + } else { + UIView.animateKeyframes(withDuration: selectionAnimationDuration, delay: 0, options: [.allowUserInteraction], animations: { + UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1, animations:transforms) + UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5, animations:fadesIn) + UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5, animations:fadesOut) + }) { (didFinish) in + done() + } + } + } else { + transforms() + fadesIn() + fadesOut() + done() + } + } + + override func didMoveToSuperview() { + super.didMoveToSuperview() + guard superview != nil else { + selectedImageButton.removeTarget(self, action: #selector(selectedImageViewWasTapped), for: .touchUpInside) + return + } + + selectedImageButton.addTarget(self, action: #selector(selectedImageViewWasTapped), for: .touchUpInside) + } + + @objc func selectedImageViewWasTapped(_ sender: UIButton) { + delegate?.articlePlaceViewWasTapped(self) + } + + var zPosition: CGFloat = 1 { + didSet { + guard !isSelected else { + return + } + layer.zPosition = zPosition + } + } + + var isPlaceholderHidden: Bool = true { + didSet { + selectedImageImagePlaceholderView.isHidden = isPlaceholderHidden + imageImagePlaceholderView.isHidden = isPlaceholderHidden + imageImageView.isHidden = !isPlaceholderHidden + selectedImageImageView.isHidden = !isPlaceholderHidden + } + } + + private var shouldRasterize = false { + didSet { + imageView.layer.shouldRasterize = shouldRasterize + selectedImageView.layer.shouldRasterize = shouldRasterize + } + } + + private var isImageLoaded = false + func loadImage() { + guard !isImageLoaded, let articlePlace = annotation as? ArticlePlace, articlePlace.articles.count == 1 else { + return + } + + if alwaysRasterize { + shouldRasterize = false + } + isPlaceholderHidden = false + isImageLoaded = true + let article = articlePlace.articles[0] + if let thumbnailURL = article.thumbnailURL { + imageImageView.wmf_setImage(with: thumbnailURL, detectFaces: true, onGPU: true, failure: { (error) in + if self.alwaysRasterize { + self.shouldRasterize = true + } + }, success: { + self.selectedImageImageView.image = self.imageImageView.image + self.selectedImageImageView.layer.contentsRect = self.imageImageView.layer.contentsRect + self.isPlaceholderHidden = true + if self.alwaysRasterize { + self.shouldRasterize = true + } + }) + } + } + + func update(withArticlePlace articlePlace: ArticlePlace?) { + let articleCount = articlePlace?.articles.count ?? 1 + switch articleCount { + case 0: + zPosition = 1 + isPlaceholderHidden = false + imageImagePlaceholderView.image = #imageLiteral(resourceName: "places-show-more") + accessibilityLabel = WMFLocalizedString("places-accessibility-show-more", value:"Show more articles", comment:"Accessibility label for a button that shows more articles") + case 1: + zPosition = 1 + isImageLoaded = false + if isSelected || alwaysShowImage { + loadImage() + } + accessibilityLabel = articlePlace?.articles.first?.displayTitle + default: + zPosition = 2 + let countString = "\(articleCount)" + countLabel.text = countString + accessibilityLabel = String.localizedStringWithFormat(WMFLocalizedString("places-accessibility-group", value:"%1$@ articles", comment:"Accessibility label for a map icon - %1$@ is replaced with the number of articles in the group {{Identical|Article}}"), countString) + } + updateDotAndImageHiddenState(with: articleCount) + } + + func updateDotAndImageHiddenState(with articleCount: Int) { + switch articleCount { + case 0: + fallthrough + case 1: + imageView.isHidden = !alwaysShowImage + dotView.isHidden = alwaysShowImage + groupView.isHidden = true + default: + imageView.isHidden = true + dotView.isHidden = true + groupView.isHidden = false + } + } + + override var annotation: MKAnnotation? { + didSet { + guard isSetup, let articlePlace = annotation as? ArticlePlace else { + return + } + update(withArticlePlace: articlePlace) + } + } + + override func prepareForReuse() { + super.prepareForReuse() + if alwaysRasterize { + shouldRasterize = false + } + isPlaceholderHidden = false + isImageLoaded = false + delegate = nil + imageImageView.wmf_reset() + selectedImageImageView.wmf_reset() + countLabel.text = nil + set(alwaysShowImage: false, animated: false) + setSelected(false, animated: false) + alpha = 1 + transform = CGAffineTransform.identity + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + guard let place = annotation as? ArticlePlace, place.articles.count == 1 else { + selectedImageView.alpha = 0 + return + } + let dotScale = collapsedDimension/dimension + let imageViewScale = imageDimension/dimension + let scale = alwaysShowImage ? imageViewScale : dotScale + let selectedImageViewScaleDownTransform = CGAffineTransform(scaleX: scale, y: scale) + let dotViewScaleUpTransform = CGAffineTransform(scaleX: 1.0/dotScale, y: 1.0/dotScale) + let imageViewScaleUpTransform = CGAffineTransform(scaleX: 1.0/imageViewScale, y: 1.0/imageViewScale) + layer.zPosition = 3 + if selected { + loadImage() + selectedImageView.transform = selectedImageViewScaleDownTransform + dotView.transform = CGAffineTransform.identity + imageView.transform = CGAffineTransform.identity + selectedImageView.alpha = 0 + imageView.alpha = 1 + dotView.alpha = 1 + } else { + selectedImageView.transform = CGAffineTransform.identity + dotView.transform = dotViewScaleUpTransform + imageView.transform = imageViewScaleUpTransform + + selectedImageView.alpha = 1 + imageView.alpha = 0 + dotView.alpha = 0 + } + let transforms = { + if selected { + self.selectedImageView.transform = CGAffineTransform.identity + self.dotView.transform = dotViewScaleUpTransform + self.imageView.transform = imageViewScaleUpTransform + } else { + self.selectedImageView.transform = selectedImageViewScaleDownTransform + self.dotView.transform = CGAffineTransform.identity + self.imageView.transform = CGAffineTransform.identity + } + } + let fadesIn = { + if selected { + self.selectedImageView.alpha = 1 + } else { + self.imageView.alpha = 1 + self.dotView.alpha = 1 + } + } + let fadesOut = { + if selected { + self.imageView.alpha = 0 + self.dotView.alpha = 0 + } else { + self.selectedImageView.alpha = 0 + } + } + if animated && rasterizeOnAnimations { + shouldRasterize = true + } + let done = { + if animated && self.rasterizeOnAnimations { + self.shouldRasterize = false + } + if !selected { + self.layer.zPosition = self.zPosition + } + } + if animated { + let duration = 2*selectionAnimationDuration + if selected { + UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: springDamping, initialSpringVelocity: 0, options: [.allowUserInteraction], animations: transforms, completion:nil) + UIView.animateKeyframes(withDuration: duration, delay: 0, options: [.allowUserInteraction], animations: { + UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: self.crossFadeRelativeHalfDuration, animations:fadesIn) + UIView.addKeyframe(withRelativeStartTime: self.crossFadeRelativeHalfDuration, relativeDuration: self.crossFadeRelativeHalfDuration, animations:fadesOut) + }) { (didFinish) in + done() + } + } else { + UIView.animateKeyframes(withDuration: 0.5*duration, delay: 0, options: [.allowUserInteraction], animations: { + UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1, animations:transforms) + UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5, animations:fadesIn) + UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5, animations:fadesOut) + }) { (didFinish) in + done() + } + } + } else { + transforms() + fadesIn() + fadesOut() + done() + } + } + + func updateLayout() { + let center = CGPoint(x: 0.5*bounds.size.width, y: 0.5*bounds.size.height) + selectedImageView.center = center + imageView.center = center + dotView.center = center + groupView.center = center + } + + override var frame: CGRect { + didSet { + guard isSetup else { + return + } + updateLayout() + } + } + + override var bounds: CGRect { + didSet { + guard isSetup else { + return + } + updateLayout() + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticlePopoverViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticlePopoverViewController.swift new file mode 100644 index 0000000..065b449 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticlePopoverViewController.swift @@ -0,0 +1,174 @@ +import UIKit +import WMF + +protocol ArticlePopoverViewControllerDelegate: NSObjectProtocol { + func articlePopoverViewController(articlePopoverViewController: ArticlePopoverViewController, didSelectAction: WMFArticleAction) +} + +class ArticlePopoverViewController: UIViewController { + fileprivate static let readActionString = CommonStrings.shortReadTitle + fileprivate static let shareActionString = CommonStrings.shortShareTitle + + weak var delegate: ArticlePopoverViewControllerDelegate? + + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var subtitleLabel: UILabel! + @IBOutlet weak var descriptionLabel: UILabel! + + @IBOutlet weak var buttonStackView: UIStackView! + + @IBOutlet weak var saveButton: SaveButton! + @IBOutlet weak var shareButton: UIButton! + @IBOutlet weak var readButton: UIButton! + + @IBOutlet weak var articleSummaryView: UIView! + @IBOutlet weak var buttonContainerView: UIView! + + var displayTitleHTML: String = "" + let article: WMFArticle + + var showSaveAndShareTitles = true + + required init(_ article: WMFArticle) { + self.article = article + super.init(nibName: "ArticlePopoverViewController", bundle: nil) + } + + required init?(coder: NSCoder) { + return nil + } + + override func viewDidLoad() { + let tapGR = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture)) + articleSummaryView.addGestureRecognizer(tapGR) + + shareButton.setTitle(ArticlePopoverViewController.shareActionString, for: .normal) + shareButton.setImage(#imageLiteral(resourceName: "places-share"), for: .normal) + + readButton.setTitle(ArticlePopoverViewController.readActionString, for: .normal) + readButton.setImage(#imageLiteral(resourceName: "places-more"), for: .normal) + + // Verify that the localized titles for save, share, and read will fit + let sizeToFit = buttonStackView.bounds.size + let widthToCheck = 0.33*sizeToFit.width + let shareButtonSize = shareButton.sizeThatFits(sizeToFit) + let saveButtonSize = saveButton.sizeThatFits(sizeToFit) + let readButtonSize = readButton.sizeThatFits(sizeToFit) + // If any of the titles don't fit, fill proportionally and remove the titles for share and save + showSaveAndShareTitles = shareButtonSize.width < widthToCheck && saveButtonSize.width < widthToCheck && readButtonSize.width < widthToCheck + if !showSaveAndShareTitles { + shareButton.setTitle(nil, for: .normal) + shareButton.imageEdgeInsets = .zero + saveButton.setTitle(nil, for: .normal) + buttonStackView.distribution = .fillProportionally + } + + displayTitleHTML = article.displayTitleHTML + subtitleLabel.text = article.capitalizedWikidataDescriptionOrSnippet + configureView(withTraitCollection: traitCollection) + view.wmf_configureSubviewsForDynamicType() + } + + override func didMove(toParent parent: UIViewController?) { + super.didMove(toParent: parent) + updateMoreButtonImage(with: traitCollection) + } + + override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) { + super.willTransition(to: newCollection, with: coordinator) + updateMoreButtonImage(with: newCollection) + } + + func updateMoreButtonImage(with traitCollection: UITraitCollection) { + var moreImage = #imageLiteral(resourceName: "places-more") + if traitCollection.layoutDirection == .rightToLeft { + moreImage = moreImage.withHorizontallyFlippedOrientation() + } + readButton.setImage(moreImage, for: .normal) + } + + public func update() { + saveButton.showTitle = showSaveAndShareTitles + saveButton.saveButtonState = article.isAnyVariantSaved ? .shortSaved : .shortSave + if !saveButton.showTitle { + saveButton.imageEdgeInsets = .zero + } + + let saveTitle = article.isAnyVariantSaved ? CommonStrings.shortUnsaveTitle : CommonStrings.shortSaveTitle + let saveAction = UIAccessibilityCustomAction(name: saveTitle, target: self, selector: #selector(save)) + let shareAction = UIAccessibilityCustomAction(name: ArticlePopoverViewController.shareActionString, target: self, selector: #selector(share)) + + var accessibilityTitles = [String]() + + if let title = article.displayTitle { + accessibilityTitles.append(title) + } + + if let description = article.wikidataDescription { + accessibilityTitles.append(description) + } + + if let distance = descriptionLabel.text { + accessibilityTitles.append(distance) + } + + let customElement = UIAccessibilityElement(accessibilityContainer: view as Any) + if let screenCoordinateSpace = view.window?.screen.coordinateSpace { + customElement.accessibilityFrame = view.convert(view.bounds, to: screenCoordinateSpace) + } else { + customElement.accessibilityFrame = view.convert(view.bounds, to: nil) + } + customElement.accessibilityLabel = accessibilityTitles.joined(separator: "\n") + customElement.accessibilityCustomActions = [saveAction, shareAction] + customElement.accessibilityTraits = UIAccessibilityTraits.link + view.accessibilityElements = [customElement] + } + + func configureView(withTraitCollection traitCollection: UITraitCollection) { + titleLabel.attributedText = displayTitleHTML.byAttributingHTML(with: .georgiaTitle3, matching: traitCollection) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + configureView(withTraitCollection: traitCollection) + } + + @objc func handleTapGesture(_ tapGR: UITapGestureRecognizer) { + switch tapGR.state { + case .recognized: + delegate?.articlePopoverViewController(articlePopoverViewController: self, didSelectAction: .read) + default: + break + } + } + + @IBAction func save(_ sender: Any) { + delegate?.articlePopoverViewController(articlePopoverViewController: self, didSelectAction: .save) + update() + } + + @IBAction func share(_ sender: Any) { + delegate?.articlePopoverViewController(articlePopoverViewController: self, didSelectAction: .share) + } + + @IBAction func read(_ sender: Any) { + delegate?.articlePopoverViewController(articlePopoverViewController: self, didSelectAction: .read) + } + +} + + +extension ArticlePopoverViewController: Themeable { + func apply(theme: Theme) { + view.tintColor = theme.colors.link + titleLabel.textColor = theme.colors.primaryText + subtitleLabel.textColor = theme.colors.secondaryText + descriptionLabel.textColor = theme.colors.tertiaryText + articleSummaryView.backgroundColor = theme.colors.popoverBackground + buttonContainerView.backgroundColor = theme.colors.border + saveButton.backgroundColor = theme.colors.popoverBackground + shareButton.backgroundColor = theme.colors.popoverBackground + readButton.backgroundColor = theme.colors.popoverBackground + } +} + diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticlePopoverViewController.xib b/Apps/Wikipedia/Wikipedia/Code/ArticlePopoverViewController.xib new file mode 100644 index 0000000..8650206 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticlePopoverViewController.xib @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticlePreviewingDelegate.swift b/Apps/Wikipedia/Wikipedia/Code/ArticlePreviewingDelegate.swift new file mode 100644 index 0000000..533b13e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticlePreviewingDelegate.swift @@ -0,0 +1,8 @@ +import Foundation + +protocol ArticlePreviewingDelegate { + func readMoreArticlePreviewActionSelected(with articleController: ArticleViewController) + func saveArticlePreviewActionSelected(with articleController: ArticleViewController, didSave: Bool, articleURL: URL) + func shareArticlePreviewActionSelected(with articleController: ArticleViewController, shareActivityController: UIActivityViewController) + func viewOnMapArticlePreviewActionSelected(with articleController: ArticleViewController) +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleRightAlignedImageCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleRightAlignedImageCollectionViewCell.swift new file mode 100644 index 0000000..dac0e58 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleRightAlignedImageCollectionViewCell.swift @@ -0,0 +1,105 @@ +import UIKit + +open class ArticleRightAlignedImageCollectionViewCell: ArticleCollectionViewCell { + public var bottomSeparator = UIView() + public var topSeparator = UIView() + public var areSeparatorsAlignedWithLabels: Bool = false + + public var singlePixelDimension: CGFloat = 0.5 + + open override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateSinglePixelDimension() + } + + private func updateSinglePixelDimension() { + singlePixelDimension = traitCollection.displayScale > 0 ? 1.0/traitCollection.displayScale : 0.5 + } + + override open func setup() { + imageView.layer.cornerRadius = 3 + bottomSeparator.isOpaque = true + contentView.addSubview(bottomSeparator) + topSeparator.isOpaque = true + contentView.addSubview(topSeparator) + updateSinglePixelDimension() + super.setup() + } + + open override func reset() { + super.reset() + bottomSeparator.isHidden = true + topSeparator.isHidden = true + titleTextStyle = .callout + updateFonts(with: traitCollection) + } + + override open func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let size = super.sizeThatFits(size, apply: apply) + let isRTL = articleSemanticContentAttribute == .forceRightToLeft + + var widthMinusMargins = layoutWidth(for: size) + if !isImageViewHidden { + widthMinusMargins = widthMinusMargins - self.layoutMargins.right - imageViewDimension + } + + let layoutMargins = calculatedLayoutMargins + let minHeight = imageViewDimension + layoutMargins.top + layoutMargins.bottom + let minHeightMinusMargins = minHeight - layoutMargins.top - layoutMargins.bottom + + var x = layoutMargins.left + if isRTL { + x = size.width - x - widthMinusMargins + } + var origin = CGPoint(x: x, y: layoutMargins.top) + + if descriptionLabel.wmf_hasText || !isImageViewHidden { + let titleLabelFrame = titleLabel.wmf_preferredFrame(at: origin, maximumWidth: widthMinusMargins, alignedBy: articleSemanticContentAttribute, apply: apply) + origin.y += titleLabelFrame.layoutHeight(with: spacing) + + let descriptionLabelFrame = descriptionLabel.wmf_preferredFrame(at: origin, maximumWidth: widthMinusMargins, alignedBy: articleSemanticContentAttribute, apply: apply) + origin.y += descriptionLabelFrame.layoutHeight(with: 0) + descriptionLabel.isHidden = false + } else { + let horizontalAlignment: HorizontalAlignment = isRTL ? .right : .left + let titleLabelFrame = titleLabel.wmf_preferredFrame(at: CGPoint(x: layoutMargins.left, y: layoutMargins.top), maximumSize: CGSize(width: widthMinusMargins, height: UIView.noIntrinsicMetric), minimumSize: CGSize(width: UIView.noIntrinsicMetric, height: minHeightMinusMargins), horizontalAlignment: horizontalAlignment, verticalAlignment: .center, apply: apply) + origin.y += titleLabelFrame.layoutHeight(with: 0) + descriptionLabel.isHidden = true + } + + origin.y += layoutMargins.bottom + let height = ceil(max(origin.y, minHeight)) + + if apply && !bottomSeparator.isHidden { + bottomSeparator.frame = CGRect(x: areSeparatorsAlignedWithLabels ? origin.x : 0, y: bounds.maxY - singlePixelDimension, width: areSeparatorsAlignedWithLabels ? widthMinusMargins : size.width, height: singlePixelDimension) + } + + if apply && !topSeparator.isHidden { + topSeparator.frame = CGRect(x: areSeparatorsAlignedWithLabels ? origin.x : 0, y: 0, width: areSeparatorsAlignedWithLabels ? widthMinusMargins : size.width, height: singlePixelDimension) + } + + if apply && !isImageViewHidden { + let imageViewY = floor(0.5*height - 0.5*imageViewDimension) + var x = layoutMargins.right + if !isRTL { + x = size.width - x - imageViewDimension + } + imageView.frame = CGRect(x: x, y: imageViewY, width: imageViewDimension, height: imageViewDimension) + } + + let totalSize = CGSize(width: size.width, height: height) + + if apply { + layer.shadowPath = UIBezierPath(roundedRect: CGRect(origin: .zero, size: totalSize), cornerRadius: backgroundView?.layer.cornerRadius ?? 0).cgPath + } + + return totalSize + } +} + +open class ArticleRightAlignedImageExploreCollectionViewCell: ArticleRightAlignedImageCollectionViewCell { + override open func apply(theme: Theme) { + super.apply(theme: theme) + setBackgroundColors(theme.colors.cardBackground, selected: theme.colors.selectedCardBackground) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleScrolling.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleScrolling.swift new file mode 100644 index 0000000..b8bcd6b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleScrolling.swift @@ -0,0 +1,115 @@ +import Foundation + +protocol ArticleScrolling: AnyObject { + /// Used to wait for the callback that the anchor is ready for scrollin' + typealias ScrollToAnchorCompletion = (_ anchor: String, _ rect: CGRect) -> Void + + var webView: WKWebView { get } + var messagingController: ArticleWebMessagingController { get } + var scrollToAnchorCompletions: [ScrollToAnchorCompletion] { get set } + var scrollViewAnimationCompletions: [() -> Void] { get set } +} + +// Must set `webView.scrollView.delegate = self` in `viewDidLoad`, as it is not permitted to override functions in extensions. +// There is also some related code in ViewController.scrollViewDidEndScrollingAnimation +// It's a tad hacky, but we need to call something on it and the function can't be overridden here. +extension ArticleScrolling where Self: ViewController { + + /// Scroll to a given offset in the article + /// + /// - Parameters: + /// - anchor: The anchor to scroll to. The anchor corresponds to an `id` attribute on a HTML tag in the article. + /// - centered: If this parameter is true, the element will be centered in the visible area of the article view after scrolling. If this parameter is false, the element will be at the top of the visible area of the article view. + /// - animated: Whether or not to animate the scroll change. + /// - completion: A completion that is called when the scroll change is complete. The Boolean passed into the completion is `true` if the point was successfully found and scrolled to or `false` if the point was invalid. + func scroll(to anchor: String, centered: Bool = false, highlighted: Bool = false, animated: Bool, completion: ((Bool) -> Void)? = nil) { + guard !anchor.isEmpty else { + webView.scrollView.scrollRectToVisible(CGRect(x: 0, y: 1, width: 1, height: 1), animated: animated) + completion?(true) + return + } + + messagingController.prepareForScroll(to: anchor, highlight: highlighted) { (result) in + assert(Thread.isMainThread) + switch result { + case .failure(let error): + self.showError(error) + completion?(false) + case .success: + // The actual scroll happens via a callback event from the WebView + // When that event is received, the scrollToAnchorCompletion is called + let scrollCompletion: ScrollToAnchorCompletion = { (anchor, rect) in + let point = CGPoint(x: self.webView.scrollView.contentOffset.x, y: rect.origin.y + self.webView.scrollView.contentOffset.y) + self.scroll(to: point, centered: centered, animated: animated, completion: completion) + } + self.scrollToAnchorCompletions.insert(scrollCompletion, at: 0) + } + } + } + + /// Scroll to a given offset in the article + /// - Parameters: + /// - offset: The content offset point to scroll to. + /// - centered: If this parameter is true, the content offset point will be centered in the visible area of the article view after scrolling. If this parameter is false, the content offset point will be at the top of the visible area of the article view. + /// - animated: Whether or not to animate the scroll change. + /// - completion: A completion that is called when the scroll change is complete. The Boolean pased into the completion is `true` if the point was successfully found and scrolled to or `false` if the point was invalid. + func scroll(to offset: CGPoint, centered: Bool = false, animated: Bool, completion: ((Bool) -> Void)? = nil) { + assert(Thread.isMainThread) + let scrollView = webView.scrollView + guard !offset.x.isNaN && !offset.x.isInfinite && !offset.y.isNaN && !offset.y.isInfinite else { + completion?(false) + return + } + let overlayTop = self.webView.yOffsetHack + self.navigationBar.hiddenHeight + let adjustmentY: CGFloat + if centered { + let overlayBottom = self.webView.scrollView.contentInset.bottom + let height = self.webView.scrollView.bounds.height + adjustmentY = -0.5 * (height - overlayTop - overlayBottom) + } else { + adjustmentY = overlayTop + } + let minYScrollPoint = 0 - scrollView.contentInset.top + let largestY = scrollView.contentSize.height + scrollView.contentInset.bottom + + /// If there is less than one screen of content, do not let this number be negative, to ensure the ranges below are valid. + let maxYScrollPoint = max(largestY - scrollView.bounds.height, 0) + + /// If y lies within the last screen, scroll should be to the final full screen. + let yContentPoint = offset.y + adjustmentY + let y = (maxYScrollPoint...largestY).contains(yContentPoint) ? maxYScrollPoint : yContentPoint + + guard (minYScrollPoint...maxYScrollPoint).contains(y) else { + completion?(false) + return + } + let boundedOffset = CGPoint(x: scrollView.contentOffset.x, y: y) + guard WMFDistanceBetweenPoints(boundedOffset, scrollView.contentOffset) >= 2 else { + scrollView.flashScrollIndicators() + completion?(true) + return + } + guard animated else { + scrollView.setContentOffset(boundedOffset, animated: false) + completion?(true) + return + } + /* + Setting scrollView.contentOffset inside of an animation block + results in a broken animation https://phabricator.wikimedia.org/T232689 + Calling [scrollView setContentOffset:offset animated:YES] inside + of an animation block fixes the animation but doesn't guarantee + the content offset will be updated when the animation's completion + block is called. + It appears the only reliable way to get a callback after the default + animation is to use scrollViewDidEndScrollingAnimation + */ + scrollViewAnimationCompletions.insert({ completion?(true) }, at: 0) + scrollView.setContentOffset(boundedOffset, animated: true) + } + + func isBoundingClientRectVisible(_ rect: CGRect) -> Bool { + let scrollView = webView.scrollView + return rect.minY > scrollView.contentInset.top && rect.maxY < scrollView.bounds.size.height - scrollView.contentInset.bottom + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleSummaryController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleSummaryController.swift new file mode 100644 index 0000000..3a63c4e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleSummaryController.swift @@ -0,0 +1,54 @@ +import Foundation +import CocoaLumberjackSwift + +@objc(WMFArticleSummaryController) +public class ArticleSummaryController: NSObject { + private let fetcher: ArticleFetcher + weak var dataStore: MWKDataStore? + + @objc required init(session: Session, configuration: Configuration, dataStore: MWKDataStore) { + self.dataStore = dataStore + self.fetcher = ArticleFetcher(session: session, configuration: configuration) + } + + @discardableResult public func updateOrCreateArticleSummaryForArticle(withKey articleKey: WMFInMemoryURLKey, cachePolicy: URLRequest.CachePolicy? = nil, completion: ((WMFArticle?, Error?) -> Void)? = nil) -> URLSessionTask? { + return fetcher.fetchSummaryForArticle(with: articleKey, cachePolicy: cachePolicy) { [weak self] (articleSummary, urlResponse, error) in + DispatchQueue.main.async { + guard let articleSummary = articleSummary, + error == nil else { + completion?(nil, error) + return + } + self?.processSummaryResponses(with: [articleKey: articleSummary]) { (result, error) in + completion?(result[articleKey], error) + } + } + } + } + + @discardableResult public func updateOrCreateArticleSummariesForArticles(withKeys articleKeys: [WMFInMemoryURLKey], cachePolicy: URLRequest.CachePolicy? = nil, completion: (([WMFInMemoryURLKey: WMFArticle], Error?) -> Void)? = nil) -> [URLSessionTask] { + + return fetcher.fetchArticleSummaryResponsesForArticles(withKeys: articleKeys, cachePolicy: cachePolicy) { [weak self] (summaryResponses) in + DispatchQueue.main.async { + self?.processSummaryResponses(with: summaryResponses, completion: completion) + } + } + } + + private func processSummaryResponses(with summaryResponses: [WMFInMemoryURLKey: ArticleSummary], completion: (([WMFInMemoryURLKey: WMFArticle], Error?) -> Void)? = nil) { + guard let moc = dataStore?.viewContext else { + completion?([:], RequestError.invalidParameters) + return + } + + moc.perform { + do { + let articles = try moc.wmf_createOrUpdateArticleSummmaries(withSummaryResponses: summaryResponses) + completion?(articles, nil) + } catch let error { + DDLogError("Error fetching article summary responses: \(error.localizedDescription)") + completion?([:], error) + } + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleSurveyTimerController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleSurveyTimerController.swift new file mode 100644 index 0000000..c52bfe1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleSurveyTimerController.swift @@ -0,0 +1,203 @@ +import Foundation + +protocol ArticleSurveyTimerControllerDelegate: AnyObject { + var isInValidSurveyCampaignAndArticleList: Bool { get } + var shouldAttemptToShowArticleAsLivingDoc: Bool { get } + var shouldShowArticleAsLivingDoc: Bool { get } + var userHasSeenSurveyPrompt: Bool { get } + var displayDelay: TimeInterval? { get } + var livingDocSurveyLinkState: ArticleAsLivingDocSurveyLinkState { get } +} + + // Manages the timer used to display survey announcements on articles +final class ArticleSurveyTimerController { + + private var isInExperimentAndAllowedArticleList: Bool { + + guard let delegate = delegate else { + return false + } + + return delegate.shouldAttemptToShowArticleAsLivingDoc && delegate.isInValidSurveyCampaignAndArticleList + } + + private var isInControlAndAllowedArticleList: Bool { + guard let delegate = delegate else { + return false + } + + return !delegate.shouldAttemptToShowArticleAsLivingDoc && delegate.isInValidSurveyCampaignAndArticleList + } + + // MARK: - Public Properties + + var timerFireBlock: (() -> Void)? + + // MARK: - Private Properties + + private weak var delegate: ArticleSurveyTimerControllerDelegate? + + private var timer: Timer? + private var timeIntervalRemainingWhenPaused: TimeInterval? + private var shouldPauseOnBackground = false + private var shouldResumeOnForeground: Bool { return shouldPauseOnBackground } + private var didScrollPastLivingDocContentInsertAndStartedTimer: Bool = false + + // MARK: - Lifecycle + + init(delegate: ArticleSurveyTimerControllerDelegate) { + self.delegate = delegate + } + + // MARK: - Public + + func articleContentDidLoad() { + + guard let delegate = delegate, + !delegate.userHasSeenSurveyPrompt, + isInControlAndAllowedArticleList else { + return + } + + startTimer() + } + + func livingDocViewWillAppear(withState state: ArticleViewController.ViewState) { + + guard let delegate = delegate, + state == .loaded, + !delegate.userHasSeenSurveyPrompt, + timer == nil, + isInExperimentAndAllowedArticleList else { + return + } + + startTimer() + } + + func livingDocViewWillPush(withState state: ArticleViewController.ViewState) { + viewWillDisappear(withState: state) + } + + func viewWillAppear(withState state: ArticleViewController.ViewState) { + + guard let delegate = delegate, + state == .loaded, + !delegate.userHasSeenSurveyPrompt, + ((isInExperimentAndAllowedArticleList && didScrollPastLivingDocContentInsertAndStartedTimer) || isInControlAndAllowedArticleList) else { + return + } + + startTimer() + } + + func userDidScrollPastLivingDocArticleContentInsert(withState state: ArticleViewController.ViewState) { + + guard let delegate = delegate, + state == .loaded, + !delegate.userHasSeenSurveyPrompt, + isInExperimentAndAllowedArticleList else { + return + } + + didScrollPastLivingDocContentInsertAndStartedTimer = true + + startTimer() + } + + func extendTimer() { + + guard let delegate = delegate, + !delegate.userHasSeenSurveyPrompt, + isInExperimentAndAllowedArticleList else { + return + } + + pauseTimer() + let newTimeInterval = (timeIntervalRemainingWhenPaused ?? 0) + 5 + startTimer(withTimeInterval: newTimeInterval) + + } + + func viewWillDisappear(withState state: ArticleViewController.ViewState) { + // Do not listen for background/foreground notifications to pause and resume survey if this article is not on screen anymore + + guard let delegate = delegate, + state == .loaded, + !delegate.userHasSeenSurveyPrompt, + (isInControlAndAllowedArticleList || isInExperimentAndAllowedArticleList) else { + return + } + + shouldPauseOnBackground = false + stopTimer() + } + + func willResignActive(withState state: ArticleViewController.ViewState) { + if state == .loaded, shouldPauseOnBackground { + + pauseTimer() + } + } + + func didBecomeActive(withState state: ArticleViewController.ViewState) { + if state == .loaded, shouldResumeOnForeground { + + resumeTimer() + } + } + + // MARK: - Timer State Management + + private func startTimer(withTimeInterval customTimeInterval: TimeInterval? = nil) { + guard let displayDelay = delegate?.displayDelay else { + return + } + + shouldPauseOnBackground = true + let timeInterval = customTimeInterval ?? displayDelay + + timer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: false, block: { [weak self] timer in + guard let self = self else { + return + } + + self.timerFireBlock?() + self.stopTimer() + self.shouldPauseOnBackground = false + }) + } + + private func stopTimer() { + timer?.invalidate() + timer = nil + } + + private func pauseTimer() { + guard timer != nil, shouldPauseOnBackground else { + return + } + + timeIntervalRemainingWhenPaused = calculateRemainingTimerTimeInterval() + stopTimer() + } + + private func resumeTimer() { + guard timer == nil, shouldResumeOnForeground else { + return + } + + startTimer(withTimeInterval: timeIntervalRemainingWhenPaused) + } + + /// Calculate remaining TimeInterval (if any) until survey timer fire date + private func calculateRemainingTimerTimeInterval() -> TimeInterval? { + guard let timer = timer else { + return nil + } + + let remainingTime = timer.fireDate.timeIntervalSince(Date()) + return remainingTime < 0 ? nil : remainingTime + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleTableOfContentsDisplayController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleTableOfContentsDisplayController.swift new file mode 100644 index 0000000..bf50b6b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleTableOfContentsDisplayController.swift @@ -0,0 +1,189 @@ +// Handles hide/display of article table of contents +// Manages a stack view and the associated constraints + +protocol ArticleTableOfContentsDisplayControllerDelegate : TableOfContentsViewControllerDelegate { + func tableOfContentsDisplayControllerDidRecreateTableOfContentsViewController() + func getVisibleSection(with: @escaping (Int, String) -> Void) + func stashOffsetPercentage() +} + +class ArticleTableOfContentsDisplayController: Themeable { + + weak var delegate: ArticleTableOfContentsDisplayControllerDelegate? + + lazy var viewController: TableOfContentsViewController = { + return recreateTableOfContentsViewController() + }() + + var theme: Theme = .standard + let articleView: WKWebView + + func apply(theme: Theme) { + self.theme = theme + separatorView.backgroundColor = theme.colors.baseBackground + stackView.backgroundColor = theme.colors.paperBackground + inlineContainerView.backgroundColor = theme.colors.midBackground + viewController.apply(theme: theme) + } + + func recreateTableOfContentsViewController() -> TableOfContentsViewController { + let displaySide: TableOfContentsDisplaySide = stackView.semanticContentAttribute == .forceRightToLeft ? .right : .left + return TableOfContentsViewController(delegate: delegate, theme: theme, displaySide: displaySide) + } + + init (articleView: WKWebView, delegate: ArticleTableOfContentsDisplayControllerDelegate, theme: Theme) { + self.delegate = delegate + self.theme = theme + self.articleView = articleView + stackView.semanticContentAttribute = delegate.tableOfContentsSemanticContentAttribute + stackView.addArrangedSubview(inlineContainerView) + stackView.addArrangedSubview(separatorView) + stackView.addArrangedSubview(articleView) + NSLayoutConstraint.activate([separatorWidthConstraint]) + } + + lazy var stackView: UIStackView = { + let stackView = UIStackView(frame: CGRect(x: 0, y: 0, width: 1, height: 1)) + stackView.axis = .horizontal + stackView.distribution = .fill + stackView.alignment = .fill + return stackView + }() + + lazy var separatorView: UIView = { + let sv = UIView(frame: .zero) + sv.isHidden = true + return sv + }() + + lazy var inlineContainerView: UIView = { + let cv = UIView(frame: .zero) + cv.isHidden = true + return cv + }() + + lazy var separatorWidthConstraint: NSLayoutConstraint = { + return separatorView.widthAnchor.constraint(equalToConstant: 1) + }() + + func show(animated: Bool) { + switch viewController.displayMode { + case .inline: + showInline() + case .modal: + showModal(animated: animated) + } + } + + func hide(animated: Bool) { + switch viewController.displayMode { + case .inline: + hideInline() + case .modal: + hideModal(animated: animated) + } + } + + func showModal(animated: Bool) { + guard delegate?.presentedViewController == nil else { + return + } + delegate?.getVisibleSection(with: { (sectionId, _) in + self.viewController.isVisible = true + self.selectAndScroll(to: sectionId, animated: false) + + // Attempts to fix TOC presentation crashes. Error message target is in comments. + guard + !self.viewController.isBeingPresented && // Application tried to present modally a view controller %@ that is already being presented by %@. + self.delegate?.presentedViewController == nil && // Attempt to present %@ on %@ (from %@) which is already presenting %@. + self.delegate !== self.viewController && // Application tried to present modal view controller on itself. + self.viewController.parent == nil && // Application tried to present modally a view controller %@ that has a parent view controller %@. + (self.delegate?.isViewLoaded ?? false) // Attempt to present %@ on %@ (from %@) whose view is not in the window hierarchy. + else { + return + } + + self.delegate?.present(self.viewController, animated: animated) + }) + } + + func hideModal(animated: Bool) { + viewController.isVisible = false + delegate?.dismiss(animated: animated) + } + + func showInline() { + delegate?.stashOffsetPercentage() + viewController.isVisible = true + UserDefaults.standard.wmf_setTableOfContentsIsVisibleInline(true) + inlineContainerView.isHidden = false + separatorView.isHidden = false + } + + func hideInline() { + delegate?.stashOffsetPercentage() + viewController.isVisible = false + UserDefaults.standard.wmf_setTableOfContentsIsVisibleInline(false) + inlineContainerView.isHidden = true + separatorView.isHidden = true + } + + func setup(with traitCollection:UITraitCollection) { + update(with: traitCollection) + guard viewController.displayMode == .inline, UserDefaults.standard.wmf_isTableOfContentsVisibleInline() else { + return + } + showInline() + } + + func update(with traitCollection: UITraitCollection) { + let isCompact = traitCollection.horizontalSizeClass == .compact + viewController.displayMode = isCompact ? .modal : .inline + setupTableOfContentsViewController() + } + + func setupTableOfContentsViewController() { + switch viewController.displayMode { + case .inline: + guard viewController.parent != delegate else { + return + } + let wasVisible = viewController.isVisible + if wasVisible { + hideModal(animated: false) + } + viewController = recreateTableOfContentsViewController() + viewController.displayMode = .inline + delegate?.addChild(viewController) + inlineContainerView.wmf_addSubviewWithConstraintsToEdges(viewController.view) + viewController.didMove(toParent: delegate) + if wasVisible { + showInline() + } + delegate?.tableOfContentsDisplayControllerDidRecreateTableOfContentsViewController() + case .modal: + guard viewController.parent == delegate else { + return + } + let wasVisible = viewController.isVisible + viewController.displayMode = .modal + viewController.willMove(toParent: nil) + viewController.view.removeFromSuperview() + viewController.removeFromParent() + viewController = recreateTableOfContentsViewController() + if wasVisible { + hideInline() + showModal(animated: false) + } + delegate?.tableOfContentsDisplayControllerDidRecreateTableOfContentsViewController() + } + } + + func selectAndScroll(to sectionId: Int, animated: Bool) { + guard let index = delegate?.tableOfContentsItems.firstIndex(where: { $0.id == sectionId }) else { + return + } + viewController.selectItem(at: index) + viewController.scrollToItem(at: index) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleToolbarController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleToolbarController.swift new file mode 100644 index 0000000..2860fde --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleToolbarController.swift @@ -0,0 +1,170 @@ +import UIKit + +protocol ArticleToolbarHandling: AnyObject { + func toggleSave(from controller: ArticleToolbarController) + func saveButtonWasLongPressed(from controller: ArticleToolbarController) + func showThemePopover(from controller: ArticleToolbarController) + func showTableOfContents(from controller: ArticleToolbarController) + func hideTableOfContents(from controller: ArticleToolbarController) + func showLanguagePicker(from controller: ArticleToolbarController) + func showFindInPage(from controller: ArticleToolbarController) + func share(from controller: ArticleToolbarController) + var isTableOfContentsVisible: Bool { get } +} + +class ArticleToolbarController: Themeable { + weak var delegate: ArticleToolbarHandling? + + let toolbar: UIToolbar + + lazy var saveButton: IconBarButtonItem = { + let item = IconBarButtonItem(iconName: "save", target: self, action: #selector(toggleSave), for: .touchUpInside) + let longPressGR = UILongPressGestureRecognizer(target: self, action: #selector(handleSaveButtonLongPress)) + if let button = item.customView as? UIButton { + button.addGestureRecognizer(longPressGR) + } + item.apply(theme: theme) + return item + }() + + lazy var themeButton: IconBarButtonItem = { + let item = IconBarButtonItem(iconName: "font-size", target: self, action: #selector(showThemes), for: .touchUpInside) + item.apply(theme: theme) + return item + }() + + lazy var showTableOfContentsButton: IconBarButtonItem = { + let item = IconBarButtonItem(iconName: "toc", target: self, action: #selector(showTableOfContents), for: .touchUpInside) + item.accessibilityLabel = WMFLocalizedString("table-of-contents-button-label", value: "Table of contents", comment: "Accessibility label for the Table of Contents button {{Identical|Table of contents}}") + item.apply(theme: theme) + return item + }() + + lazy var shareButton: IconBarButtonItem = { + let item = IconBarButtonItem(iconName: "share", target: self, action: #selector(share), for: .touchUpInside) + item.accessibilityLabel = CommonStrings.accessibilityShareTitle + item.apply(theme: theme) + return item + }() + + lazy var findInPageButton: IconBarButtonItem = { + let item = IconBarButtonItem(iconName: "find-in-page", target: self, action: #selector(findInPage), for: .touchUpInside) + item.accessibilityLabel = CommonStrings.findInPage + item.apply(theme: theme) + return item + }() + + lazy var hideTableOfContentsButton: IconBarButtonItem = { + let item = IconBarButtonItem(iconName: "toc", target: self, action: #selector(hideTableOfContents), for: .touchUpInside) + if let button = item.customView as? UIButton { + button.layer.cornerRadius = 5 + button.layer.masksToBounds = true + } + item.accessibilityLabel = WMFLocalizedString("table-of-contents-hide-button-label", value: "Hide table of contents", comment: "Accessibility label for the hide Table of Contents button") + item.apply(theme: theme) + return item + }() + + lazy var languagesButton: IconBarButtonItem = { + let item = IconBarButtonItem(iconName: "language", target: self, action: #selector(showLanguagePicker), for: .touchUpInside) + item.accessibilityLabel = CommonStrings.accessibilityLanguagesTitle + item.apply(theme: theme) + return item + }() + + init(toolbar: UIToolbar, delegate: ArticleToolbarHandling) { + self.toolbar = toolbar + self.delegate = delegate + update() + } + + // MARK: Actions + + @objc func toggleSave(_ sender: UIBarButtonItem) { + delegate?.toggleSave(from: self) + } + + @objc func handleSaveButtonLongPress(_ longPressGestureRecognizer: UILongPressGestureRecognizer) { + guard longPressGestureRecognizer.state == .began else { + return + } + delegate?.saveButtonWasLongPressed(from: self) + } + + @objc func showThemes() { + delegate?.showThemePopover(from: self) + } + + @objc func showTableOfContents() { + delegate?.showTableOfContents(from: self) + } + + @objc func hideTableOfContents() { + delegate?.hideTableOfContents(from: self) + } + + @objc func showLanguagePicker() { + delegate?.showLanguagePicker(from: self) + } + + @objc func share() { + delegate?.share(from: self) + } + + @objc func findInPage() { + delegate?.showFindInPage(from: self) + } + + // MARK: State + + func setSavedState(isSaved: Bool) { + saveButton.accessibilityLabel = isSaved ? CommonStrings.accessibilitySavedTitle : CommonStrings.saveTitle + if let innerButton = saveButton.customView as? UIButton { + let assetName = isSaved ? "save-filled" : "save" + innerButton.setImage(UIImage(named: assetName), for: .normal) + } + } + + // MARK: Theme + + var theme: Theme = Theme.standard + + func apply(theme: Theme) { + for item in toolbar.items ?? [] { + guard let item = item as? Themeable else { + continue + } + item.apply(theme: theme) + } + hideTableOfContentsButton.customView?.backgroundColor = theme.colors.midBackground + } + + func override(items: [UIBarButtonItem]) { + // disable updating here if that's ever a thing + toolbar.items = items + } + + func update() { + let tocItem = delegate?.isTableOfContentsVisible ?? false ? hideTableOfContentsButton : showTableOfContentsButton + toolbar.items = [ + UIBarButtonItem.flexibleSpaceToolbar(), + tocItem, + UIBarButtonItem.flexibleSpaceToolbar(), + languagesButton, + UIBarButtonItem.flexibleSpaceToolbar(), + saveButton, + UIBarButtonItem.flexibleSpaceToolbar(), + shareButton, + UIBarButtonItem.flexibleSpaceToolbar(), + themeButton, + UIBarButtonItem.flexibleSpaceToolbar(), + findInPageButton, + UIBarButtonItem.flexibleSpaceToolbar() + ] + } + + func setToolbarButtons(enabled: Bool) { + toolbar.items?.forEach { $0.isEnabled = enabled } + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleURLListViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleURLListViewController.swift new file mode 100644 index 0000000..4b2b1bc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleURLListViewController.swift @@ -0,0 +1,131 @@ +import UIKit + +class ArticleURLListViewController: ArticleCollectionViewController, DetailPresentingFromContentGroup { + let articleURLs: [URL] + private let articleKeys: Set + let contentGroupIDURIString: String? + + required init(articleURLs: [URL], dataStore: MWKDataStore, contentGroup: WMFContentGroup? = nil, theme: Theme) { + self.articleURLs = articleURLs + self.articleKeys = Set(articleURLs.compactMap { $0.wmf_databaseKey }) + self.contentGroupIDURIString = contentGroup?.objectID.uriRepresentation().absoluteString + super.init() + feedFunnelContext = FeedFunnelContext(contentGroup) + self.theme = theme + self.dataStore = dataStore + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + @objc func articleDidChange(_ note: Notification) { + guard + let article = note.object as? WMFArticle, + article.hasChangedValuesForCurrentEventThatAffectPreviews, + let articleKey = article.key, + articleKeys.contains(articleKey) + else { + return + } + collectionView.reloadData() + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) not supported") + } + + override func articleURL(at indexPath: IndexPath) -> URL? { + guard indexPath.item < articleURLs.count else { + return nil + } + return articleURLs[indexPath.item] + } + + override func article(at indexPath: IndexPath) -> WMFArticle? { + guard let articleURL = articleURL(at: indexPath) else { + return nil + } + return dataStore.fetchOrCreateArticle(with: articleURL) + } + + override func viewDidLoad() { + super.viewDidLoad() + collectionView.reloadData() + NotificationCenter.default.addObserver(self, selector: #selector(articleDidChange(_:)), name: NSNotification.Name.WMFArticleUpdated, object: nil) + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + if isMovingFromParent { + FeedFunnel.shared.logFeedCardClosed(for: feedFunnelContext, maxViewed: maxViewed) + } + } + + override var eventLoggingCategory: EventLoggingCategory { + return .feed + } + + override var eventLoggingLabel: EventLoggingLabel? { + return feedFunnelContext?.label + } + + override func collectionViewFooterButtonWasPressed(_ collectionViewFooter: CollectionViewFooter) { + navigationController?.popViewController(animated: true) + } + + + override func shareArticlePreviewActionSelected(with articleController: ArticleViewController, shareActivityController: UIActivityViewController) { + FeedFunnel.shared.logFeedDetailShareTapped(for: feedFunnelContext, index: previewedIndexPath?.item) + super.shareArticlePreviewActionSelected(with: articleController, shareActivityController: shareActivityController) + } + + override func readMoreArticlePreviewActionSelected(with articleController: ArticleViewController) { + articleController.wmf_removePeekableChildViewControllers() + push(articleController, context: feedFunnelContext, index: previewedIndexPath?.item, animated: true) + } + + // MARK: - CollectionViewContextMenuShowing + override func previewingViewController(for indexPath: IndexPath, at location: CGPoint) -> UIViewController? { + let vc = super.previewingViewController(for: indexPath, at: location) + FeedFunnel.shared.logArticleInFeedDetailPreviewed(for: feedFunnelContext, index: previewedIndexPath?.item) + return vc + } + + override var poppingIntoVCCompletion: () -> Void { + return { [weak self] in + guard let self = self else { + return + } + FeedFunnel.shared.logArticleInFeedDetailReadingStarted(for: self.feedFunnelContext, index: self.previewedIndexPath?.item, maxViewed: self.maxViewed) + } + } +} + +// MARK: - UICollectionViewDataSource +extension ArticleURLListViewController { + override func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } + + override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return articleURLs.count + } +} + +// MARK: - UICollectionViewDelegate +extension ArticleURLListViewController { + override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + super.collectionView(collectionView, didSelectItemAt: indexPath) + FeedFunnel.shared.logArticleInFeedDetailReadingStarted(for: feedFunnelContext, index: indexPath.item, maxViewed: maxViewed) + } +} + +extension ArticleURLListViewController { + override func didPerformAction(_ action: Action) -> Bool { + if action.type == .share { + FeedFunnel.shared.logFeedDetailShareTapped(for: feedFunnelContext, index: action.indexPath.item) + } + return super.didPerformAction(action) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Analytics.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Analytics.swift new file mode 100644 index 0000000..ce808d8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Analytics.swift @@ -0,0 +1,9 @@ +extension ArticleViewController: EventLoggingEventValuesProviding { + var eventLoggingLabel: EventLoggingLabel? { + return .outLink + } + + var eventLoggingCategory: EventLoggingCategory { + return .article + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Announcements.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Announcements.swift new file mode 100644 index 0000000..d9e34d6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Announcements.swift @@ -0,0 +1,73 @@ +import WMF +import CocoaLumberjackSwift + +extension ArticleViewController { + + func showAnnouncementIfNeeded() { + guard (isInValidSurveyCampaignAndArticleList && userHasSeenSurveyPrompt) || !isInValidSurveyCampaignAndArticleList else { + return + } + let predicate = NSPredicate(format: "placement == 'article' && isVisible == YES") + let contentGroups = dataStore.viewContext.orderedGroups(of: .announcement, with: predicate) + let currentDate = Date() + + // get the first content group with a valid date + let contentGroup = contentGroups?.first(where: { (group) -> Bool in + guard group.contentType == .announcement, + let announcement = group.contentPreview as? WMFAnnouncement, + let startDate = announcement.startTime, + let endDate = announcement.endTime + else { + return false + } + + return (startDate...endDate).contains(currentDate) + }) + + guard + !isBeingPresentedAsPeek, + let contentGroupURL = contentGroup?.url, + let announcement = contentGroup?.contentPreview as? WMFAnnouncement, + let actionURL = announcement.actionURL + else { + return + } + + let dismiss = { + // re-fetch since time has elapsed + let contentGroup = self.dataStore.viewContext.contentGroup(for: contentGroupURL) + contentGroup?.markDismissed() + contentGroup?.updateVisibilityForUserIsLogged(in: self.session.isAuthenticated) + do { + try self.dataStore.viewContext.save() + } catch let saveError { + DDLogError("Error saving after marking article announcement as dismissed: \(saveError)") + } + } + + guard !articleURL.isThankYouDonationURL else { + dismiss() + return + } + + let context = FeedFunnelContext(contentGroup) + FeedFunnel.shared.logFeedImpression(for: context) + wmf_showAnnouncementPanel(announcement: announcement, primaryButtonTapHandler: { (sender) in + self.navigate(to: actionURL, useSafari: true) + // dismiss handler is called + }, secondaryButtonTapHandler: { (sender) in + // dismiss handler is called + }, footerLinkAction: { (url) in + self.navigate(to: url, useSafari: true) + // intentionally don't dismiss + }, traceableDismissHandler: { _ in + dismiss() + }, theme: theme) + } +} + +private extension URL { + var isThankYouDonationURL: Bool { + return self.host == "thankyou.wikipedia.org" + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ArticleInformation.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ArticleInformation.swift new file mode 100644 index 0000000..e795a96 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ArticleInformation.swift @@ -0,0 +1,73 @@ +extension ArticleViewController { + func showLanguages() { + let languagesVC = WMFArticleLanguagesViewController(articleURL: articleURL) + themesPresenter.dismissReadingThemesPopoverIfActive(from: self) + languagesVC.delegate = self + presentEmbedded(languagesVC, style: .sheet) + } + + func showDisambiguation(with payload: Any?) { + guard let payload = payload as? [[String: Any]] else { + showGenericError() + return + } + var pageURLs: [URL] = [] + for info in payload { + guard let links = info["links"] as? [String] else { + continue + } + let linkURLs = links.compactMap { URL(string: $0) } + pageURLs.append(contentsOf: linkURLs) + } + let listVC = DisambiguationPagesViewController(with: pageURLs, siteURL: articleURL, dataStore: dataStore, theme: theme) + push(listVC) + } + + func showEditHistory() { + + guard let title = articleURL.wmf_title else { + showGenericError() + return + } + + EditHistoryCompareFunnel.shared.logShowHistory(articleURL: articleURL) + + let historyVC = PageHistoryViewController(pageTitle: title, pageURL: articleURL) + historyVC.apply(theme: theme) + push(historyVC) + } + + func showTalkPage() { + guard let talkPageURL = articleURL.articleTalkPage else { + showGenericError() + return + } + + let userInfo: [AnyHashable : Any] = [RoutingUserInfoKeys.source: RoutingUserInfoSourceValue.article.rawValue] + navigate(to: talkPageURL, userInfo: userInfo) + } + + func showCoordinate() { + let placesURL = NSUserActivity.wmf_URLForActivity(of: .places, withArticleURL: articleURL) + UIApplication.shared.open(placesURL) + } + + func showPageIssues(with payload: Any?) { + guard let payload = payload as? [[String: Any]] else { + showGenericError() + return + } + let issues = payload.compactMap { ($0["html"] as? String)?.removingHTML } + let issuesVC = PageIssuesTableViewController(style: .grouped) + issuesVC.issues = issues + presentEmbedded(issuesVC, style: .sheet) + } +} + +extension ArticleViewController: WMFLanguagesViewControllerDelegate { + func languagesController(_ controller: WMFLanguagesViewController, didSelectLanguage language: MWKLanguageLink) { + dismiss(animated: true) { + self.navigate(to: language.articleURL) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ArticleToolbarHandling.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ArticleToolbarHandling.swift new file mode 100644 index 0000000..5410a61 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ArticleToolbarHandling.swift @@ -0,0 +1,47 @@ +extension ArticleViewController: ArticleToolbarHandling { + func showTableOfContents(from controller: ArticleToolbarController) { + showTableOfContents() + } + + func hideTableOfContents(from controller: ArticleToolbarController) { + hideTableOfContents() + } + + var isTableOfContentsVisible: Bool { + return tableOfContentsController.viewController.displayMode == .inline && tableOfContentsController.viewController.isVisible + } + + func toggleSave(from controller: ArticleToolbarController) { + let isSaved = dataStore.savedPageList.toggleSavedPage(for: articleURL) + if isSaved { + savedPagesFunnel.logSaveNew(withArticleURL: articleURL) + readingListsFunnel.logArticleSaveInCurrentArticle(articleURL) + } else { + savedPagesFunnel.logDelete(withArticleURL: articleURL) + readingListsFunnel.logArticleUnsaveInCurrentArticle(articleURL) + } + } + + func showThemePopover(from controller: ArticleToolbarController) { + themesPresenter.showReadingThemesControlsPopup(on: self, responder: self, theme: theme) + } + + func saveButtonWasLongPressed(from controller: ArticleToolbarController) { + let addArticlesToReadingListVC = AddArticlesToReadingListViewController(with: dataStore, articles: [article], theme: theme) + let nc = WMFThemeableNavigationController(rootViewController: addArticlesToReadingListVC, theme: theme) + nc.setNavigationBarHidden(false, animated: false) + present(nc, animated: true) + } + + func showLanguagePicker(from controller: ArticleToolbarController) { + showLanguages() + } + + func share(from controller: ArticleToolbarController) { + shareArticle() + } + + func showFindInPage(from controller: ArticleToolbarController) { + showFindInPage() + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ArticleWebMessageHandling.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ArticleWebMessageHandling.swift new file mode 100644 index 0000000..f6cb3b6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ArticleWebMessageHandling.swift @@ -0,0 +1,118 @@ +import WMF + +extension ArticleViewController: ArticleWebMessageHandling { + + func didRecieve(action: ArticleWebMessagingController.Action) { + dismissReferencesPopover() + switch action { + case .setup: + handlePCSDidFinishInitialSetup() + case .finalSetup: + handlePCSDidFinishFinalSetup() + case .unknown(let href): + fallthrough + case .link(let href, _, _): + handleLink(with: href) + case .leadImage(let source, let width, let height): + handleLeadImage(source: source, width: width, height: height) + case .tableOfContents(items: let items): + handleTableOfContents(items: items) + case .footerItem(let type, let payload): + handleFooterItem(type: type, payload: payload) + case .edit(let sectionID, let descriptionSource): + showEditorForSectionOrTitleDescription(with: sectionID, descriptionSource: descriptionSource, funnelSource: .pencil) + case .backLink(let referenceId, let referenceText, let backLinks): + showReferenceBackLinks(backLinks, referenceId: referenceId, referenceText: referenceText) + case .reference(let index, let group): + showReferences(group, selectedIndex: index, animated: true) + case .image(let src, let href, let width, let height): + showImage(src: src, href: href, width: width, height: height) + case .addTitleDescription: + showTitleDescriptionEditor(with: .none, funnelSource: .titleDescription) + case .scrollToAnchor(let anchor, let rect): + scrollToAnchorCompletions.popLast()?(anchor, rect) + scrollToAnchorCompletions.removeAll() + case .viewInBrowser: + navigate(to: self.articleURL, useSafari: true) + case .aaaldInsertOnScreen: + handleAaaLDInsertOnScreenEvent() + } + } + + func handleTableOfContents(items: [TableOfContentsItem]) { + let titleItem = TableOfContentsItem(id: 0, titleHTML: article.displayTitleHTML, anchor: "", rootItemId: 0, indentationLevel: 0) + var allItems: [TableOfContentsItem] = [titleItem] + allItems.append(contentsOf: items) + + // While `items` includes strings localized to the language of the article, our additional appended strings here are not. We need to specify we want localized strings for a local `.lproj` we actually have (which unfortunately doesn't always match the article itself). Here, we send the full language variant code to hint to pull the most accurate localized string we may have available based on the language variant of the article itself. + let languageCode = articleURL.wmf_contentLanguageCode ?? articleLanguageCode + let aboutThisArticleTitle = CommonStrings.aboutThisArticleTitle(with: languageCode) + let readMoreTitle = CommonStrings.readMoreTitle(with: languageCode) + let aboutThisArticleItem = TableOfContentsItem(id: -2, titleHTML: aboutThisArticleTitle, anchor: PageContentService.Footer.Menu.fragment, rootItemId: -2, indentationLevel: 0) + allItems.append(aboutThisArticleItem) + let readMoreItem = TableOfContentsItem(id: -3, titleHTML: readMoreTitle, anchor: PageContentService.Footer.ReadMore.fragment, rootItemId: -3, indentationLevel: 0) + allItems.append(readMoreItem) + tableOfContentsItems = allItems + } + + func handlePCSDidFinishInitialSetup() { + let oldState = state + state = .loaded + showWIconPopoverIfNecessary() + refreshControl.endRefreshing() + surveyTimerController?.articleContentDidLoad() + loadSummary(oldState: oldState) + initialSetupCompletion?() + initialSetupCompletion = nil + } + + @objc func handlePCSDidFinishFinalSetup() { + assignScrollStateFromArticleFlagsIfNecessary() + articleLoadWaitGroup?.leave() + addToHistory() + syncCachedResourcesIfNeeded() + } + + func handleFooterItem(type: PageContentService.Footer.Menu.Item, payload: Any?) { + switch type { + case .talkPage: + showTalkPage() + case .coordinate: + showCoordinate() + case .disambiguation: + showDisambiguation(with: payload) + case .lastEdited: + showEditHistory() + case .pageIssues: + showPageIssues(with: payload) + } + } + + func handleLeadImage(source: String?, width: Int?, height: Int?) { + assert(Thread.isMainThread) + guard let source = source else { + leadImageHeightConstraint.constant = 0 + return + } + guard let leadImageURLToRequest = WMFArticle.imageURL(forTargetImageWidth: traitCollection.wmf_leadImageWidth, fromImageSource: source, withOriginalWidth: width ?? 0) else { + return + } + loadLeadImage(with: leadImageURLToRequest) + } + + func setupFooter() { + // Always use Configuration.production for related articles + guard let baseURL = Configuration.production.pageContentServiceAPIURLForURL(articleURL, appending: []) else { + return + } + var menuItems: [PageContentService.Footer.Menu.Item] = [.talkPage, .lastEdited, .pageIssues, .disambiguation] + if article.coordinate != nil { + menuItems.append(.coordinate) + } + messagingController.addFooter(articleURL: articleURL, restAPIBaseURL: baseURL, menuItems: menuItems, lastModified: article.lastModifiedDate) + } + + func handleAaaLDInsertOnScreenEvent() { + surveyTimerController?.userDidScrollPastLivingDocArticleContentInsert(withState: state) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ContextMenu.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ContextMenu.swift new file mode 100644 index 0000000..1252ea8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+ContextMenu.swift @@ -0,0 +1,31 @@ +extension ArticleViewController { + func updateMenuItems() { + let shareMenuItemTitle = CommonStrings.shareMenuTitle + let shareMenuItem = UIMenuItem(title: shareMenuItemTitle, action: #selector(shareMenuItemTapped)) + let editMenuItemTitle = WMFLocalizedString("edit-menu-item", value: "Edit", comment: "Button label for text selection 'Edit' menu item") + let editMenuItem = UIMenuItem(title: editMenuItemTitle, action: #selector(editMenuItemTapped)) + + UIMenuController.shared.menuItems = [editMenuItem, shareMenuItem] + } + + @objc func shareMenuItemTapped() { + self.shareArticle() + } + + @objc func editMenuItemTapped() { + webView.wmf_getSelectedTextEditInfo { (editInfo, error) in + guard let editInfo = editInfo else { + self.showError(error ?? RequestError.unexpectedResponse) + return + } + + if editInfo.isSelectedTextInTitleDescription, let descriptionSource = editInfo.descriptionSource, descriptionSource == .central { + // Only show the description editor if the description is from Wikidata (descriptionSource == .central) + self.showTitleDescriptionEditor(with: .unknown, funnelSource: .highlight) + } else { + // Otherwise it needs to be changed in the section editor by editing the {{Short description}} template + self.showEditorForSection(with: editInfo.sectionID, selectedTextEditInfo: editInfo, funnelSource: .highlight) + } + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Editing.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Editing.swift new file mode 100644 index 0000000..d586994 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Editing.swift @@ -0,0 +1,230 @@ +import CocoaLumberjackSwift + +extension ArticleViewController { + func showEditorForSectionOrTitleDescription(with id: Int, descriptionSource: ArticleDescriptionSource?, selectedTextEditInfo: SelectedTextEditInfo? = nil, funnelSource: EditFunnelSource) { + /// If this is a first section with an existing description, show the dialog box. (This is reported as a `central` or `local` description source.) Otherwise, just show the editor for the section. (A first section without an article description has an `Add article description` button, and thus doesn't need the dialog box.) + if let descriptionSource = descriptionSource, (descriptionSource == .central || descriptionSource == .local) { + showEditSectionOrTitleDescriptionDialogForSection(with: id, descriptionSource: descriptionSource, selectedTextEditInfo: selectedTextEditInfo, funnelSource: funnelSource) + } else { + showEditorForSection(with: id, selectedTextEditInfo: selectedTextEditInfo, funnelSource: funnelSource) + } + } + + func showEditorForSection(with id: Int, selectedTextEditInfo: SelectedTextEditInfo? = nil, funnelSource: EditFunnelSource) { + editFunnel.logSectionEditingStart(from: funnelSource, language: articleLanguageCode) + cancelWIconPopoverDisplay() + let sectionEditVC = SectionEditorViewController(articleURL: articleURL, sectionID: id, dataStore: dataStore, selectedTextEditInfo: selectedTextEditInfo, theme: theme) + sectionEditVC.delegate = self + sectionEditVC.editFunnel = editFunnel + let navigationController = WMFThemeableNavigationController(rootViewController: sectionEditVC, theme: theme) + navigationController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext + + let needsIntro = !UserDefaults.standard.didShowEditingOnboarding + if needsIntro { + editFunnel.logOnboardingPresentation(initiatedBy: funnelSource, language: articleLanguageCode) + let editingWelcomeViewController = EditingWelcomeViewController(theme: theme) { + self.present(navigationController, animated: true) + } + editingWelcomeViewController.apply(theme: theme) + present(editingWelcomeViewController, animated: true) { + UserDefaults.standard.didShowEditingOnboarding = true + } + + } else { + present(navigationController, animated: true) + } + } + + func showTitleDescriptionEditor(with descriptionSource: ArticleDescriptionSource, funnelSource: EditFunnelSource) { + + editFunnel.logTitleDescriptionEditingStart(from: funnelSource, language: articleLanguageCode) + + let maybeDescriptionController: ArticleDescriptionControlling? = (articleURL.wmf_isEnglishWikipedia || articleURL.wmf_isTestWikipedia) ? ShortDescriptionController(article: article, articleLanguageCode: articleLanguageCode, articleURL: articleURL, descriptionSource: descriptionSource, delegate: self) : WikidataDescriptionController(article: article, articleLanguageCode: articleLanguageCode, descriptionSource: descriptionSource) + + guard let descriptionController = maybeDescriptionController else { + showGenericError() + return + } + + let editVC = DescriptionEditViewController.with(dataStore: dataStore, theme: theme, articleDescriptionController: descriptionController) + editVC.delegate = self + editVC.editFunnel = editFunnel + editVC.editFunnelSource = funnelSource + let navigationController = WMFThemeableNavigationController(rootViewController: editVC, theme: theme) + navigationController.modalPresentationStyle = .overCurrentContext + navigationController.view.isOpaque = false + navigationController.view.backgroundColor = .clear + let needsIntro = !UserDefaults.standard.wmf_didShowTitleDescriptionEditingIntro() + if needsIntro { + navigationController.view.alpha = 0 + } + let showIntro: (() -> Void)? = { + self.editFunnel.logOnboardingPresentation(initiatedBy: funnelSource, language: self.articleLanguageCode) + let welcomeVC = DescriptionWelcomeInitialViewController.wmf_viewControllerFromDescriptionWelcomeStoryboard() + welcomeVC.completionBlock = { + self.editFunnel.logTitleDescriptionReadyToEditFrom(from: funnelSource, isAddingNewTitleDescription: descriptionSource == .none, language: self.articleLanguageCode) + } + welcomeVC.apply(theme: self.theme) + navigationController.present(welcomeVC, animated: true) { + UserDefaults.standard.wmf_setDidShowTitleDescriptionEditingIntro(true) + navigationController.view.alpha = 1 + } + } + present(navigationController, animated: !needsIntro) { + if needsIntro { + showIntro?() + } else { + self.editFunnel.logTitleDescriptionReadyToEditFrom(from: funnelSource, isAddingNewTitleDescription: descriptionSource == .none, language: self.articleLanguageCode) + } + } + } + + func showEditSectionOrTitleDescriptionDialogForSection(with id: Int, descriptionSource: ArticleDescriptionSource, selectedTextEditInfo: SelectedTextEditInfo? = nil, funnelSource: EditFunnelSource) { + let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .alert) + + let editTitleDescriptionTitle = WMFLocalizedString("description-edit-pencil-title", value: "Edit article description", comment: "Title for button used to show article description editor") + let editTitleDescriptionAction = UIAlertAction(title: editTitleDescriptionTitle, style: .default) { (action) in + self.showTitleDescriptionEditor(with: descriptionSource, funnelSource: funnelSource) + } + sheet.addAction(editTitleDescriptionAction) + + let editLeadSectionTitle = WMFLocalizedString("description-edit-pencil-introduction", value: "Edit introduction", comment: "Title for button used to show article lead section editor") + let editLeadSectionAction = UIAlertAction(title: editLeadSectionTitle, style: .default) { (action) in + self.showEditorForSection(with: id, selectedTextEditInfo: selectedTextEditInfo, funnelSource: funnelSource) + } + sheet.addAction(editLeadSectionAction) + + sheet.addAction(UIAlertAction(title: CommonStrings.cancelActionTitle, style: .cancel)) + + present(sheet, animated: true) + } + +} + +extension ArticleViewController: ShortDescriptionControllerDelegate { + + /// Pulls title description from article content. + /// Looks for the innerText of the "pcs-edit-section-title-description" ID element + /// - Parameter completion: Completion when bridge call completes. Passes back title description or nil if pcs-edit-section-title-description could not be extracted. + func currentDescription(completion: @escaping (String?) -> Void) { + + let javascript = """ + function extractTitleDescription() { + var editTitleDescriptionElement = document.getElementById('pcs-edit-section-title-description'); + if (editTitleDescriptionElement) { + return editTitleDescriptionElement.innerText; + } + return null; + } + extractTitleDescription(); + """ + + webView.evaluateJavaScript(javascript) { (result, error) in + DispatchQueue.main.async { + if let error = error { + DDLogDebug("Failure in articleHtmlTitleDescription: \(error)") + completion(nil) + return + } + + guard let stringResult = result as? String else { + completion(nil) + return + } + + completion(stringResult) + } + } + } + + enum ArticleEditingDescriptionError: Error { + case failureInjectingNewDescription + } + + func injectNewDescriptionIntoArticleContent(_ newDescription: String, completion: @escaping (Result) -> Void) { + let javascript = """ + function injectTitleDescription(description) { + + //first attempt to swap out add description callout + var addTitleDescriptionElement = document.getElementById("pcs-edit-section-add-title-description"); + if (addTitleDescriptionElement) { + addTitleDescriptionElement.insertAdjacentHTML("beforebegin",`

    ${description}

    `); + addTitleDescriptionElement.parentElement.removeChild(addTitleDescriptionElement); + return true; + } + + //else replace existing description + var editTitleDescriptionElement = document.getElementById('pcs-edit-section-title-description'); + if (editTitleDescriptionElement) { + editTitleDescriptionElement.innerHTML = description; + return true; + } + return false; + } + injectTitleDescription(`\(newDescription.sanitizedForJavaScriptTemplateLiterals)`); + """ + + webView.evaluateJavaScript(javascript) { (result, error) in + DispatchQueue.main.async { + if let error = error { + DDLogDebug("Failure in injectNewDescriptionIntoArticleContent: \(error)") + completion(.failure(error)) + return + } + + guard let boolResult = result as? Bool, + boolResult == true else { + completion(.failure(ArticleEditingDescriptionError.failureInjectingNewDescription)) + return + } + + completion(.success(())) + } + } + } +} + +extension ArticleViewController: SectionEditorViewControllerDelegate { + func sectionEditorDidFinishEditing(_ sectionEditor: SectionEditorViewController, result: Result) { + switch result { + case .failure(let error): + showError(error) + case .success(let changes): + dismiss(animated: true) + waitForNewContentAndRefresh(changes.newRevisionID) + } + } + + func sectionEditorDidCancelEditing(_ sectionEditor: SectionEditorViewController, navigateToURL url: URL?) { + dismiss(animated: true) { + self.navigate(to: url) + } + } + + func sectionEditorDidFinishLoadingWikitext(_ sectionEditor: SectionEditorViewController) { + + } +} + +extension ArticleViewController: DescriptionEditViewControllerDelegate { + func descriptionEditViewControllerEditSucceeded(_ descriptionEditViewController: DescriptionEditViewController, result: ArticleDescriptionPublishResult) { + injectNewDescriptionIntoArticleContent(result.newDescription) { [weak self] injectResult in + + guard let self = self else { + return + } + + switch injectResult { + case .failure(let error): + DDLogError("Failure injecting new description into article content, refreshing instead: \(error)") + self.waitForNewContentAndRefresh(result.newRevisionID) + case .success: + break + } + } + } +} + +// Save these strings in case we need them - right now I don't think mobile-html even sends the event if they can't edit +// WMFLocalizedStringWithDefaultValue(@"page-protected-can-not-edit-title", nil, nil, @"This page is protected", @"Title of alert dialog shown when trying to edit a page that is protected beyond what the user can edit.") +// WMFLocalizedStringWithDefaultValue(@"page-protected-can-not-edit", nil, nil, @"You do not have the rights to edit this page", @"Text of alert dialog shown when trying to edit a page that is protected beyond what the user can edit.") diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+FindInPage.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+FindInPage.swift new file mode 100644 index 0000000..c07a4f0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+FindInPage.swift @@ -0,0 +1,136 @@ +struct ArticleFindInPageState { + var view: FindAndReplaceKeyboardBar? + + var matches: [String] = [] { + didSet { + updateViewState() + } + } + var selectedIndex: Int = -1 { + didSet { + updateViewState() + } + } + + var selectedMatch: String? { + guard selectedIndex >= 0, selectedIndex < matches.count else { + return nil + } + return matches[selectedIndex] + } + + mutating func next() { + guard matches.count > 0 else { + return + } + guard selectedIndex < matches.count - 1 else { + selectedIndex = 0 + return + } + selectedIndex += 1 + } + + mutating func previous() { + guard matches.count > 0 else { + return + } + + guard selectedIndex > 0 else { + selectedIndex = matches.count - 1 + return + } + + selectedIndex -= 1 + } + + func updateViewState() { + view?.updateMatchCounts(index: selectedIndex, total: UInt(matches.count)) + } +} +extension ArticleViewController { + func createFindInPageViewIfNecessary() { + guard findInPage.view == nil else { + return + } + let view = FindAndReplaceKeyboardBar.wmf_viewFromClassNib()! + view.delegate = self + view.apply(theme: theme) + findInPage.view = view + } + + func showFindInPage() { + createFindInPageViewIfNecessary() + becomeFirstResponder() + findInPage.view?.show() + } + + func hideFindInPage(_ completion: (() -> Void)? = nil) { + resetFindInPage { + self.findInPage.view?.hide() + self.resignFirstResponder() + completion?() + } + } + + func resetFindInPage(_ completion: (() -> Void)? = nil) { + webView.evaluateJavaScript("window.wmf.findInPage.removeSearchTermHighlights()", completionHandler: { obj, error in + self.findInPage.matches = [] + self.findInPage.selectedIndex = -1 + self.findInPage.view?.resetFind() + if completion != nil { + completion?() + } + }) + } + + func scrollToAndFocusOnFirstFindInPageMatch() { + findInPage.selectedIndex = -1 + keyboardBarDidTapNext(findInPage.view) + } + + func scrollToAndFocusOnSelectedMatch() { + guard let selectedMatch = findInPage.selectedMatch else { + return + } + scroll(to: selectedMatch, centered: true, animated: true) + webView.evaluateJavaScript("window.wmf.findInPage.useFocusStyleForHighlightedSearchTermWithId(`\(selectedMatch.sanitizedForJavaScriptTemplateLiterals)`)", completionHandler: nil) + } +} + +extension ArticleViewController: FindAndReplaceKeyboardBarDelegate { + func keyboardBar(_ keyboardBar: FindAndReplaceKeyboardBar, didChangeSearchTerm searchTerm: String?) { + guard let searchTerm = searchTerm?.sanitizedForJavaScriptTemplateLiterals else { + return + } + webView.evaluateJavaScript("window.wmf.findInPage.findAndHighlightAllMatchesForSearchTerm(`\(searchTerm)`)", completionHandler: { obj, error in + self.findInPage.matches = obj as? [String] ?? [] + self.scrollToAndFocusOnFirstFindInPageMatch() + }) + } + + func keyboardBarDidTapClose(_ keyboardBar: FindAndReplaceKeyboardBar) { + hideFindInPage() + } + + func keyboardBarDidTapClear(_ keyboardBar: FindAndReplaceKeyboardBar) { + resetFindInPage() + } + + func keyboardBarDidTapPrevious(_ keyboardBar: FindAndReplaceKeyboardBar) { + findInPage.previous() + scrollToAndFocusOnSelectedMatch() + } + + func keyboardBarDidTapNext(_ keyboardBar: FindAndReplaceKeyboardBar?) { + findInPage.next() + scrollToAndFocusOnSelectedMatch() + } + + func keyboardBarDidTapReturn(_ keyboardBar: FindAndReplaceKeyboardBar) { + findInPage.view?.hide() + } + + func keyboardBarDidTapReplace(_ keyboardBar: FindAndReplaceKeyboardBar, replaceText: String, replaceType: ReplaceType) { + // no-op, not showing replace bar in this context + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+LinkPreviewing.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+LinkPreviewing.swift new file mode 100644 index 0000000..9e1a09b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+LinkPreviewing.swift @@ -0,0 +1,180 @@ +// MARK: - Context Menu for ArticleVC (iOS 13 and later) +// All functions in this extension are for Context Menus (used in iOS 13 and later) +extension ArticleViewController: ArticleContextMenuPresenting, WKUIDelegate { + func getPeekViewControllerAsync(for destination: Router.Destination, completion: @escaping (UIViewController?) -> Void) { + switch destination { + case .inAppLink(let linkURL): + // Request the media list to see if this is a link from the gallery + getMediaList { (result) in + switch result { + case .success(let mediaList): + // Check the media list items to find the item for this link + let fileName = linkURL.lastPathComponent + if let item = mediaList.items.first(where: { (item) -> Bool in return item.title == fileName }) { + let galleryVC = self.getGalleryViewController(for: item, in: mediaList) + galleryVC.setOverlayViewTopBarHidden(true) + completion(galleryVC) + } else { + completion(nil) + } + case .failure(let error): + self.showError(error) + completion(nil) + } + } + default: + completion(getPeekViewController(for: destination)) + } + } + + var contextMenuItems: [UIAction] { + // Read action + let readActionTitle = WMFLocalizedString("button-read-now", value: "Read now", comment: "Read now button text used in various places.") + let readAction = UIAction(title: readActionTitle, handler: { (action) in + self.articlePreviewingDelegate?.readMoreArticlePreviewActionSelected(with: self) + }) + + var actions = [readAction] + + // Save action + let logReadingListsSaveIfNeeded = { [weak self] in + guard let delegate = self?.articlePreviewingDelegate as? EventLoggingEventValuesProviding else { + return + } + self?.readingListsFunnel.logSave(category: delegate.eventLoggingCategory, label: delegate.eventLoggingLabel, articleURL: self?.articleURL) + } + if articleURL.namespace == .main { + let saveActionTitle = article.isAnyVariantSaved ? WMFLocalizedString("button-saved-remove", value: "Remove from saved", comment: "Remove from saved button text used in various places.") : CommonStrings.saveTitle + let saveAction = UIAction(title: saveActionTitle, handler: { (action) in + let isSaved = self.dataStore.savedPageList.toggleSavedPage(for: self.articleURL) + let notification = isSaved ? CommonStrings.accessibilitySavedNotification : CommonStrings.accessibilityUnsavedNotification + UIAccessibility.post(notification: .announcement, argument: notification) + self.articlePreviewingDelegate?.saveArticlePreviewActionSelected(with: self, didSave: isSaved, articleURL: self.articleURL) + }) + actions.append(saveAction) + } + + // Location action + if article.location != nil { + let placeActionTitle = WMFLocalizedString("page-location", value: "View on a map", comment: "Label for button used to show an article on the map") + let placeAction = UIAction(title: placeActionTitle, handler: { (action) in + self.articlePreviewingDelegate?.viewOnMapArticlePreviewActionSelected(with: self) + }) + actions.append(placeAction) + } + + // Share action + let shareActionTitle = CommonStrings.shareMenuTitle + let shareAction = UIAction(title: shareActionTitle, handler: { (action) in + guard let presenter = self.articlePreviewingDelegate as? UIViewController else { + return + } + let customActivity = self.addToReadingListActivity(with: presenter, eventLogAction: logReadingListsSaveIfNeeded) + guard let shareActivityViewController = self.sharingActivityViewController(with: nil, button: self.toolbarController.shareButton, shareFunnel: self.shareFunnel, customActivities: [customActivity]) else { + return + } + self.articlePreviewingDelegate?.shareArticlePreviewActionSelected(with: self, shareActivityController: shareActivityViewController) + }) + + actions.append(shareAction) + + return actions + } + + var previewMenuItems: [UIMenuElement]? { + return contextMenuItems + } + + func webView(_ webView: WKWebView, contextMenuConfigurationForElement elementInfo: WKContextMenuElementInfo, completionHandler: @escaping (UIContextMenuConfiguration?) -> Void) { + self.contextMenuConfigurationForElement(elementInfo, completionHandler: completionHandler) + } + + func webView(_ webView: WKWebView, contextMenuForElement elementInfo: WKContextMenuElementInfo, willCommitWithAnimator animator: UIContextMenuInteractionCommitAnimating) { + guard + elementInfo.linkURL != nil, + let vc = animator.previewViewController + else { + return + } + + animator.preferredCommitStyle = .pop + + animator.addCompletion { + self.commitPreview(of: vc) + } + } + + func getPeekViewController(for destination: Router.Destination) -> UIViewController? { + switch destination { + case .article(let newArticleURL): + guard var currentArticleWithoutFragment = URLComponents(url: articleURL, resolvingAgainstBaseURL: false), + var newArticleWithoutFragment = URLComponents(url: newArticleURL, resolvingAgainstBaseURL: false) else { + // Not a valid URL + return nil + } + currentArticleWithoutFragment.fragment = nil + newArticleWithoutFragment.fragment = nil + + let isDestinationReferenceForCurrentArticle = currentArticleWithoutFragment == newArticleWithoutFragment + guard !isDestinationReferenceForCurrentArticle else { + return nil + } + + let articleVC = ArticleViewController(articleURL: newArticleURL, dataStore: dataStore, theme: theme) + articleVC?.articlePreviewingDelegate = self + articleVC?.wmf_addPeekableChildViewController(for: newArticleURL, dataStore: dataStore, theme: theme) + return articleVC + default: + return nil + } + } + + func commitPreview(of viewControllerToCommit: UIViewController) { + if let vc = viewControllerToCommit as? ArticleViewController { + readMoreArticlePreviewActionSelected(with: vc) + } else { + if let vc = viewControllerToCommit as? WMFImageGalleryViewController { + vc.setOverlayViewTopBarHidden(false) + } + presentEmbedded(viewControllerToCommit, style: .gallery) + } + } +} + +// MARK: Peek/Pop for Lead Image of ArticleVC +extension ArticleViewController: UIContextMenuInteractionDelegate { + func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { + // If gallery has not been opened on that article, self.mediaList is nil - and we need to create the media list + guard let mediaList = self.mediaList ?? MediaList(from: leadImageView.wmf_imageURLToFetch) else { + return nil + } + let previewProvider: UIContextMenuContentPreviewProvider = { + + let completion: ((Result) -> Void) = { _ in + // Nothing - We preload the medialist (if needed) to provide better performance in the likely case the user pops into image gallery. + } + self.getMediaList(completion) + + let galleryVC = self.getGalleryViewController(for: mediaList.items.first, in: mediaList) + galleryVC.setOverlayViewTopBarHidden(true) + return galleryVC + } + return UIContextMenuConfiguration(identifier: nil, previewProvider: previewProvider, actionProvider: nil) + } + + func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { + animator.addCompletion { + if self.mediaList != nil { + self.showLeadImage() + } else { + // fetchAndDisplayGalleryViewController() is very similar to showLeadImage(). In both cases, if self.mediaList doesn't exist, we make + // a network request to load it. When that mediaList network fetch is happening in showLeadImage, we don't do anything - so when + // transitioning from peek to pop, we are back to the main article with no indication we are in the process of popping. This + // only happens on very slow networks (especially since we try to preload the mediaList when peeking - see above), but when it happens + // it is not great for the user. Solution: If a mediaList needs to be fetched, fetchAndDisplayGalleryViewController() fakes the loading + // photo screen while loading that mediaList - providing a much smoother user experience. + self.fetchAndDisplayGalleryViewController() + } + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Media.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Media.swift new file mode 100644 index 0000000..385e7e4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Media.swift @@ -0,0 +1,102 @@ +import Foundation + +extension ArticleViewController { + func getMediaList(_ completion: @escaping (Result) -> Void) { + assert(Thread.isMainThread) + if let mediaList = mediaList { + completion(.success(mediaList)) + return + } + + let request: URLRequest + do { + request = try fetcher.mobileHTMLMediaListRequest(articleURL: articleURL) + } catch let error { + completion(.failure(error)) + return + } + + fetcher.fetchMediaList(with: request) { [weak self] (result, _) in + DispatchQueue.main.async { + switch result { + case .success(let mediaList): + self?.mediaList = mediaList + completion(.success(mediaList)) + case .failure(let error): + completion(.failure(error)) + } + } + } + } + + func showImage(src: String, href: String, width: Int?, height: Int?) { + getMediaList { (result) in + switch result { + case .failure(let error): + self.showError(error) + case .success(let mediaList): + self.showImage(in: mediaList, src: src, href: href, width: width, height: height) + } + } + } + + func showLeadImage() { + getMediaList { (result) in + switch result { + case .failure(let error): + self.showError(error) + case .success(let mediaList): + self.showImage(in: mediaList, item: mediaList.items.first(where: { $0.isLeadImage })) + } + } + } + + func showImage(in mediaList: MediaList, src: String, href: String, width: Int?, height: Int?) { + let title = href.replacingOccurrences(of: "./", with: "", options: .anchored) + guard let index = mediaList.items.firstIndex(where: { $0.title == title }) else { + showImage(in: mediaList, item: nil) + return + } + let item = mediaList.items[index] + showImage(in: mediaList, item: item) + } + + func showImage(in mediaList: MediaList, item: MediaListItem?) { + let gallery = getGalleryViewController(for: item, in: mediaList) + present(gallery, animated: true) + } + + func fetchAndDisplayGalleryViewController() { + /// We can't easily change the photos on the VC after it launches, so we create a loading VC screen and then add the proper galleryVC as a child after the data returns. + + let emptyPhotoViewer = WMFImageGalleryViewController(photos: nil) + let activityIndicator = UIActivityIndicatorView(style: .medium) + activityIndicator.color = .white + emptyPhotoViewer.view.addSubview(activityIndicator) + activityIndicator.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + activityIndicator.centerXAnchor.constraint(equalTo: emptyPhotoViewer.view.centerXAnchor), + activityIndicator.centerYAnchor.constraint(equalTo: emptyPhotoViewer.view.centerYAnchor) + ]) + activityIndicator.startAnimating() + present(emptyPhotoViewer, animated: true) + + getMediaList { (result) in + switch result { + case .failure(let error): + let completion = { + self.showError(error) + } + emptyPhotoViewer.dismiss(animated: true, completion: completion) + case .success(let mediaList): + activityIndicator.stopAnimating() + let gallery = self.getGalleryViewController(for: nil, in: mediaList) + emptyPhotoViewer.wmf_add(childController: gallery, andConstrainToEdgesOfContainerView: emptyPhotoViewer.view) + } + } + } + + func getGalleryViewController(for item: MediaListItem?, in mediaList: MediaList) -> MediaListGalleryViewController { + return MediaListGalleryViewController(articleURL: articleURL, mediaList: mediaList, dataStore: dataStore, initialItem: item, theme: theme) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+References.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+References.swift new file mode 100644 index 0000000..4664f83 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+References.swift @@ -0,0 +1,51 @@ +import Foundation + +extension ArticleViewController: UIPageViewControllerDelegate { + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + didFinishAnimating(pageViewController) + } +} + + +extension ArticleViewController: ReferenceBackLinksViewControllerDelegate, WMFReferencePageViewAppearanceDelegate, ReferenceShowing { + func referenceViewControllerUserDidTapClose(_ vc: ReferenceViewController) { + if vc is ReferenceBackLinksViewController { + dismissReferenceBackLinksViewController() + } else { + dismissReferencesPopover() + } + } + + func referenceBackLinksViewControllerUserDidNavigateTo(referenceBackLink: ReferenceBackLink, referenceBackLinksViewController: ReferenceBackLinksViewController) { + scroll(to: referenceBackLink.id, centered: true, highlighted: true, animated: true) { [weak self] (_) in + self?.webView.wmf_accessibilityCursor(toFragment: referenceBackLink.id) + self?.updateTableOfContentsHighlight() + } + } + + func referenceViewControllerUserDidNavigateBackToReference(_ vc: ReferenceViewController) { + referenceViewControllerUserDidTapClose(vc) + guard let referenceId = vc.referenceId else { + showGenericError() + return + } + let backLink = "back_link_\(referenceId)" + scroll(to: backLink, highlighted: true, animated: true) { [weak self] (_) in + self?.webView.wmf_accessibilityCursor(toFragment: backLink) + self?.updateTableOfContentsHighlight() + dispatchOnMainQueueAfterDelayInSeconds(1.0) { [weak self] in + self?.messagingController.removeElementHighlights() + } + } + } + + @objc func tappedWebViewBackground() { + dismissReferenceBackLinksViewController() + } +} + +extension ArticleViewController: UIGestureRecognizerDelegate { + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + return shouldRecognizeSimultaneousGesture(recognizer: gestureRecognizer) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Sharing.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Sharing.swift new file mode 100644 index 0000000..8dfb4a2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+Sharing.swift @@ -0,0 +1,65 @@ +extension ArticleViewController { + func shareArticle() { + themesPresenter.dismissReadingThemesPopoverIfActive(from: self) + webView.wmf_getSelectedText({ [weak self] selectedText in + guard let self = self else { + return + } + self.shareArticle(with: selectedText) + }) + } + + func shareArticle(with selectedText: String?) { + var activities: [UIActivity] = [] + let readingListActivity = addToReadingListActivity(with: self, eventLogAction: { + self.readingListsFunnel.logArticleSaveInCurrentArticle(self.articleURL) + }) + activities.append(readingListActivity) + + if let text = selectedText, !text.isEmpty { + let shareAFactActivity = CustomShareActivity(title: "Share-a-fact", imageName: "share-a-fact", action: { + self.shareFunnel?.logHighlight() + self.shareAFact(with: text) + }) + activities.append(shareAFactActivity) + } + + guard let vc = sharingActivityViewController(with: selectedText, button: toolbarController.shareButton, shareFunnel: shareFunnel, customActivities: activities) else { + return + } + present(vc, animated: true) + } + + func sharingActivityViewController(with textSnippet: String?, button: UIBarButtonItem, shareFunnel: WMFShareFunnel?, customActivities: [UIActivity]?) -> ShareActivityController? { + shareFunnel?.logShareButtonTappedResulting(inSelection: textSnippet) + let vc: ShareActivityController + let textActivitySource = WMFArticleTextActivitySource(article: article, shareText: textSnippet) + if let customActivities = customActivities, !customActivities.isEmpty { + vc = ShareActivityController(customActivities: customActivities, article: article, textActivitySource: textActivitySource) + } else { + vc = ShareActivityController(article: article, textActivitySource: textActivitySource) + } + vc.popoverPresentationController?.barButtonItem = button + return vc + } + + func shareAFact(with text: String) { + guard let shareViewController = ShareViewController(text: text, article: article, theme: theme) else { + return + } + present(shareViewController, animated: true) + } + + + func addToReadingListActivity(with presenter: UIViewController, eventLogAction: @escaping () -> Void) -> UIActivity { + let addToReadingListActivity = AddToReadingListActivity { + let vc = AddArticlesToReadingListViewController(with: self.dataStore, articles: [self.article], theme: self.theme) + vc.eventLogAction = eventLogAction + let nc = WMFThemeableNavigationController(rootViewController: vc, theme: self.theme) + nc.setNavigationBarHidden(true, animated: false) + presenter.present(nc, animated: true) + } + return addToReadingListActivity + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+SurveyAnnouncements.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+SurveyAnnouncements.swift new file mode 100644 index 0000000..783269c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+SurveyAnnouncements.swift @@ -0,0 +1,68 @@ +import Foundation + +extension ArticleViewController { + + func showSurveyAnnouncementPanel(surveyAnnouncementResult: SurveyAnnouncementsController.SurveyAnnouncementResult, linkState: ArticleAsLivingDocSurveyLinkState) { + let currentDate = Date() + guard state == .loaded, let surveyEndTime = surveyAnnouncementResult.announcement.endTime, currentDate.isBefore(surveyEndTime), + let googleFormattedArticleTitle = articleURL.wmf_title?.googleFormPercentEncodedPageTitle else { + return + } + + let didSeeModal: Bool + let isInExperiment: Bool + + switch linkState { + case .notInExperiment: + didSeeModal = false + isInExperiment = false + case .inExperimentFailureLoadingEvents, + .inExperimentLoadingEvents: + assertionFailure("We should not be attempting to show a survey for a user that is still loading or has failed to load events.") + return + case .inExperimentLoadedEventsDidNotSeeModal: + didSeeModal = false + isInExperiment = true + case .inExperimentLoadedEventsDidSeeModal: + didSeeModal = true + isInExperiment = true + } + + let newActionURLString = surveyAnnouncementResult.actionURLString + .replacingOccurrences(of: "{{articleTitle}}", with: googleFormattedArticleTitle) + .replacingOccurrences(of: "{{didSeeModal}}", with: "\(didSeeModal)") + .replacingOccurrences(of: "{{isInExperiment}}", with: "\(isInExperiment)") + + guard let actionURL = URL(string: newActionURLString) else { + assertionFailure("Cannot show survey - failure generating actionURL.") + return + } + + var vcToPresentSurvey: UIViewController? = self + if let presentedNavVC = presentedViewController as? UINavigationController, + let livingDocVC = presentedNavVC.viewControllers.first as? ArticleAsLivingDocViewController { + vcToPresentSurvey = presentedNavVC.viewControllers.count == 1 ? livingDocVC : nil + } + + vcToPresentSurvey?.wmf_showAnnouncementPanel(announcement: surveyAnnouncementResult.announcement, style: .minimal, primaryButtonTapHandler: { (sender) in + self.navigate(to: actionURL, useSafari: true) + // dismiss handler is called + }, secondaryButtonTapHandler: { (sender) in + // dismiss handler is called + }, footerLinkAction: { (url) in + self.navigate(to: url, useSafari: true) + // intentionally don't dismiss + }, traceableDismissHandler: { lastAction in + switch lastAction { + case .tappedBackground, .tappedClose, .tappedSecondary: + SurveyAnnouncementsController.shared.markSurveyAnnouncementAnswer(false, campaignIdentifier: surveyAnnouncementResult.campaignIdentifier) + case .tappedPrimary: + SurveyAnnouncementsController.shared.markSurveyAnnouncementAnswer(true, campaignIdentifier: surveyAnnouncementResult.campaignIdentifier) + case .none: + assertionFailure("Unexpected lastAction in Panel dismissHandler") + break + } + }, theme: self.theme) + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+TableOfContents.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+TableOfContents.swift new file mode 100644 index 0000000..1d5a5af --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+TableOfContents.swift @@ -0,0 +1,83 @@ +import CocoaLumberjackSwift + +extension ArticleViewController : ArticleTableOfContentsDisplayControllerDelegate { + func getVisibleSection(with completion: @escaping (_ sectionID: Int, _ anchor: String) -> Void) { + webView.evaluateJavaScript("window.wmf.elementLocation.getFirstOnScreenSection(\(navigationBar.visibleHeight))") { (result, error) in + guard + let info = result as? [String: Any], + let sectionId = info["id"] as? Int, + let anchor = info["anchor"] as? String + else { + DDLogError("Error getting first on screen section: \(String(describing: error))") + completion(-1, "") + return + } + completion(sectionId, anchor) + } + } + + func hideTableOfContents() { + tableOfContentsController.hide(animated: true) + toolbarController.update() + if tableOfContentsController.viewController.displayMode == .inline { + updateArticleMargins() + } + } + + func showTableOfContents() { + tableOfContentsController.show(animated: true) + toolbarController.update() + if tableOfContentsController.viewController.displayMode == .inline { + updateArticleMargins() + } + } + + var tableOfContentsDisplaySide: TableOfContentsDisplaySide { + return tableOfContentsSemanticContentAttribute == .forceRightToLeft ? .right : .left + } + + var tableOfContentsSemanticContentAttribute: UISemanticContentAttribute { + return MWKLanguageLinkController.semanticContentAttribute(forContentLanguageCode: articleURL.wmf_contentLanguageCode) + } + + func tableOfContentsDisplayControllerDidRecreateTableOfContentsViewController() { + updateTableOfContentsInsets() + } + + func tableOfContentsControllerWillDisplay(_ controller: TableOfContentsViewController) { + + } + + public func tableOfContentsController(_ controller: TableOfContentsViewController, didSelectItem item: TableOfContentsItem) { + switch tableOfContentsController.viewController.displayMode { + case .inline: + scroll(to: item.anchor, animated: true) + dispatchOnMainQueueAfterDelayInSeconds(1) { + self.webView.wmf_accessibilityCursor(toFragment: item.anchor) + } + case .modal: + scroll(to: item.anchor, animated: true) + // Don't dismiss immediately - it looks jarring - let the user see the ToC selection before dismissing + dispatchOnMainQueueAfterDelayInSeconds(0.15) { + self.hideTableOfContents() + // HAX: This is terrible, but iOS events not under our control would steal our focus if we didn't wait long enough here and due to problems in UIWebView, we cannot work around it either. + dispatchOnMainQueueAfterDelayInSeconds(1.5) { + self.webView.wmf_accessibilityCursor(toFragment: item.anchor) + } + } + } + } + + public func tableOfContentsControllerDidCancel(_ controller: TableOfContentsViewController) { + tableOfContentsController.hide(animated: true) + } + + public var tableOfContentsArticleLanguageURL: URL? { + let articleNSURL = self.articleURL as NSURL + if articleNSURL.wmf_isNonStandardURL { + return NSURL.wmf_URL(withDefaultSiteAndLanguageCode: "en") + } else { + return articleNSURL.wmf_site + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+WIconPopover.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+WIconPopover.swift new file mode 100644 index 0000000..4b3d3b3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController+WIconPopover.swift @@ -0,0 +1,41 @@ +import Foundation + +/// W icon popover tooltip +extension ArticleViewController { + var shouldShowWIconPopover: Bool { + guard + !UserDefaults.standard.wmf_didShowWIconPopover(), + presentedViewController == nil, + navigationController != nil, + navigationBar.navigationBarPercentHidden < 0.1 + else { + return false + } + return true + } + + func showWIconPopoverIfNecessary() { + guard shouldShowWIconPopover else { + return + } + perform(#selector(showWIconPopover), with: nil, afterDelay: 1.0) + } + + func cancelWIconPopoverDisplay() { + NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(showWIconPopover), object: nil) + } + + @objc func showWIconPopover() { + guard let titleView = navigationItem.titleView else { + return + } + let sourceRect = titleView.convert(titleView.bounds, to: view) + guard sourceRect.origin.y > 0 else { + return + } + let title = WMFLocalizedString("back-button-popover-title", value: "Tap to go back", comment: "Title for popover explaining the 'W' icon may be tapped to go back.") + let message = WMFLocalizedString("original-tab-button-popover-description", value: "Tap on the 'W' to return to the tab you started from", comment: "Description for popover explaining the 'W' icon may be tapped to return to the original tab.") + wmf_presentDynamicHeightPopoverViewController(forSourceRect: sourceRect, withTitle: title, message: message, width: 230, duration: 3) + UserDefaults.standard.wmf_setDidShowWIconPopover(true) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController.swift new file mode 100644 index 0000000..93a6b06 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleViewController.swift @@ -0,0 +1,1273 @@ +import UIKit +import WMF +import CocoaLumberjackSwift + +@objc(WMFArticleViewController) +class ArticleViewController: ViewController, HintPresenting { + enum ViewState { + case initial + case loading + case reloading + case loaded + case error + } + + internal lazy var toolbarController: ArticleToolbarController = { + return ArticleToolbarController(toolbar: toolbar, delegate: self) + }() + + /// Article holds article metadata (displayTitle, description, etc) and user state (isSaved, viewedDate, viewedFragment, etc) + internal var article: WMFArticle + internal var mediaList: MediaList? + + /// Use separate properties for URL and language code since they're optional on WMFArticle and to save having to re-calculate them + @objc public var articleURL: URL + let articleLanguageCode: String + + /// Set by the state restoration system + /// Scroll to the last viewed scroll position in this case + /// Also prioritize pulling data from cache (without revision/etag validation) so the user sees the article as quickly as possible + var isRestoringState: Bool = false + + /// Called when initial load starts + @objc public var loadCompletion: (() -> Void)? + + /// Called when initial JS setup is complete + @objc public var initialSetupCompletion: (() -> Void)? + + internal let schemeHandler: SchemeHandler + internal let dataStore: MWKDataStore + + private let cacheController: ArticleCacheController + + var session: Session { + return dataStore.session + } + + var configuration: Configuration { + return dataStore.configuration + } + + private var authManager: WMFAuthenticationManager { + return dataStore.authenticationManager + } + + internal lazy var fetcher: ArticleFetcher = ArticleFetcher(session: session, configuration: configuration) + + private var leadImageHeight: CGFloat = 210 + + private var contentSizeObservation: NSKeyValueObservation? = nil + + /// Current ETag of the web content response. Used to verify when content has changed on the server. + var currentETag: String? + + /// Used to delay reloading the web view to prevent `UIScrollView` jitter + fileprivate var shouldPerformWebRefreshAfterScrollViewDeceleration = false + + lazy var refreshControl: UIRefreshControl = { + let rc = UIRefreshControl() + rc.addTarget(self, action: #selector(refresh), for: .valueChanged) + return rc + }() + + lazy var referenceWebViewBackgroundTapGestureRecognizer: UITapGestureRecognizer = { + let tapGR = UITapGestureRecognizer(target: self, action: #selector(tappedWebViewBackground)) + tapGR.delegate = self + webView.scrollView.addGestureRecognizer(tapGR) + tapGR.isEnabled = false + return tapGR + }() + + // BEGIN: Article As Living Doc properties + private(set) var surveyTimerController: ArticleSurveyTimerController? + + lazy var articleAsLivingDocController = ArticleAsLivingDocController(delegate: self) + + var surveyAnnouncementResult: SurveyAnnouncementsController.SurveyAnnouncementResult? { + SurveyAnnouncementsController.shared.activeSurveyAnnouncementResultForArticleURL(articleURL) + } + // END: Article As Living Doc properties + + @objc init?(articleURL: URL, dataStore: MWKDataStore, theme: Theme, schemeHandler: SchemeHandler? = nil) { + guard let article = dataStore.fetchOrCreateArticle(with: articleURL) else { + return nil + } + let cacheController = dataStore.cacheController.articleCache + + self.articleURL = articleURL + self.articleLanguageCode = articleURL.wmf_languageCode ?? Locale.current.languageCode ?? "en" + self.article = article + + self.dataStore = dataStore + self.schemeHandler = schemeHandler ?? SchemeHandler(scheme: "app", session: dataStore.session) + self.cacheController = cacheController + + super.init(theme: theme) + + self.surveyTimerController = ArticleSurveyTimerController(delegate: self) + + // `viewDidLoad` isn't called when re-creating the navigation stack on an iPad, and hence a cold launch on iPad doesn't properly show article names when long-pressing the back button if this code is in `viewDidLoad` + self.navigationItem.backButtonTitle = articleURL.wmf_title + self.navigationItem.backButtonDisplayMode = .generic + } + + deinit { + contentSizeObservation?.invalidate() + messagingController.removeScriptMessageHandler() + articleLoadWaitGroup = nil + NotificationCenter.default.removeObserver(self) + } + + // MARK: WebView + + static let webProcessPool = WKProcessPool() + + private(set) var messagingController = ArticleWebMessagingController() + + lazy var webViewConfiguration: WKWebViewConfiguration = { + let configuration = WKWebViewConfiguration() + configuration.processPool = ArticleViewController.webProcessPool + configuration.setURLSchemeHandler(schemeHandler, forURLScheme: schemeHandler.scheme) + return configuration + }() + + lazy var webView: WKWebView = { + let webView = WMFWebView(frame: view.bounds, configuration: webViewConfiguration) + view.addSubview(webView) + return webView + }() + + // MARK: HintPresenting + + var hintController: HintController? + + // MARK: Find In Page + + var findInPage = ArticleFindInPageState() + + // MARK: Responder chain + + override var canBecomeFirstResponder: Bool { + return findInPage.view != nil + } + + override var inputAccessoryView: UIView? { + return findInPage.view + } + + // MARK: Lead Image + + @objc func userDidTapLeadImage() { + showLeadImage() + } + + func loadLeadImage(with leadImageURL: URL) { + leadImageHeightConstraint.constant = leadImageHeight + leadImageView.wmf_setImage(with: leadImageURL, detectFaces: true, onGPU: true, failure: { (error) in + DDLogError("Error loading lead image: \(error)") + }) { + self.updateLeadImageMargins() + self.updateArticleMargins() + + /// see implementation in `extension ArticleViewController: UIContextMenuInteractionDelegate` + let interaction = UIContextMenuInteraction(delegate: self) + self.leadImageView.addInteraction(interaction) + } + } + + lazy var leadImageLeadingMarginConstraint: NSLayoutConstraint = { + return leadImageView.leadingAnchor.constraint(equalTo: leadImageContainerView.leadingAnchor) + }() + + lazy var leadImageTrailingMarginConstraint: NSLayoutConstraint = { + return leadImageContainerView.trailingAnchor.constraint(equalTo: leadImageView.trailingAnchor) + }() + + lazy var leadImageHeightConstraint: NSLayoutConstraint = { + return leadImageContainerView.heightAnchor.constraint(equalToConstant: 0) + }() + + lazy var leadImageView: UIImageView = { + let imageView = NoIntrinsicContentSizeImageView(frame: .zero) + imageView.isUserInteractionEnabled = true + imageView.clipsToBounds = true + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.contentMode = .scaleAspectFill + imageView.accessibilityIgnoresInvertColors = true + let tapGR = UITapGestureRecognizer(target: self, action: #selector(userDidTapLeadImage)) + imageView.addGestureRecognizer(tapGR) + return imageView + }() + + lazy var leadImageBorderHeight: CGFloat = { + let scale = UIScreen.main.scale + return scale > 1 ? 0.5 : 1 + }() + + lazy var leadImageContainerView: UIView = { + + let height: CGFloat = 10 + let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: height)) + containerView.clipsToBounds = true + containerView.translatesAutoresizingMaskIntoConstraints = false + + let borderView = UIView(frame: CGRect(x: 0, y: height - leadImageBorderHeight, width: 1, height: leadImageBorderHeight)) + borderView.backgroundColor = UIColor(white: 0, alpha: 0.2) + borderView.autoresizingMask = [.flexibleTopMargin, .flexibleWidth] + + leadImageView.frame = CGRect(x: 0, y: 0, width: 1, height: height - leadImageBorderHeight) + containerView.addSubview(leadImageView) + containerView.addSubview(borderView) + return containerView + }() + + lazy var refreshOverlay: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + view.alpha = 0 + view.backgroundColor = .black + view.isUserInteractionEnabled = true + return view + }() + + override func updateViewConstraints() { + super.updateViewConstraints() + updateLeadImageMargins() + } + + func updateLeadImageMargins() { + let doesArticleUseLargeMargin = (tableOfContentsController.viewController.displayMode == .inline && !tableOfContentsController.viewController.isVisible) + var marginWidth: CGFloat = 0 + if doesArticleUseLargeMargin { + marginWidth = articleHorizontalMargin + } + leadImageLeadingMarginConstraint.constant = marginWidth + leadImageTrailingMarginConstraint.constant = marginWidth + } + + // MARK: Previewing + + public var articlePreviewingDelegate: ArticlePreviewingDelegate? + + // MARK: Layout + + override func scrollViewInsetsDidChange() { + super.scrollViewInsetsDidChange() + updateTableOfContentsInsets() + } + + override func viewLayoutMarginsDidChange() { + super.viewLayoutMarginsDidChange() + updateArticleMargins() + } + + internal func updateArticleMargins() { + + let defaultUpdateBlock = { + self.messagingController.updateMargins(with: self.articleMargins, leadImageHeight: self.leadImageHeightConstraint.constant) + } + + if articleAsLivingDocController.shouldAttemptToShowArticleAsLivingDoc { + messagingController.customUpdateMargins(with: articleMargins, leadImageHeight: self.leadImageHeightConstraint.constant) + } else { + defaultUpdateBlock() + } + + updateLeadImageMargins() + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + stashOffsetPercentage() + super.viewWillTransition(to: size, with: coordinator) + let marginUpdater: ((UIViewControllerTransitionCoordinatorContext) -> Void) = { _ in self.updateArticleMargins() } + coordinator.animate(alongsideTransition: marginUpdater) + } + + // MARK: Loading + + var state: ViewState = .initial { + didSet { + switch state { + case .initial: + break + case .reloading: + fallthrough + case .loading: + fakeProgressController.start() + case .loaded: + fakeProgressController.stop() + rethemeWebViewIfNecessary() + case .error: + fakeProgressController.stop() + } + } + } + + lazy private var fakeProgressController: FakeProgressController = { + let progressController = FakeProgressController(progress: navigationBar, delegate: navigationBar) + progressController.delay = 0.0 + return progressController + }() + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + setup() + super.viewDidLoad() + setupToolbar() // setup toolbar needs to be after super.viewDidLoad because the superview owns the toolbar + setupForStateRestorationIfNecessary() + surveyTimerController?.timerFireBlock = { [weak self] in + guard let self = self, + let result = self.surveyAnnouncementResult else { + return + } + + self.showSurveyAnnouncementPanel(surveyAnnouncementResult: result, linkState: self.articleAsLivingDocController.surveyLinkState) + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + tableOfContentsController.setup(with: traitCollection) + toolbarController.update() + loadIfNecessary() + startSignificantlyViewedTimer() + surveyTimerController?.viewWillAppear(withState: state) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + /// When jumping back to an article via long pressing back button (on iOS 14 or above), W button disappears. Couldn't find cause. It disappears between `viewWillAppear` and `viewDidAppear`, as setting this on the `viewWillAppear`doesn't fix the problem. If we can find source of this bad behavior, we can remove this next line. + setupWButton() + guard isFirstAppearance else { + return + } + showAnnouncementIfNeeded() + isFirstAppearance = false + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + tableOfContentsController.update(with: traitCollection) + toolbarController.update() + } + + override func wmf_removePeekableChildViewControllers() { + super.wmf_removePeekableChildViewControllers() + addToHistory() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + cancelWIconPopoverDisplay() + saveArticleScrollPosition() + stopSignificantlyViewedTimer() + surveyTimerController?.viewWillDisappear(withState: state) + } + + // MARK: Article load + + var articleLoadWaitGroup: DispatchGroup? + + func loadIfNecessary() { + guard state == .initial else { + return + } + load() + } + + func load() { + state = .loading + + setupPageContentServiceJavaScriptInterface { + let cachePolicy: WMFCachePolicy? = self.isRestoringState ? .foundation(.returnCacheDataElseLoad) : nil + self.loadPage(cachePolicy: cachePolicy) + } + } + + /// Waits for the article and article summary to finish loading (or re-loading) and performs post load actions + private func setupArticleLoadWaitGroup() { + assert(Thread.isMainThread) + + guard articleLoadWaitGroup == nil else { + return + } + + articleLoadWaitGroup = DispatchGroup() + articleLoadWaitGroup?.enter() // will leave on setup complete + articleLoadWaitGroup?.notify(queue: DispatchQueue.main) { [weak self] in + + guard let self = self else { + return + } + + self.articleAsLivingDocController.articleContentFinishedLoading() + + self.setupFooter() + self.shareIfNecessary() + self.restoreScrollStateIfNecessary() + self.articleLoadWaitGroup = nil + } + } + + internal func loadSummary(oldState: ViewState) { + guard let key = article.inMemoryKey else { + return + } + + var oldFeedPreview: WMFFeedArticlePreview? + if isWidgetCachedFeaturedArticle { + oldFeedPreview = article.feedArticlePreview() + } + + articleLoadWaitGroup?.enter() + let cachePolicy: URLRequest.CachePolicy? = oldState == .reloading ? .reloadRevalidatingCacheData : nil + + self.dataStore.articleSummaryController.updateOrCreateArticleSummaryForArticle(withKey: key, cachePolicy: cachePolicy) { (article, error) in + defer { + self.articleLoadWaitGroup?.leave() + self.updateMenuItems() + } + guard let article = article else { + return + } + self.article = article + + if let oldFeedPreview, + let newFeedPreview = article.feedArticlePreview(), + oldFeedPreview != newFeedPreview { + SharedContainerCacheClearFeaturedArticleWrapper.clearOutFeaturedArticleWidgetCache() + WidgetController.shared.reloadFeaturedArticleWidgetIfNecessary() + } + + // Handle redirects + guard let newKey = article.inMemoryKey, newKey != key, let newURL = article.url else { + return + } + self.articleURL = newURL + self.addToHistory() + } + } + + func loadPage(cachePolicy: WMFCachePolicy? = nil, revisionID: UInt64? = nil) { + defer { + callLoadCompletionIfNecessary() + } + + guard var request = try? fetcher.mobileHTMLRequest(articleURL: articleURL, revisionID: revisionID, scheme: schemeHandler.scheme, cachePolicy: cachePolicy, isPageView: true) else { + showGenericError() + state = .error + return + } + + // Add the URL fragment to request, if the fragment exists + if let articleFragment = URLComponents(url: articleURL, resolvingAgainstBaseURL: true)?.fragment, + let url = request.url, + var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true) { + urlComponents.fragment = articleFragment + request.url = urlComponents.url + } + + articleAsLivingDocController.articleContentWillBeginLoading(traitCollection: traitCollection, theme: theme) + + webView.load(request) + } + + func syncCachedResourcesIfNeeded() { + guard let groupKey = articleURL.wmf_databaseKey else { + return + } + + fetcher.isCached(articleURL: articleURL) { [weak self] (isCached) in + + guard let self = self, + isCached else { + return + } + + self.cacheController.syncCachedResources(url: self.articleURL, groupKey: groupKey) { (result) in + switch result { + case .success(let itemKeys): + DDLogDebug("successfully synced \(itemKeys.count) resources") + case .failure(let error): + DDLogDebug("failed to synced resources for \(groupKey): \(error)") + } + } + } + } + + // MARK: History + + func addToHistory() { + // Don't add to history if we're in peek/pop + guard self.wmf_PeekableChildViewController == nil else { + return + } + try? article.addToReadHistory() + } + + var significantlyViewedTimer: Timer? + + func startSignificantlyViewedTimer() { + guard significantlyViewedTimer == nil, !article.wasSignificantlyViewed else { + return + } + significantlyViewedTimer = Timer.scheduledTimer(withTimeInterval: 30, repeats: false, block: { [weak self] (timer) in + self?.article.wasSignificantlyViewed = true + self?.stopSignificantlyViewedTimer() + }) + } + + func stopSignificantlyViewedTimer() { + significantlyViewedTimer?.invalidate() + significantlyViewedTimer = nil + } + + // MARK: Scroll State Restoration + + /// Tracks desired scroll restoration. + /// This occurs when a user is re-opening the app and expects the article to be scrolled to the last position they were reading at or when a user taps on a link that goes to a particular section in another article. + /// The state needs to be preserved because the given offset or anchor will not be availble until after the page fully loads. + /// `scrollToOffset` and `scrollToAnchor` will track attempts made after each `webView.contentSize` change, hoping the requested offset or anchor is available. After a certain number of attempts, it's assumed that the value is invalid and the restoration logic gives up. + private enum ScrollRestorationState { + case none + /// Scroll to absolute Y offset + case scrollToOffset(_ offsetY: CGFloat, animated: Bool, attempt: Int = 1, maxAttempts: Int = 5, completion: ((Bool, Bool) -> Void)? = nil) + /// Scroll to percentage Y offset + case scrollToPercentage(_ percentageOffsetY: CGFloat) + /// Scroll to anchor, an id of an element on the page + case scrollToAnchor(_ anchor: String, attempt: Int = 1, maxAttempts: Int = 5, completion: ((Bool, Bool) -> Void)? = nil) + } + + private var scrollRestorationState: ScrollRestorationState = .none + + /// Checks scrollRestorationState and performs the necessary scroll restoration + private func restoreScrollStateIfNecessary() { + switch scrollRestorationState { + case .none: + break + case .scrollToOffset(let offset, let animated, let attempt, let maxAttempts, let completion): + scrollRestorationState = .none + self.scroll(to: CGPoint(x: 0, y: offset), animated: animated) { [weak self] (success) in + guard !success, attempt < maxAttempts else { + completion?(success, attempt >= maxAttempts) + return + } + self?.scrollRestorationState = .scrollToOffset(offset, animated: animated, attempt: attempt + 1, maxAttempts: maxAttempts, completion: completion) + } + case .scrollToPercentage(let verticalOffsetPercentage): + scrollRestorationState = .none + webView.scrollView.verticalOffsetPercentage = verticalOffsetPercentage + case .scrollToAnchor(let anchor, let attempt, let maxAttempts, let completion): + scrollRestorationState = .none + self.scroll(to: anchor, animated: true) { [weak self] (success) in + guard !success, attempt < maxAttempts else { + completion?(success, attempt >= maxAttempts) + return + } + self?.scrollRestorationState = .scrollToAnchor(anchor, attempt: attempt + 1, maxAttempts: maxAttempts, completion: completion) + } + + // HACK: Sometimes the `scroll_to_anchor` message is not triggered from the web view over the JS bridge, even after prepareForScrollToAnchor successfully goes through. This means the completion block above is queued to scrollToAnchorCompletions but never run. We are trying to scroll again here once more after a slight delay in hopes of triggering `scroll_to_anchor` again. + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { [weak self] in + + guard let self = self else { + return + } + + // This conditional check should target the bug a little closer, since scrollToAnchorCompletions are cleaned out after the last `scroll_to_anchor` message is received. Remaining scrollToAnchorCompletions at this point indicates that likely we're hitting the missing `scroll_to_anchor` message bug. + if self.scrollToAnchorCompletions.count > 0 { + self.scroll(to: anchor, animated: false) + } + } + } + } + + internal func stashOffsetPercentage() { + let offset = webView.scrollView.verticalOffsetPercentage + // negative and 0 offsets make small errors in scrolling, allow it to automatically handle those cases + if offset > 0 { + scrollRestorationState = .scrollToPercentage(offset) + } + } + + private func checkForScrollToAnchor(in response: HTTPURLResponse) { + guard let fragment = response.url?.fragment else { + return + } + scrollRestorationState = .scrollToAnchor(fragment, attempt: 1) + } + + // MARK: Article State Restoration + + /// Save article scroll position for restoration later + func saveArticleScrollPosition() { + getVisibleSection { (sectionId, anchor) in + assert(Thread.isMainThread) + self.article.viewedScrollPosition = Double(self.webView.scrollView.contentOffset.y) + self.article.viewedFragment = anchor + try? self.article.managedObjectContext?.save() + } + } + + /// Perform any necessary initial configuration for state restoration + func setupForStateRestorationIfNecessary() { + guard isRestoringState else { + return + } + setWebViewHidden(true, animated: false) + } + + /// Translates an article's viewedScrollPosition or viewedFragment values to a scrollRestorationState. These values are saved to the article object when the ArticleVC disappears,the app is backgrounded, or an edit is made and the article is reloaded. + func assignScrollStateFromArticleFlagsIfNecessary() { + guard isRestoringState else { + return + } + isRestoringState = false + let scrollPosition = CGFloat(article.viewedScrollPosition) + if scrollPosition > 0 { + scrollRestorationState = .scrollToOffset(scrollPosition, animated: false, completion: { [weak self] success, maxedAttempts in + if success || maxedAttempts { + self?.setWebViewHidden(false, animated: true) + } + }) + } else if let fragment = article.viewedFragment { + scrollRestorationState = .scrollToAnchor(fragment, completion: { [weak self] success, maxedAttempts in + if success || maxedAttempts { + self?.setWebViewHidden(false, animated: true) + } + }) + } else { + setWebViewHidden(false, animated: true) + } + } + + func setWebViewHidden(_ hidden: Bool, animated: Bool, completion: ((Bool) -> Void)? = nil) { + let block = { + self.webView.alpha = hidden ? 0 : 1 + } + guard animated else { + block() + completion?(true) + return + } + UIView.animate(withDuration: 0.3, animations: block, completion: completion) + } + + func callLoadCompletionIfNecessary() { + loadCompletion?() + loadCompletion = nil + } + + // MARK: Theme + + lazy var themesPresenter: ReadingThemesControlsArticlePresenter = { + return ReadingThemesControlsArticlePresenter(readingThemesControlsViewController: themesViewController, wkWebView: webView, readingThemesControlsToolbarItem: toolbarController.themeButton) + }() + + private lazy var themesViewController: ReadingThemesControlsViewController = { + return ReadingThemesControlsViewController(nibName: ReadingThemesControlsViewController.nibName, bundle: nil) + }() + + override func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.paperBackground + webView.scrollView.indicatorStyle = theme.scrollIndicatorStyle + toolbarController.apply(theme: theme) + tableOfContentsController.apply(theme: theme) + findInPage.view?.apply(theme: theme) + if state == .loaded { + messagingController.updateTheme(theme) + } + } + + private func rethemeWebViewIfNecessary() { + // Sometimes the web view theme and article theme is out if sync + // The last call to update the theme comes before the web view is fully loaded to accept a theme change + // In this case we are checking and triggering a web view theme change once more after the JS bridge indicates it's loaded + // https://phabricator.wikimedia.org/T275239 + if let webViewTheme = messagingController.parameters?.theme, + webViewTheme != self.theme.webName { + messagingController.updateTheme(theme) + } + } + + // MARK: Sharing + + private var isSharingWhenReady = false + + @objc public func shareArticleWhenReady() { + isSharingWhenReady = true + } + + func shareIfNecessary() { + guard isSharingWhenReady else { + return + } + isSharingWhenReady = false + shareArticle() + } + + // MARK: Navigation + + @objc(showAnchor:) + func show(anchor: String) { + dismiss(animated: false) + scroll(to: anchor, animated: true) + } + + // MARK: Refresh + + @objc public func refresh() { + state = .reloading + if !shouldPerformWebRefreshAfterScrollViewDeceleration { + updateRefreshOverlay(visible: true) + } + shouldPerformWebRefreshAfterScrollViewDeceleration = true + } + + /// Preserves the current scroll position, loads the provided revisionID or waits for a change in etag on the mobile-html response, then refreshes the page and restores the prior scroll position + internal func waitForNewContentAndRefresh(_ revisionID: UInt64? = nil) { + showNavigationBar() + state = .reloading + saveArticleScrollPosition() + isRestoringState = true + setupForStateRestorationIfNecessary() + // If a revisionID was provided, just load that revision + if let revisionID = revisionID { + performWebViewRefresh(revisionID) + return + } + // If no revisionID was provided, wait for the ETag to change + guard let eTag = currentETag else { + performWebViewRefresh() + return + } + fetcher.waitForMobileHTMLChange(articleURL: articleURL, eTag: eTag, maxAttempts: 5) { (result) in + DispatchQueue.main.async { + switch result { + case .failure(let error): + self.showError(error, sticky: true) + fallthrough + default: + self.performWebViewRefresh() + } + } + } + } + + internal func performWebViewRefresh(_ revisionID: UInt64? = nil) { + + articleAsLivingDocController.articleDidTriggerPullToRefresh() + + switch Configuration.current.environment { + case .local(let options): + if options.contains(.localPCS) { + webView.reloadFromOrigin() + } else { + loadPage(cachePolicy: .noPersistentCacheOnError, revisionID: revisionID) + } + default: + loadPage(cachePolicy: .noPersistentCacheOnError, revisionID: revisionID) + } + } + + internal func updateRefreshOverlay(visible: Bool, animated: Bool = true) { + let duration = animated ? (visible ? 0.15 : 0.1) : 0.0 + let alpha: CGFloat = visible ? 0.3 : 0.0 + UIViewPropertyAnimator.runningPropertyAnimator(withDuration: duration, delay: 0, options: [.curveEaseIn], animations: { + self.refreshOverlay.alpha = alpha + }) + toolbarController.setToolbarButtons(enabled: !visible) + } + + // MARK: Overrideable functionality + + internal func handleLink(with href: String) { + guard let resolvedURL = articleURL.resolvingRelativeWikiHref(href) else { + showGenericError() + return + } + // Check if this is the same article by comparing in-memory keys + guard resolvedURL.wmf_inMemoryKey == articleURL.wmf_inMemoryKey else { + + let userInfo: [AnyHashable : Any] = [RoutingUserInfoKeys.source: RoutingUserInfoSourceValue.article.rawValue] + navigate(to: resolvedURL, userInfo: userInfo) + + return + } + // Check for a fragment - if this is the same article and there's no fragment just do nothing? + guard let anchor = resolvedURL.fragment?.removingPercentEncoding else { + return + } + + articleAsLivingDocController.handleArticleAsLivingDocLinkForAnchor(anchor, articleURL: articleURL) + scroll(to: anchor, animated: true) + } + + // MARK: Table of contents + + lazy var tableOfContentsController: ArticleTableOfContentsDisplayController = ArticleTableOfContentsDisplayController(articleView: webView, delegate: self, theme: theme) + + var tableOfContentsItems: [TableOfContentsItem] = [] { + didSet { + tableOfContentsController.viewController.reload() + } + } + + var previousContentOffsetYForTOCUpdate: CGFloat = 0 + + func updateTableOfContentsHighlightIfNecessary() { + guard tableOfContentsController.viewController.displayMode == .inline, tableOfContentsController.viewController.isVisible else { + return + } + let scrollView = webView.scrollView + guard abs(previousContentOffsetYForTOCUpdate - scrollView.contentOffset.y) > 15 else { + return + } + guard scrollView.isTracking || scrollView.isDragging || scrollView.isDecelerating else { + return + } + updateTableOfContentsHighlight() + } + + func updateTableOfContentsHighlight() { + previousContentOffsetYForTOCUpdate = webView.scrollView.contentOffset.y + getVisibleSection { (sectionId, _) in + self.tableOfContentsController.selectAndScroll(to: sectionId, animated: true) + } + } + + func updateTableOfContentsInsets() { + let tocScrollView = tableOfContentsController.viewController.tableView + let topOffsetY = 0 - tocScrollView.contentInset.top + let wasAtTop = tocScrollView.contentOffset.y <= topOffsetY + switch tableOfContentsController.viewController.displayMode { + case .inline: + tocScrollView.contentInset = webView.scrollView.contentInset + tocScrollView.verticalScrollIndicatorInsets = webView.scrollView.verticalScrollIndicatorInsets + case .modal: + tocScrollView.contentInset = UIEdgeInsets(top: view.safeAreaInsets.top, left: 0, bottom: view.safeAreaInsets.bottom, right: 0) + tocScrollView.scrollIndicatorInsets = tocScrollView.contentInset + } + guard wasAtTop else { + return + } + tocScrollView.contentOffset = CGPoint(x: 0, y: topOffsetY) + } + + // MARK: Scroll + + var scrollToAnchorCompletions: [ScrollToAnchorCompletion] = [] + var scrollViewAnimationCompletions: [() -> Void] = [] + + override func scrollViewDidScroll(_ scrollView: UIScrollView) { + super.scrollViewDidScroll(scrollView) + updateTableOfContentsHighlightIfNecessary() + } + + override func scrollViewDidScrollToTop(_ scrollView: UIScrollView) { + super.scrollViewDidScrollToTop(scrollView) + updateTableOfContentsHighlight() + } + + override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + super.scrollViewWillBeginDragging(scrollView) + dismissReferencesPopover() + hintController?.dismissHintDueToUserInteraction() + } + + override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + super.scrollViewDidEndDecelerating(scrollView) + if shouldPerformWebRefreshAfterScrollViewDeceleration { + webView.scrollView.showsVerticalScrollIndicator = false + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: { + self.performWebViewRefresh() + }) + } + } + + // MARK: Analytics + + internal lazy var editFunnel: EditFunnel = EditFunnel.shared + internal lazy var shareFunnel: WMFShareFunnel? = WMFShareFunnel(article: article) + internal lazy var savedPagesFunnel: SavedPagesFunnel = SavedPagesFunnel() + internal lazy var readingListsFunnel = ReadingListsFunnel.shared +} + +private extension ArticleViewController { + + func setup() { + setupWButton() + setupSearchButton() + addNotificationHandlers() + setupWebView() + setupMessagingController() + } + + // MARK: Notifications + + func addNotificationHandlers() { + NotificationCenter.default.addObserver(self, selector: #selector(didReceiveArticleUpdatedNotification), name: NSNotification.Name.WMFArticleUpdated, object: article) + NotificationCenter.default.addObserver(self, selector: #selector(applicationWillResignActive), name: UIApplication.willResignActiveNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil) + contentSizeObservation = webView.scrollView.observe(\.contentSize) { [weak self] (scrollView, change) in + self?.contentSizeDidChange() + } + } + + /// Track and debounce `contentSize` changes to wait for a desired scroll position to become available. See `ScrollRestorationState` for more information. + func contentSizeDidChange() { + // debounce + NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(debouncedContentSizeDidChange), object: nil) + perform(#selector(debouncedContentSizeDidChange), with: nil, afterDelay: 0.1) + } + + @objc func debouncedContentSizeDidChange() { + restoreScrollStateIfNecessary() + } + + @objc func didReceiveArticleUpdatedNotification(_ notification: Notification) { + toolbarController.setSavedState(isSaved: article.isAnyVariantSaved) + } + + @objc func applicationWillResignActive(_ notification: Notification) { + saveArticleScrollPosition() + stopSignificantlyViewedTimer() + surveyTimerController?.willResignActive(withState: state) + } + + @objc func applicationDidBecomeActive(_ notification: Notification) { + startSignificantlyViewedTimer() + surveyTimerController?.didBecomeActive(withState: state) + } + + func setupSearchButton() { + navigationItem.rightBarButtonItem = AppSearchBarButtonItem.newAppSearchBarButtonItem + } + + func setupMessagingController() { + messagingController.delegate = self + } + + func setupWebView() { + // Add the stack view that contains the table of contents and the web view. + // This stack view is owned by the tableOfContentsController to control presentation of the table of contents + view.wmf_addSubviewWithConstraintsToEdges(tableOfContentsController.stackView) + view.widthAnchor.constraint(equalTo: tableOfContentsController.inlineContainerView.widthAnchor, multiplier: 3).isActive = true + + // Prevent flash of white in dark mode + webView.isOpaque = false + webView.backgroundColor = .clear + webView.scrollView.backgroundColor = .clear + + // Scroll view + scrollView = webView.scrollView // so that content insets are inherited + scrollView?.delegate = self + webView.scrollView.keyboardDismissMode = .interactive + webView.scrollView.refreshControl = refreshControl + + // Lead image + setupLeadImageView() + + // Add overlay to prevent interaction while reloading + webView.wmf_addSubviewWithConstraintsToEdges(refreshOverlay) + + // Delegates + webView.uiDelegate = self + webView.navigationDelegate = self + + // User Agent + webView.customUserAgent = WikipediaAppUtils.versionedUserAgent() + } + + /// Adds the lead image view to the web view's scroll view and configures the associated constraints + func setupLeadImageView() { + webView.scrollView.addSubview(leadImageContainerView) + + let leadingConstraint = leadImageContainerView.leadingAnchor.constraint(equalTo: webView.leadingAnchor) + let trailingConstraint = webView.trailingAnchor.constraint(equalTo: leadImageContainerView.trailingAnchor) + let topConstraint = webView.scrollView.topAnchor.constraint(equalTo: leadImageContainerView.topAnchor) + let imageTopConstraint = leadImageView.topAnchor.constraint(equalTo: leadImageContainerView.topAnchor) + imageTopConstraint.priority = UILayoutPriority(rawValue: 999) + let imageBottomConstraint = leadImageContainerView.bottomAnchor.constraint(equalTo: leadImageView.bottomAnchor, constant: leadImageBorderHeight) + NSLayoutConstraint.activate([topConstraint, leadingConstraint, trailingConstraint, leadImageHeightConstraint, imageTopConstraint, imageBottomConstraint, leadImageLeadingMarginConstraint, leadImageTrailingMarginConstraint]) + + articleAsLivingDocController.setupLeadImageView() + } + + func setupPageContentServiceJavaScriptInterface(with completion: @escaping () -> Void) { + guard let siteURL = articleURL.wmf_site else { + DDLogError("Missing site for \(articleURL)") + showGenericError() + return + } + + // Need user groups to let the Page Content Service know if the page is editable for this user + authManager.getLoggedInUser(for: siteURL) { (result) in + assert(Thread.isMainThread) + switch result { + case .success(let user): + self.setupPageContentServiceJavaScriptInterface(with: user?.groups ?? []) + case .failure: + DDLogError("Error getting userinfo for \(siteURL)") + self.setupPageContentServiceJavaScriptInterface(with: []) + } + completion() + } + } + + func setupPageContentServiceJavaScriptInterface(with userGroups: [String]) { + let areTablesInitiallyExpanded = UserDefaults.standard.wmf_isAutomaticTableOpeningEnabled + + messagingController.shouldAttemptToShowArticleAsLivingDoc = articleAsLivingDocController.shouldAttemptToShowArticleAsLivingDoc + + messagingController.setup(with: webView, languageCode: articleLanguageCode, theme: theme, layoutMargins: articleMargins, leadImageHeight: leadImageHeight, areTablesInitiallyExpanded: areTablesInitiallyExpanded, userGroups: userGroups) + } + + func setupToolbar() { + enableToolbar() + toolbarController.apply(theme: theme) + toolbarController.setSavedState(isSaved: article.isAnyVariantSaved) + setToolbarHidden(false, animated: false) + } + + var isWidgetCachedFeaturedArticle: Bool { + let sharedCache = SharedContainerCache(fileName: SharedContainerCacheCommonNames.widgetCache, defaultCache: { WidgetCache(settings: .default, featuredContent: nil) }) + guard let widgetFeaturedArticleURLString = sharedCache.loadCache().featuredContent?.featuredArticle?.contentURL.desktop.page, + let widgetFeaturedArticleURL = URL(string: widgetFeaturedArticleURLString) else { + return false + } + + return widgetFeaturedArticleURL == articleURL + } + +} + +extension ArticleViewController { + func presentEmbedded(_ viewController: UIViewController, style: WMFThemeableNavigationControllerStyle) { + let nc = WMFThemeableNavigationController(rootViewController: viewController, theme: theme, style: style) + present(nc, animated: true) + } +} + +extension ArticleViewController: ReadingThemesControlsResponding { + func updateWebViewTextSize(textSize: Int) { + messagingController.updateTextSizeAdjustmentPercentage(textSize) + } + + func toggleSyntaxHighlighting(_ controller: ReadingThemesControlsViewController) { + // no-op here, syntax highlighting shouldnt be displayed + } +} + +extension ArticleViewController: ImageScaleTransitionProviding { + var imageScaleTransitionView: UIImageView? { + return leadImageView + } + + func prepareViewsForIncomingImageScaleTransition(with imageView: UIImageView?) { + guard let imageView = imageView, let image = imageView.image else { + return + } + + leadImageHeightConstraint.constant = leadImageHeight + leadImageView.image = image + leadImageView.layer.contentsRect = imageView.layer.contentsRect + + view.layoutIfNeeded() + } + +} + +// MARK: - Article Load Errors + +extension ArticleViewController { + func handleArticleLoadFailure(with error: Error, showEmptyView: Bool) { + fakeProgressController.finish() + if showEmptyView { + wmf_showEmptyView(of: .articleDidNotLoad, theme: theme, frame: view.bounds) + } + showError(error) + refreshControl.endRefreshing() + updateRefreshOverlay(visible: false) + } + + func articleLoadDidFail(with error: Error) { + // Convert from mobileview if necessary + guard article.isConversionFromMobileViewNeeded else { + handleArticleLoadFailure(with: error, showEmptyView: !article.isSaved) + return + } + dataStore.migrateMobileviewToMobileHTMLIfNecessary(article: article) { [weak self] (migrationError) in + DispatchQueue.main.async { + self?.oneOffArticleMigrationDidFinish(with: migrationError) + } + } + } + + func oneOffArticleMigrationDidFinish(with migrationError: Error?) { + if let error = migrationError { + handleArticleLoadFailure(with: error, showEmptyView: true) + return + } + guard !article.isConversionFromMobileViewNeeded else { + handleArticleLoadFailure(with: RequestError.unexpectedResponse, showEmptyView: true) + return + } + loadPage() + } +} + +extension ArticleViewController: WKNavigationDelegate { + + func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + switch navigationAction.navigationType { + case .reload: + fallthrough + case .other: + setupArticleLoadWaitGroup() + decisionHandler(.allow) + default: + decisionHandler(.cancel) + } + } + + func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) { + switch navigationAction.navigationType { + case .reload: + fallthrough + case .other: + setupArticleLoadWaitGroup() + decisionHandler(.allow, preferences) + default: + decisionHandler(.cancel, preferences) + } + } + + func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) { + defer { + decisionHandler(.allow) + } + guard let response = navigationResponse.response as? HTTPURLResponse else { + return + } + currentETag = response.allHeaderFields[HTTPURLResponse.etagHeaderKey] as? String + checkForScrollToAnchor(in: response) + } + + func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { + articleLoadDidFail(with: error) + } + + func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { + articleLoadDidFail(with: error) + } + + func webViewWebContentProcessDidTerminate(_ webView: WKWebView) { + // On process did terminate, the WKWebView goes blank + // Re-load the content in this case to show it again + webView.reload() + } + + func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { + if shouldPerformWebRefreshAfterScrollViewDeceleration { + updateRefreshOverlay(visible: false) + webView.scrollView.showsVerticalScrollIndicator = true + shouldPerformWebRefreshAfterScrollViewDeceleration = false + } + } +} + +extension ViewController { // Putting extension on ViewController rather than ArticleVC allows for re-use by EditPreviewVC + + var articleMargins: UIEdgeInsets { + return UIEdgeInsets(top: 8, left: articleHorizontalMargin, bottom: 0, right: articleHorizontalMargin) + } + + var articleHorizontalMargin: CGFloat { + let viewForCalculation: UIView = navigationController?.view ?? view + + if let tableOfContentsVC = (self as? ArticleViewController)?.tableOfContentsController.viewController, tableOfContentsVC.isVisible { + // full width + return viewForCalculation.layoutMargins.left + } else { + // If (is EditPreviewVC) or (is TOC OffScreen) then use readableContentGuide to make text inset from screen edges. + // Since readableContentGuide has no effect on compact width, both paths of this `if` statement result in an identical result for smaller screens. + return viewForCalculation.readableContentGuide.layoutFrame.minX + } + } +} + +// MARK: Article As Living Doc Protocols + +extension ArticleViewController: ArticleAsLivingDocViewControllerDelegate { + func livingDocViewWillPush() { + surveyTimerController?.livingDocViewWillPush(withState: state) + } + + func livingDocViewWillAppear() { + surveyTimerController?.livingDocViewWillAppear(withState: state) + } + + var articleAsLivingDocViewModel: ArticleAsLivingDocViewModel? { + return articleAsLivingDocController.articleAsLivingDocViewModel + } + + func fetchNextPage(nextRvStartId: UInt, theme: Theme) { + articleAsLivingDocController.fetchNextPage(nextRvStartId: nextRvStartId, traitCollection: traitCollection, theme: theme) + } + + var isFetchingAdditionalPages: Bool { + return articleAsLivingDocController.isFetchingAdditionalPages + } +} + +extension ArticleViewController: ArticleAsLivingDocControllerDelegate { + var abTestsController: ABTestsController { + return dataStore.abTestsController + } + + var isInValidSurveyCampaignAndArticleList: Bool { + surveyAnnouncementResult != nil + } + + func extendTimerForPresentingModal() { + surveyTimerController?.extendTimer() + } +} + +extension ArticleViewController: ArticleSurveyTimerControllerDelegate { + var displayDelay: TimeInterval? { + surveyAnnouncementResult?.displayDelay + } + + var shouldAttemptToShowArticleAsLivingDoc: Bool { + return articleAsLivingDocController.shouldAttemptToShowArticleAsLivingDoc + } + + var userHasSeenSurveyPrompt: Bool { + + guard let identifier = surveyAnnouncementResult?.campaignIdentifier else { + return false + } + + return SurveyAnnouncementsController.shared.userHasSeenSurveyPrompt(forCampaignIdentifier: identifier) + } + + var shouldShowArticleAsLivingDoc: Bool { + return articleAsLivingDocController.shouldShowArticleAsLivingDoc + } + + var livingDocSurveyLinkState: ArticleAsLivingDocSurveyLinkState { + return articleAsLivingDocController.surveyLinkState + } + + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ArticleWebMessagingController.swift b/Apps/Wikipedia/Wikipedia/Code/ArticleWebMessagingController.swift new file mode 100644 index 0000000..4b6930b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ArticleWebMessagingController.swift @@ -0,0 +1,715 @@ +import Foundation +import CocoaLumberjackSwift + +protocol ArticleWebMessageHandling: AnyObject { + func didRecieve(action: ArticleWebMessagingController.Action) +} + +class ArticleWebMessagingController: NSObject { + + fileprivate weak var webView: WKWebView? + weak var delegate: ArticleWebMessageHandling? + + private let bodyActionKey = "action" + private let bodyDataKey = "data" + + var parameters: PageContentService.Setup.Parameters? + var contentController: WKUserContentController? + var shouldAttemptToShowArticleAsLivingDoc = false + + func setup(with webView: WKWebView, languageCode: String, theme: Theme, layoutMargins: UIEdgeInsets, leadImageHeight: CGFloat = 0, areTablesInitiallyExpanded: Bool = false, textSizeAdjustment: Int? = nil, userGroups: [String] = []) { + let margins = getPageContentServiceMargins(from: layoutMargins) + let textSizeAdjustment = textSizeAdjustment ?? UserDefaults.standard.wmf_articleFontSizeMultiplier() as? Int ?? 100 + let parameters = PageContentService.Setup.Parameters(theme: theme.webName.lowercased(), dimImages: theme.imageOpacity < 1, margins: margins, leadImageHeight: "\(leadImageHeight)px", areTablesInitiallyExpanded: areTablesInitiallyExpanded, textSizeAdjustmentPercentage: "\(textSizeAdjustment)%", userGroups: userGroups) + self.parameters = parameters + self.webView = webView + let contentController = webView.configuration.userContentController + contentController.add(self, name: PageContentService.messageHandlerName) + self.contentController = contentController + do { + try updateUserScripts(on: contentController, with: parameters) + } catch let error { + WMFAlertManager.sharedInstance.showErrorAlert(error as NSError, sticky: true, dismissPreviousAlerts: false) + } + } + + func removeScriptMessageHandler() { + webView?.configuration.userContentController.removeScriptMessageHandler(forName: PageContentService.messageHandlerName) + } + + /// Update the scripts that run on page load. Utilize this when any parameters change. + func updateUserScripts(on contentController: WKUserContentController, with parameters: PageContentService.Setup.Parameters) throws { + let pcsSetup = try PageContentService.SetupScript(parameters) + // Unfortunately we can only remove all and re-add all + // it doesn't appear there's any way to just replace the script we need to update + contentController.removeAllUserScripts() + contentController.addUserScript(pcsSetup) + let propertiesScript = PageContentService.PropertiesScript() + contentController.addUserScript(propertiesScript) + let utilitiesScript = PageContentService.UtilitiesScript() + contentController.addUserScript(utilitiesScript) + + if shouldAttemptToShowArticleAsLivingDoc { + let significantEventsStyleScript = PageContentService.SignificantEventsStyleScript(theme: parameters.theme) + contentController.addUserScript(significantEventsStyleScript) + } else { + let styleScript = PageContentService.StyleScript() + contentController.addUserScript(styleScript) + } + } + + func addFooter(articleURL: URL, restAPIBaseURL: URL, menuItems: [PageContentService.Footer.Menu.Item], lastModified: Date?) { + guard let title = articleURL.percentEncodedPageTitleForPathComponents else { + return + } + var editedDaysAgo: Int? + if let lastModified = lastModified { + editedDaysAgo = NSCalendar.wmf_gregorian().wmf_days(from: lastModified, to: Date()) + } + let menu = PageContentService.Footer.Menu(items: menuItems, editedDaysAgo: editedDaysAgo) + let readMore = PageContentService.Footer.ReadMore(itemCount: 3, baseURL: restAPIBaseURL.absoluteString) + let parameters = PageContentService.Footer.Parameters(title: title, menu: menu, readMore: readMore) + guard let parametersJS = try? PageContentService.getJavascriptFor(parameters) else { + return + } + webView?.evaluateJavaScript("pcs.c1.Footer.add(\(parametersJS))", completionHandler: { (result, error) in + if let error = error { + DDLogError("Error adding footer: \(error)") + } + }) + } + + // MARK: - Adjustable state + + // MARK: PCS + + /// Update the pageSetupSettings so that they are correct on reload + /// Without updating these after changing settings like theme, text size, etc, the page will revert to the original settings on reload + func updateSetupParameters() { + guard let parameters = parameters, let contentController = contentController else { + return + } + try? updateUserScripts(on: contentController, with: parameters) + } + + func updateTheme(_ theme: Theme) { + let webTheme = theme.webName.lowercased() + let js = "pcs.c1.Page.setTheme(pcs.c1.Themes.\(theme.webName.uppercased()))" + webView?.evaluateJavaScript(js) + parameters?.theme = webTheme + updateSetupParameters() + + if shouldAttemptToShowArticleAsLivingDoc { + let significantEventsJS = PageContentService.SignificantEventsStyleScript.sourceForTheme(webTheme) + webView?.evaluateJavaScript(significantEventsJS) + } + } + + func getPageContentServiceMargins(from insets: UIEdgeInsets, leadImageHeight: CGFloat = 0) -> PageContentService.Setup.Parameters.Margins { + return PageContentService.Setup.Parameters.Margins(top: "\(insets.top + leadImageHeight)px", right: "\(insets.right)px", bottom: "\(insets.bottom)px", left: "\(insets.left)px") + } + + func updateMargins(with layoutMargins: UIEdgeInsets, leadImageHeight: CGFloat) { + let margins = getPageContentServiceMargins(from: layoutMargins, leadImageHeight: leadImageHeight) + guard let marginsJSON = try? PageContentService.getJavascriptFor(margins) else { + return + } + webView?.evaluateJavaScript("pcs.c1.Page.setMargins(\(marginsJSON))") + parameters?.margins = margins + updateSetupParameters() + } + + func updateTextSizeAdjustmentPercentage(_ percentage: Int) { + let percentage = "\(percentage)%" + let js = "pcs.c1.Page.setTextSizeAdjustmentPercentage('\(percentage)')" + webView?.evaluateJavaScript(js) + parameters?.textSizeAdjustmentPercentage = percentage + updateSetupParameters() + } + + func prepareForScroll(to anchor: String, highlight: Bool, completion: @escaping (Result) -> Void) { + guard let webView = webView else { + completion(.failure(RequestError.invalidParameters)) + return + } + webView.evaluateJavaScript("pcs.c1.Page.prepareForScrollToAnchor(`\(anchor.sanitizedForJavaScriptTemplateLiterals)`, {highlight: \(highlight ? "true" : "false")})") { (result, error) in + if let error = error { + DDLogError("Error attempting to scroll to anchor: \(anchor) \(error)") + completion(.failure(error)) + } else { + completion(.success(())) + } + } + } + + func removeElementHighlights() { + webView?.evaluateJavaScript("pcs.c1.Page.removeHighlightsFromHighlightedElements()") + } + + // MARK: iOS App Specific overrides (code in www/, built products in assets/) + + func removeSearchTermHighlights() { + let js = "window.wmf.findInPage.removeSearchTermHighlights()" + webView?.evaluateJavaScript(js) + } +} + +struct ReferenceBackLink { + let id: String + init?(scriptMessageDict: [String: Any]) { + guard + let id = scriptMessageDict["id"] as? String + else { + return nil + } + self.id = id + } +} + +extension ArticleWebMessagingController: WKScriptMessageHandler { + /// Actions represent events from the web page + enum Action { + case setup + case finalSetup + case image(src: String, href: String, width: Int?, height: Int?) + case link(href: String, text: String?, title: String?) + case reference(selectedIndex: Int, group: [WMFLegacyReference]) + case backLink(referenceId: String, referenceText: String, backLinks: [ReferenceBackLink]) + case edit(sectionID: Int, descriptionSource: ArticleDescriptionSource?) + case addTitleDescription + case footerItem(type: PageContentService.Footer.Menu.Item, payload: Any?) + case viewInBrowser + case leadImage(source: String?, width: Int?, height: Int?) + case tableOfContents(items: [TableOfContentsItem]) + case scrollToAnchor(anchor: String, rect: CGRect) + case aaaldInsertOnScreen + case unknown(href: String) + } + + // PCSActions are receieved from the JS bridge and converted into actions + // Handle both _clicked and non-clicked variants in case the names change + private enum PCSAction: String { + case setup + case finalSetup = "final_setup" + case image + case link + case reference + case backLink = "back_link" + case pronunciation + case edit = "edit_section" + case addTitleDescription = "add_title_description" + case footerItem = "footer_item" + case viewInBrowser = "view_in_browser" + case leadImage + case tableOfContents + case scrollToAnchor = "scroll_to_anchor" + case aaaldInsertOnScreen + init?(pcsActionString: String) { + let cleaned = pcsActionString.replacingOccurrences(of: "_clicked", with: "") + self.init(rawValue: cleaned) + } + func getAction(with data: [String: Any]?) -> Action? { + switch self { + case .setup: + return .setup + case .finalSetup: + return .finalSetup + case .image: + return getImageAction(with: data) + case .reference: + return getReferenceAction(with: data) + case .backLink: + return getBackLinkAction(with: data) + case .pronunciation: + return getPronunciationAction(with: data) + case .edit: + return getEditAction(with: data) + case .addTitleDescription: + return .addTitleDescription + case .footerItem: + return getFooterItemAction(with: data) + case .viewInBrowser: + return .viewInBrowser + case .link: + return getLinkAction(with: data) + case .leadImage: + return getLeadImageAction(with: data) + case .tableOfContents: + return getTableOfContentsAction(with: data) + case .scrollToAnchor: + return getScrollToAnchorAction(with: data) + case .aaaldInsertOnScreen: + return .aaaldInsertOnScreen + } + } + func getLeadImageAction(with data: [String: Any]?) -> Action? { + // Send back a lead image event even if it's empty - we need to handle this case + let leadImage = data?["leadImage"] as? [String: Any] + let source = leadImage?["source"] as? String + let width = leadImage?["width"] as? Int + let height = leadImage?["height"] as? Int + return Action.leadImage(source: source, width: width, height: height) + } + + func getTableOfContentsAction(with data: [String: Any]?) -> Action? { + guard let tableOfContents = data?["tableOfContents"] as? [[String: Any]] else { + return nil + } + var currentRootSectionId = -1 + let items = tableOfContents.compactMap { (tocJSON) -> TableOfContentsItem? in + guard + let id = tocJSON["id"] as? Int, + let level = tocJSON["level"] as? Int, + let anchor = tocJSON["anchor"] as? String, + let title = tocJSON["title"] as? String + else { + return nil + } + let indentationLevel = level - 1 + if indentationLevel == 0 { + currentRootSectionId = id + } + return TableOfContentsItem(id: id, titleHTML: title, anchor: anchor, rootItemId: currentRootSectionId, indentationLevel: indentationLevel) + } + return Action.tableOfContents(items: items) + } + + func getLinkAction(with data: [String: Any]?) -> Action? { + guard let href = data?["href"] as? String else { + return nil + } + let title = data?["title"] as? String + let text = data?["text"] as? String + return .link(href: href, text: text, title: title) + } + + func getEditAction(with data: [String: Any]?) -> Action? { + guard let sectionIDString = data?["sectionId"] as? String, let sectionID = Int(sectionIDString) else { + return nil + } + + let sourceString = data?["descriptionSource"] as? String + let source = ArticleDescriptionSource.from(string: sourceString) + return .edit(sectionID: sectionID, descriptionSource: source) + } + + func getImageAction(with data: [String: Any]?) -> Action? { + guard let src = data?["src"] as? String, let href = data?["href"] as? String else { + return nil + } + let width = data?["data-file-width"] as? Int + let height = data?["data-file-height"] as? Int + return .image(src: src, href: href, width: width, height: height) + } + + func getReferenceAction(with data: [String: Any]?) -> Action? { + guard let selectedIndex = data?["selectedIndex"] as? Int, let groupArray = data?["referencesGroup"] as? [[String: Any]] else { + return nil + } + let group = groupArray.compactMap { WMFLegacyReference(scriptMessageDict: $0) } + return .reference(selectedIndex: selectedIndex, group: group) + } + + func getBackLinkAction(with data: [String: Any]?) -> Action? { + guard + let referenceId = data?["referenceId"] as? String, + let referenceText = data?["referenceText"] as? String, + let backLinkDictionaries = data?["backLinks"] as? [[String: Any]] + else { + return nil + } + let backLinks = backLinkDictionaries.compactMap { ReferenceBackLink(scriptMessageDict: $0) } + return .backLink(referenceId: referenceId, referenceText: referenceText, backLinks: backLinks) + } + + func getPronunciationAction(with data: [String: Any]?) -> Action? { + guard let urlString = data?["url"] as? String else { + return nil + } + return .link(href: urlString, text: nil, title: nil) + } + + func getFooterItemAction(with data: [String: Any]?) -> Action? { + guard let itemTypeString = data?["itemType"] as? String, let menuItemType = PageContentService.Footer.Menu.Item(rawValue: itemTypeString) else { + return nil + } + return .footerItem(type: menuItemType, payload: data?["payload"]) + } + + func getScrollToAnchorAction(with data: [String: Any]?) -> Action? { + guard + let dictionary = data?["rect"] as? [String: Any], + let anchor = data?["anchor"] as? String, + let x = dictionary["x"] as? CGFloat, + let y = dictionary["y"] as? CGFloat, + let width = dictionary["width"] as? CGFloat, + let height = dictionary["height"] as? CGFloat, + width > 0, + height > 0 + else { + return nil + } + let rect = CGRect(x: x, y: y, width: width, height: height) + return .scrollToAnchor(anchor: anchor, rect: rect) + } + } + + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + guard let body = message.body as? [String: Any] else { + return + } + guard let actionString = body[bodyActionKey] as? String else { + return + } + let data = body[bodyDataKey] as? [String: Any] + guard let action = PCSAction(pcsActionString: actionString)?.getAction(with: data) else { + // Fallback on href for future unknown event types + if let href = data?["href"] as? String { + let action = ArticleWebMessagingController.Action.unknown(href: href) + delegate?.didRecieve(action: action) + } + return + } + delegate?.didRecieve(action: action) + } +} + +// Article as a Living Document Scripts + +extension ArticleWebMessagingController { + + enum TopBadgeType { + case lastUpdated + case newChanges + } + + var articleAsLivingDocBoxSkeletonContainerID: String { + return "significant-changes-skeleton" + } + var badgeHTMLSkeletonContainerID: String { + return "significant-changes-top-skeleton" + } + var articleAsLivingDocBoxContainerID: String { + return "significant-changes-container" + } + var badgeHTMLContainerID: String { + return "significant-changes-top-container" + } + var articleAsLivingDocBoxContainerHTMLStart: String { + return "
    " + } + var articleAsLivingDocBoxInnerContainerID: String { + return "significant-changes-inner-container" + } + var articleAsLivingDocBoxInnerContainerHTMLStart: String { + return "
    " + } + var articleAsLivingDocBoxInnerContainerHTMLEnd: String { + return "
    " + } + var articleAsLivingDocBoxContainerHTMLEnd: String { + return "
    " + } + var badgeHTMLContainerStart: String { + return "
    " + } + var badgeHTMLContainerEnd: String { + return "
    " + } + func createAndInsertArticleContainerScript(innerHTML: String) -> String { + return """ + //then create and insert new element + var pcs = document.getElementById('pcs'); + if (!pcs) { + return false; + } + var sections = pcs.getElementsByTagName('section'); + if (sections.length === 0) { + return false; + } + var firstSection = sections[0]; + var pTags = firstSection.getElementsByTagName('p'); + if (pTags.length === 0) { + return false; + } + var firstParagraph = pTags[0]; + firstParagraph.insertAdjacentHTML("afterend","\(articleAsLivingDocBoxContainerHTMLStart + innerHTML + articleAsLivingDocBoxContainerHTMLEnd)"); + return true; + """ + } + + func createAndInsertBadgeScript(innerHTML: String) -> String { + return """ + //then create and insert new element + var pcs = document.getElementById('pcs'); + if (!pcs) { + return false; + } + var headers = pcs.getElementsByTagName('header'); + if (headers.length === 0) { + return false; + } + var firstHeader = headers[0]; + firstHeader.insertAdjacentHTML("afterbegin","\(badgeHTMLContainerStart + innerHTML + badgeHTMLContainerEnd)"); + return true; + """ + } + + func removeArticleAsLivingDocContent(completion: ((Bool) -> Void)? = nil) { + let javascript = """ + function removeSignificantEventsContent() { + var boxSkeleton = document.getElementById('\(articleAsLivingDocBoxContainerID)'); + var missingSkeletons = false; + if (boxSkeleton) { + boxSkeleton.remove(); + } else { + missingSkeletons = true; + } + + var topSkeleton = document.getElementById('\(badgeHTMLContainerID)'); + if (topSkeleton) { + topSkeleton.remove(); + } else { + missingSkeletons = true; + } + + if (missingSkeletons == true) { + return false; + } else { + return true; + } + } + + removeSignificantEventsContent(); + """ + + webView?.evaluateJavaScript(javascript) { (result, error) in + DispatchQueue.main.async { + if let error = error { + DDLogDebug("Failure in removeSkeletonArticleAsLivingDocContent: \(error)") + completion?(false) + return + } + + if let boolResult = result as? Bool { + if !boolResult { + DDLogDebug("Failure in removeSkeletonArticleAsLivingDocContent") + } + completion?(boolResult) + return + } + + DDLogDebug("Failure in removeSkeletonArticleAsLivingDocContent") + completion?(false) + } + } + } + + func injectSkeletonArticleAsLivingDocContent(completion: ((Bool) -> Void)? = nil) { + + let innerBoxHTML = "
    " + let innerBadgeHTML = "
    " + + let javascript = """ + function injectSignificantEventsContent() { + \(createAndInsertArticleContainerScript(innerHTML: innerBoxHTML)) + } + injectSignificantEventsContent(); + + function injectNewChangesBadge() { + \(createAndInsertBadgeScript(innerHTML: innerBadgeHTML)) + } + injectNewChangesBadge(); + """ + + webView?.evaluateJavaScript(javascript) { (result, error) in + DispatchQueue.main.async { + if let error = error { + DDLogDebug("Failure in injectSkeletonArticleAsLivingDocContent: \(error)") + completion?(false) + return + } + + if let boolResult = result as? Bool { + if !boolResult { + DDLogDebug("Failure in injectSkeletonArticleAsLivingDocContent") + } + completion?(boolResult) + return + } + + DDLogDebug("Failure in injectSkeletonArticleAsLivingDocContent") + completion?(false) + } + } + } + + func aaaldContentInsertJS(articleInsertHtmlSnippets: [String]) -> String { + let insertHeaderText = WMFLocalizedString("aaald-article-insert-header", value: "Significant Updates", comment: "Header text in article content insert section that displays recent significant article updates.") + + var articleAsLivingDocBoxInnerHTML = "\(articleAsLivingDocBoxInnerContainerHTMLStart)

    \(insertHeaderText.uppercased(with: Locale.current))

      " + + for articleInsertHtmlSnippet in articleInsertHtmlSnippets { + articleAsLivingDocBoxInnerHTML += articleInsertHtmlSnippet + } + + let readMoreUpdatesText = WMFLocalizedString("aaald-article-insert-read-more", value: "Read more updates", comment: "Footer text in article content insert section of recent significant article updates that invites user to read more updates on another screen.") + + articleAsLivingDocBoxInnerHTML += "

    \(readMoreUpdatesText)

    \(articleAsLivingDocBoxInnerContainerHTMLEnd)" + + let js = """ + function injectSignificantEventsContent() { + //first remove existing element if it's there + var existing = document.getElementById('\(articleAsLivingDocBoxContainerID)'); + if (existing) { + existing.innerHTML = "\(articleAsLivingDocBoxInnerHTML)"; + return true; + } + \(createAndInsertArticleContainerScript(innerHTML: articleAsLivingDocBoxInnerHTML)) + } + injectSignificantEventsContent(); + """ + + return js + } + + func aaaldScrollViewDetectionJS() -> String { + return """ + + function isInViewport(el) { + const rect = el.getBoundingClientRect(); + return ( + (rect.top >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight)) || + (rect.bottom >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight)) + ); + } + + var aaaldInsertElementForScrollDetection = document.getElementById('\(articleAsLivingDocBoxInnerContainerID)'); + var detectedIsOnScreen = false; + + function detectIfInsertIsOnScreen() { + if (!aaaldInsertElementForScrollDetection) { + aaaldInsertElementForScrollDetection = document.getElementById('\(articleAsLivingDocBoxInnerContainerID)'); + } + if (aaaldInsertElementForScrollDetection) { + const isOnScreen = isInViewport(aaaldInsertElementForScrollDetection); + if (detectedIsOnScreen === false && isOnScreen === true) { + window.webkit.messageHandlers.\(PageContentService.messageHandlerName).postMessage({"\(bodyActionKey)": "\(PCSAction.aaaldInsertOnScreen.rawValue)"}); + detectedIsOnScreen = true; + } + } + } + + document.addEventListener('scroll', function () { + detectIfInsertIsOnScreen(); + }, { + passive: true + }); + + detectIfInsertIsOnScreen(); + """ + } + + func aaaldTopBadgeJS(timestamp: String?, topBadgeType: TopBadgeType) -> String { + let newChangesText = WMFLocalizedString("aaald-article-insert-new-changes", value: "New changes", comment: "Badge text in article content showing that there have been new significant updates to the article since the user last viewed it.") + let lastUpdatedText = WMFLocalizedString("aaald-article-insert-last-updated", value: "Last updated", comment: "Badge text in article content showing when the article as last updated.") + + guard let timestamp = timestamp else { + return "" + } + + let innerContainerID = topBadgeType == .lastUpdated ? "significant-changes-top-inner-container-last-updated" : "significant-changes-top-inner-container-new-changes" + let topTextID = topBadgeType == .lastUpdated ? "significant-changes-top-text-last-updated" : "significant-changes-top-text-new-changes" + let badgeText = topBadgeType == .lastUpdated ? lastUpdatedText : newChangesText + var badgeInnerHTML = "
    " + if topBadgeType == .newChanges { + badgeInnerHTML += "" + } + badgeInnerHTML += "\(badgeText)
    " + badgeInnerHTML += "\(timestamp)" + + return """ + function injectNewChangesBadge() { + //first remove existing element if it's there + var existing = document.getElementById('\(badgeHTMLContainerID)'); + if (existing) { + existing.innerHTML = "\(badgeInnerHTML)"; + return true; + } + \(createAndInsertBadgeScript(innerHTML: badgeInnerHTML)) + } + + injectNewChangesBadge(); + """ + } + + func injectArticleAsLivingDocContent(articleInsertHtmlSnippets: [String], topBadgeType: TopBadgeType = .lastUpdated, timestamp: String? = nil, _ completion: ((Bool) -> Void)? = nil) { + + guard articleInsertHtmlSnippets.count > 0 else { + completion?(false) + return + } + + let contentInsertJS = aaaldContentInsertJS(articleInsertHtmlSnippets: articleInsertHtmlSnippets) + let scrollViewDetectionJS = aaaldScrollViewDetectionJS() + let topBadgeJS = aaaldTopBadgeJS(timestamp: timestamp, topBadgeType: topBadgeType) + + let finalInjectJS = contentInsertJS + scrollViewDetectionJS + topBadgeJS + + webView?.evaluateJavaScript(finalInjectJS) { (result, error) in + DispatchQueue.main.async { + if let error = error { + DDLogDebug("Failure in injectArticleAsLivingDocContent: \(error)") + completion?(false) + return + } + + if let boolResult = result as? Bool { + if !boolResult { + DDLogDebug("Failure in injectArticleAsLivingDocContent") + } + completion?(boolResult) + return + } + + DDLogDebug("Failure in injectArticleAsLivingDocContent") + completion?(false) + } + } + } + + // should be used only when significant events is active + // we are manually suppressing left and right body margins in standard view + // and adding back in as padding so we get the edge to edge gray background + func customUpdateMargins(with layoutMargins: UIEdgeInsets, leadImageHeight: CGFloat) { + let javascript = """ + function customUpdateMargins() { + document.body.style.marginLeft = "0px"; + document.body.style.marginRight = "0px"; + document.body.style.paddingLeft = "\(layoutMargins.left)px"; + document.body.style.paddingRight = "\(layoutMargins.right)px"; + document.body.style.marginTop = "\(leadImageHeight + layoutMargins.top)px"; + var seContainer = document.getElementById('\(articleAsLivingDocBoxContainerID)'); + if (seContainer) { + seContainer.style.marginLeft = "-\(layoutMargins.left)px"; + seContainer.style.marginRight = "-\(layoutMargins.right)px"; + seContainer.style.paddingLeft = "\(layoutMargins.left)px"; + seContainer.style.paddingRight = "\(layoutMargins.right)px"; + } + + return true; + } + customUpdateMargins(); + """ + webView?.evaluateJavaScript(javascript) { (success, error) in + if let error = error { + DDLogDebug("Failure in customUpdateMargins: \(error)") + } + + if let success = success as? Bool, + success == false { + DDLogDebug("Failure in customUpdateMargins") + } + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/AutoLayoutSafeMultiLineButton.swift b/Apps/Wikipedia/Wikipedia/Code/AutoLayoutSafeMultiLineButton.swift new file mode 100644 index 0000000..b5bace7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/AutoLayoutSafeMultiLineButton.swift @@ -0,0 +1,12 @@ +@IBDesignable +class AutoLayoutSafeMultiLineButton: SetupButton { + override func setup() { + super.setup() + titleLabel?.numberOfLines = 0 + assert(wmf_hasRequiredNonZeroHeightConstraint() == false, "Multiline button should not have any UILayoutPriority.required height constraints. Remove the height constraint or lower its priority.") + } + + override var intrinsicContentSize: CGSize { + return wmf_sizeThatFits(CGSize(width: bounds.size.width, height: UIView.noIntrinsicMetric)) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/BackgroundHighlightingButtonStyle.swift b/Apps/Wikipedia/Wikipedia/Code/BackgroundHighlightingButtonStyle.swift new file mode 100644 index 0000000..0f25a29 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/BackgroundHighlightingButtonStyle.swift @@ -0,0 +1,12 @@ +import Foundation +import SwiftUI + +struct BackgroundHighlightingButtonStyle: ButtonStyle { + + @EnvironmentObject var observableTheme: ObservableTheme + + func makeBody(configuration: SwiftUI.ButtonStyle.Configuration) -> some View { + configuration.label + .background(configuration.isPressed ? Color(observableTheme.theme.colors.midBackground) : Color(observableTheme.theme.colors.paperBackground)) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/BarButtonImageStyle.swift b/Apps/Wikipedia/Wikipedia/Code/BarButtonImageStyle.swift new file mode 100644 index 0000000..50649ab --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/BarButtonImageStyle.swift @@ -0,0 +1,28 @@ +import UIKit + +@objc class BarButtonImageStyle: NSObject { + + @objc(notificationsButtonImageForTheme:indicated:) + static func notificationsButtonImage(theme: Theme, indicated: Bool = false) -> UIImage? { + switch theme { + case .dark, .black: + return UIImage(named: indicated ? "notifications-bell-dark-black-indicated" : "notifications-bell-dark-black") + case .sepia: + return UIImage(named: indicated ? "notifications-bell-sepia-indicated" : "notifications-bell-sepia") + default: + return UIImage(named: indicated ? "notifications-bell-light-indicated" : "notifications-bell-light") + } + } + + @objc(settingsButtonImageForTheme:) + static func settingsButtonImage(theme: Theme) -> UIImage? { + switch theme { + case .dark, .black: + return UIImage(named: "settings-gear-dark-black") + default: + return UIImage(named: "settings-gear-light-sepia") + } + } + +} + diff --git a/Apps/Wikipedia/Wikipedia/Code/BaseExploreFeedSettingsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/BaseExploreFeedSettingsViewController.swift new file mode 100644 index 0000000..736863f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/BaseExploreFeedSettingsViewController.swift @@ -0,0 +1,350 @@ +protocol ExploreFeedSettingsItem { + var title: String { get } + var subtitle: String? { get } + var disclosureType: WMFSettingsMenuItemDisclosureType { get } + var disclosureText: String? { get } + var iconName: String? { get } + var iconColor: UIColor? { get } + var iconBackgroundColor: UIColor? { get } + var controlTag: Int { get } + var isOn: Bool { get } + func updateSubtitle(for displayType: ExploreFeedSettingsDisplayType) + func updateDisclosureText(for displayType: ExploreFeedSettingsDisplayType) + func updateIsOn(for displayType: ExploreFeedSettingsDisplayType) +} + +extension ExploreFeedSettingsItem { + var subtitle: String? { return nil } + var disclosureType: WMFSettingsMenuItemDisclosureType { return .switch } + var disclosureText: String? { return nil } + var iconName: String? { return nil } + var iconColor: UIColor? { return nil } + var iconBackgroundColor: UIColor? { return nil } + func updateSubtitle(for displayType: ExploreFeedSettingsDisplayType) { + + } + func updateDisclosureText(for displayType: ExploreFeedSettingsDisplayType) { + + } + func updateIsOn(for displayType: ExploreFeedSettingsDisplayType) { + + } +} + +enum ExploreFeedSettingsMainType: Equatable { + case entireFeed + case singleFeedCard(WMFContentGroupKind) +} + +private extension WMFContentGroupKind { + var switchTitle: String { + switch self { + case .news: + return WMFLocalizedString("explore-feed-preferences-show-news-title", value: "Show In the news card", comment: "Text for the setting that allows users to toggle the visibility of the In the news card") + case .featuredArticle: + return WMFLocalizedString("explore-feed-preferences-show-featured-article-title", value: "Show Featured article card", comment: "Text for the setting that allows users to toggle the visibility of the Featured article card") + case .topRead: + return WMFLocalizedString("explore-feed-preferences-show-top-read-title", value: "Show Top read card", comment: "Text for the setting that allows users to toggle the visibility of the Top read card") + case .onThisDay: + return WMFLocalizedString("explore-feed-preferences-show-on-this-day-title", value: "Show On this day card", comment: "Text for the setting that allows users to toggle the visibility of the On this day card") + case .pictureOfTheDay: + return WMFLocalizedString("explore-feed-preferences-show-picture-of-the-day-title", value: "Show Picture of the day card", comment: "Text for the setting that allows users to toggle the visibility of the Picture of the day card") + case .locationPlaceholder: + fallthrough + case .location: + return WMFLocalizedString("explore-feed-preferences-show-places-title", value: "Show Places card", comment: "Text for the setting that allows users to toggle the visibility of the Places card") + case .random: + return WMFLocalizedString("explore-feed-preferences-show-randomizer-title", value: "Show Randomizer card", comment: "Text for the setting that allows users to toggle the visibility of the Randomizer card") + case .continueReading: + return WMFLocalizedString("explore-feed-preferences-show-continue-reading-title", value: "Show Continue reading card", comment: "Text for the setting that allows users to toggle the visibility of the Continue reading card") + case .relatedPages: + return WMFLocalizedString("explore-feed-preferences-show-related-pages-title", value: "Show Because you read card", comment: "Text for the setting that allows users to toggle the visibility of the Because you read card") + default: + assertionFailure("\(self) is not customizable") + return "" + } + } +} + +class ExploreFeedSettingsPrimary: ExploreFeedSettingsItem { + let title: String + let controlTag: Int = -1 + var isOn: Bool = false + let type: ExploreFeedSettingsMainType + + init(for type: ExploreFeedSettingsMainType) { + self.type = type + if case let .singleFeedCard(contentGroupKind) = type { + title = contentGroupKind.switchTitle + isOn = contentGroupKind.isInFeed + } else { + title = WMFLocalizedString("explore-feed-preferences-explore-tab", value: "Explore tab", comment: "Text for the setting that allows users to toggle whether the Explore tab is enabled or not") + isOn = UserDefaults.standard.defaultTabType == .explore + } + } + + func updateIsOn(for displayType: ExploreFeedSettingsDisplayType) { + if case let .singleFeedCard(contentGroupKind) = type { + isOn = contentGroupKind.isInFeed + } else { + isOn = UserDefaults.standard.defaultTabType == .explore + } + } +} + +struct ExploreFeedSettingsSection { + let headerTitle: String? + let footerTitle: String + let items: [ExploreFeedSettingsItem] +} + +class ExploreFeedSettingsLanguage: ExploreFeedSettingsItem { + let title: String + let subtitle: String? + let controlTag: Int + var isOn: Bool = false + let siteURL: URL + let languageLink: MWKLanguageLink + + init(_ languageLink: MWKLanguageLink, controlTag: Int, displayType: ExploreFeedSettingsDisplayType) { + self.languageLink = languageLink + title = languageLink.localizedName + subtitle = languageLink.contentLanguageCode.uppercased() + self.controlTag = controlTag + siteURL = languageLink.siteURL + updateIsOn(for: displayType) + } + + func updateIsOn(for displayType: ExploreFeedSettingsDisplayType) { + switch displayType { + case .singleLanguage: + return + case .multipleLanguages: + isOn = languageLink.isInFeed + case .detail(let contentGroupKind): + isOn = languageLink.isInFeed(for: contentGroupKind) + } + } +} + +class ExploreFeedSettingsGlobalCards: ExploreFeedSettingsItem { + let disclosureType: WMFSettingsMenuItemDisclosureType = .switch + let title: String = WMFLocalizedString("explore-feed-preferences-global-cards-title", value: "Global cards", comment: "Title for the setting that allows users to toggle non-language specific feed cards") + let subtitle: String? = WMFLocalizedString("explore-feed-preferences-global-cards-description", value: "Non-language specific cards", comment: "Description of global feed cards") + let controlTag: Int = -2 + var isOn: Bool = MWKDataStore.shared().feedContentController.areGlobalContentGroupKindsInFeed + + func updateIsOn(for displayType: ExploreFeedSettingsDisplayType) { + guard displayType == .singleLanguage || displayType == .multipleLanguages else { + return + } + isOn = MWKDataStore.shared().feedContentController.areGlobalContentGroupKindsInFeed + } +} + +enum ExploreFeedSettingsDisplayType: Equatable { + case singleLanguage + case multipleLanguages + case detail(WMFContentGroupKind) +} + +class BaseExploreFeedSettingsViewController: SubSettingsViewController { + @objc var dataStore: MWKDataStore? + + open var displayType: ExploreFeedSettingsDisplayType = .singleLanguage + var activeSwitch: UISwitch? + + var updateFeedBeforeViewDisappears: Bool = false + + override func viewDidLoad() { + super.viewDidLoad() + tableView.register(WMFSettingsTableViewCell.wmf_classNib(), forCellReuseIdentifier: WMFSettingsTableViewCell.identifier) + tableView.register(WMFTableHeaderFooterLabelView.wmf_classNib(), forHeaderFooterViewReuseIdentifier: WMFTableHeaderFooterLabelView.identifier) + tableView.sectionHeaderHeight = UITableView.automaticDimension + tableView.estimatedSectionHeaderHeight = 44 + tableView.sectionFooterHeight = UITableView.automaticDimension + tableView.estimatedSectionFooterHeight = 44 + NotificationCenter.default.addObserver(self, selector: #selector(exploreFeedPreferencesDidSave(_:)), name: NSNotification.Name.WMFExploreFeedPreferencesDidSave, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(newExploreFeedPreferencesWereRejected(_:)), name: NSNotification.Name.WMFNewExploreFeedPreferencesWereRejected, object: nil) + } + + var preferredLanguages: [MWKLanguageLink] { + return MWKDataStore.shared().languageLinkController.preferredLanguages + } + + lazy var languages: [ExploreFeedSettingsLanguage] = { + let languages = preferredLanguages.enumerated().compactMap { (index, languageLink) in + ExploreFeedSettingsLanguage(languageLink, controlTag: index, displayType: self.displayType) + } + return languages + }() + + var feedContentController: WMFExploreFeedContentController? { + return dataStore?.feedContentController + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + open var sections: [ExploreFeedSettingsSection] { + assertionFailure("Subclassers should override") + return [] + } + + lazy var itemsGroupedByIndexPaths: [IndexPath: ExploreFeedSettingsItem] = { + var dictionary = [IndexPath: ExploreFeedSettingsItem]() + for (sectionIndex, section) in sections.enumerated() { + for (itemIndex, item) in section.items.enumerated() { + dictionary[IndexPath(row: itemIndex, section: sectionIndex)] = item + } + } + return dictionary + }() + + func getItem(at indexPath: IndexPath) -> ExploreFeedSettingsItem { + let items = getSection(at: indexPath.section).items + assert(items.indices.contains(indexPath.row), "Item at indexPath \(indexPath) doesn't exist") + return items[indexPath.row] + } + + func getSection(at index: Int) -> ExploreFeedSettingsSection { + assert(sections.indices.contains(index), "Section at index \(index) doesn't exist") + return sections[index] + } + + // MARK: - Notifications + + private func reload() { + for (indexPath, item) in itemsGroupedByIndexPaths { + item.updateIsOn(for: displayType) + item.updateDisclosureText(for: displayType) + item.updateSubtitle(for: displayType) + guard let cell = tableView.cellForRow(at: indexPath) as? WMFSettingsTableViewCell else { + continue + } + cell.disclosureSwitch.setOn(item.isOn, animated: true) + cell.disclosureText = item.disclosureText + cell.subtitle = item.subtitle + } + } + + @objc private func exploreFeedPreferencesDidSave(_ notification: Notification) { + updateFeedBeforeViewDisappears = true + DispatchQueue.main.async { + self.reload() + } + } + + @objc private func newExploreFeedPreferencesWereRejected(_ notification: Notification) { + guard let activeSwitch = activeSwitch else { + return + } + activeSwitch.setOn(!activeSwitch.isOn, animated: true) + } + + // MARK: - Themeable + + override func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.baseBackground + tableView.backgroundColor = theme.colors.baseBackground + if viewIfLoaded?.window != nil { + tableView.reloadData() + } + } + +} + +// MARK: - UITableViewDataSource + +extension BaseExploreFeedSettingsViewController { + override func numberOfSections(in tableView: UITableView) -> Int { + return sections.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + let section = getSection(at: section) + return section.items.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: WMFSettingsTableViewCell.identifier, for: indexPath) as? WMFSettingsTableViewCell else { + return UITableViewCell() + } + let item = getItem(at: indexPath) + configureCell(cell, item: item) + return cell + } + + private func configureCell(_ cell: WMFSettingsTableViewCell, item: ExploreFeedSettingsItem) { + cell.configure(item.disclosureType, disclosureText: item.disclosureText, title: item.title, subtitle: item.subtitle, iconName: item.iconName, isSwitchOn: item.isOn, iconColor: item.iconColor, iconBackgroundColor: item.iconBackgroundColor, controlTag: item.controlTag, theme: theme) + cell.delegate = self + } +} + +// MARK: - UITableViewDelegate + +extension BaseExploreFeedSettingsViewController { + + @objc func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return UITableView.automaticDimension + } + + @objc func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + let text = getSection(at: section).headerTitle + return WMFTableHeaderFooterLabelView.headerFooterViewForTableView(tableView, text: text, theme: theme) + } + + @objc func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + let text = getSection(at: section).footerTitle + return WMFTableHeaderFooterLabelView.headerFooterViewForTableView(tableView, text: text, type: .footer, setShortTextAsProse: true, theme: theme) + } + + @objc func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + + let text = getSection(at: section).footerTitle + guard !text.isEmpty else { + return 0 + } + + return UITableView.automaticDimension + } +} + +// MARK: - WMFSettingsTableViewCellDelegate + +extension BaseExploreFeedSettingsViewController: WMFSettingsTableViewCellDelegate { + open func settingsTableViewCell(_ settingsTableViewCell: WMFSettingsTableViewCell!, didToggleDisclosureSwitch sender: UISwitch!) { + assertionFailure("Subclassers should override") + } +} + +// MARK: - MWKLanguageLink Convenience Methods + +fileprivate extension MWKLanguageLink { + private var feedContentController: WMFExploreFeedContentController { + MWKDataStore.shared().feedContentController + } + + /** + Flag indicating whether there are any visible customizable feed content sources in this language. + Returns true if there is at least one content source in this language visible in the feed. + Returns false if there are no content sources in this language visible in the feed. + */ + var isInFeed: Bool { + feedContentController.anyContentGroupsVisibleInTheFeed(forSiteURL: siteURL) + } + + /** + Flag indicating whether the content group of given kind is visible in the feed in this language. + Returns YES if the content group of given kind is visible in the feed in this language. + Returns NO if the content group of given kind is not visible in the feed in this language. + */ + func isInFeed(for contentGroupKind: WMFContentGroupKind) -> Bool { + feedContentController.contentLanguageCodes(for: contentGroupKind).contains(contentLanguageCode) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/BasicLogger.swift b/Apps/Wikipedia/Wikipedia/Code/BasicLogger.swift new file mode 100644 index 0000000..28354dc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/BasicLogger.swift @@ -0,0 +1,7 @@ +@objc class BasicLogger: NSObject, DDLogger { + func log(message logMessage: DDLogMessage) { + print(logFormatter?.format(message: logMessage) ?? logMessage.message) + } + + var logFormatter: DDLogFormatter? +} diff --git a/Apps/Wikipedia/Wikipedia/Code/BatchEditSelectView.swift b/Apps/Wikipedia/Wikipedia/Code/BatchEditSelectView.swift new file mode 100644 index 0000000..afefd73 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/BatchEditSelectView.swift @@ -0,0 +1,147 @@ +import UIKit + +public class BatchEditSelectView: SizeThatFitsView, Themeable { + public var theme = Theme.standard + public func apply(theme: Theme) { + self.theme = theme + } + + fileprivate var multiSelectIndicator: UIImageView? + + override public init(frame: CGRect) { + super.init(frame: frame) + createSubview() + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public var isSelected: Bool = false { + didSet { + updateMultiSelectIndicatorImage() + } + } + + public var isSelectionDisabled: Bool = false { + didSet { + updateMultiSelectIndicatorImage() + } + } + + public var selectedImage: UIImage? { + didSet { + updateMultiSelectIndicatorImage() + } + } + + fileprivate func updateMultiSelectIndicatorImage() { + guard !isSelectionDisabled else { + multiSelectIndicator?.image = UIImage(named: "selection-disabled", in: Bundle.main, compatibleWith: nil) + multiSelectIndicator?.tintColor = theme.colors.midBackground + return + } + multiSelectIndicator?.image = isSelected ? (selectedImage ?? theme.multiSelectIndicatorImage) : UIImage(named: "unselected", in: Bundle.main, compatibleWith: nil) + } + + public override var frame: CGRect { + didSet { + setNeedsLayout() + } + } + + public static let fixedWidth: CGFloat = 60 + + public override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let superSize = super.sizeThatFits(size, apply: apply) + let width = superSize.width == UIView.noIntrinsicMetric ? BatchEditSelectView.fixedWidth : superSize.width + if apply { + multiSelectIndicator?.frame = CGRect(x: 0, y: 0, width: width, height: size.height) + } + let height = superSize.height == UIView.noIntrinsicMetric ? 50 : superSize.height + return CGSize(width: width, height: height) + } + + fileprivate func createSubview() { + for view in subviews { + view.removeFromSuperview() + } + + let multiSelectIndicator = UIImageView() + multiSelectIndicator.backgroundColor = .clear + insertSubview(multiSelectIndicator, at: 0) + multiSelectIndicator.contentMode = .center + self.multiSelectIndicator = multiSelectIndicator + updateMultiSelectIndicatorImage() + + backgroundColor = multiSelectIndicator.backgroundColor + setNeedsLayout() + } +} + +public enum EditingState: Int { + case unknown // pre-init state, nil delegate state + case empty // collection view is empty + case none // initial state + case open // batch editing pane is open + case closed // batch editing pane is closed + case swiping // swipe action is open + case editing // user is editing text + case cancelled // user pressed cancel bar button + case done // user pressed done bar button + + var tag: Int { + return self.rawValue + } +} + +public enum BatchEditToolbarActionType { + case update, addTo, addToList, moveTo, unsave, remove, delete + + public func action(with target: Any?) -> BatchEditToolbarAction { + var title: String = CommonStrings.updateActionTitle + var type: BatchEditToolbarActionType = .update + switch self { + case .moveTo: + title = CommonStrings.moveToActionTitle + type = .moveTo + case .addTo: + title = CommonStrings.addToActionTitle + type = .addTo + case .addToList: + title = CommonStrings.addToReadingListShortActionTitle + type = .addTo + case .unsave: + title = CommonStrings.shortUnsaveTitle + type = .unsave + case .delete: + title = CommonStrings.deleteActionTitle + type = .delete + case .remove: + title = CommonStrings.removeActionTitle + type = .remove + default: + break + } + return BatchEditToolbarAction(title: title, type: type, target: target) + } +} + +public class BatchEditToolbarAction: UIAccessibilityCustomAction { + let title: String + public let type: BatchEditToolbarActionType + + public init(title: String, type: BatchEditToolbarActionType, target: Any?) { + self.title = title + self.type = type + super.init(name: title, target: target, selector: #selector(ActionDelegate.didPerformBatchEditToolbarAction(_:completion:))) + } +} + +public protocol BatchEditableCell: NSObjectProtocol { + var isBatchEditing: Bool { get set } + var isBatchEditable: Bool { get set } + var batchEditSelectView: BatchEditSelectView? { get } + func layoutIfNeeded() // call to layout views after setting batch edit translation +} + diff --git a/Apps/Wikipedia/Wikipedia/Code/BeKindInputAccessoryView.swift b/Apps/Wikipedia/Wikipedia/Code/BeKindInputAccessoryView.swift new file mode 100644 index 0000000..699771d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/BeKindInputAccessoryView.swift @@ -0,0 +1,64 @@ +import UIKit + +protocol BeKindInputAccessoryViewDelegate: AnyObject { + func didUpdateHeight(view: BeKindInputAccessoryView) +} + +class BeKindInputAccessoryView: UIView, Themeable { + @IBOutlet private weak var beKindView: InfoBannerView! + @IBOutlet private weak var heightConstraint: NSLayoutConstraint! + + weak var delegate: BeKindInputAccessoryViewDelegate? + + var height: CGFloat { + return heightConstraint.constant + } + + var containerHeight: CGFloat? + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + private func commonInit() { + autoresizingMask = .flexibleHeight + } + + override var intrinsicContentSize: CGSize { + let superSize = super.intrinsicContentSize + return CGSize(width: superSize.width, height: heightConstraint.constant) + } + + override func awakeFromNib() { + super.awakeFromNib() + beKindView.configure(iconName: "heart-icon", title: CommonStrings.talkPageNewBannerTitle, subtitle: CommonStrings.talkPageNewBannerSubtitle) + heightConstraint.constant = beKindView.sizeThatFits(bounds.size, apply: true).height + } + + func apply(theme: Theme) { + backgroundColor = theme.colors.hintBackground + beKindView.apply(theme: theme) + } + + override func layoutSubviews() { + super.layoutSubviews() + + if let containerHeight = containerHeight { + beKindView.isDynamicFont = traitCollection.verticalSizeClass == .regular && containerHeight >= 600 + } + + let heightThatFits = beKindView.sizeThatFits(bounds.size, apply: true).height + if heightConstraint.constant != heightThatFits { + heightConstraint.constant = heightThatFits + delegate?.didUpdateHeight(view: self) + invalidateIntrinsicContentSize() + } + + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/BeKindInputAccessoryView.xib b/Apps/Wikipedia/Wikipedia/Code/BeKindInputAccessoryView.xib new file mode 100644 index 0000000..1dcb987 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/BeKindInputAccessoryView.xib @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/CIContext+WMFImageProcessing.h b/Apps/Wikipedia/Wikipedia/Code/CIContext+WMFImageProcessing.h new file mode 100644 index 0000000..16f8aac --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CIContext+WMFImageProcessing.h @@ -0,0 +1,6 @@ +@interface CIContext (WMFImageProcessing) + ++ (instancetype)wmf_sharedGPUContext; ++ (instancetype)wmf_sharedCPUContext; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/CIContext+WMFImageProcessing.m b/Apps/Wikipedia/Wikipedia/Code/CIContext+WMFImageProcessing.m new file mode 100644 index 0000000..3debbfc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CIContext+WMFImageProcessing.m @@ -0,0 +1,30 @@ +@import CoreImage; + +@implementation CIContext (WMFImageProcessing) + ++ (instancetype)wmf_sharedGPUContext { + static CIContext *sharedContext; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSDictionary *options = @{ + kCIContextPriorityRequestLow: @YES + }; + sharedContext = [CIContext contextWithOptions:options]; + }); + return sharedContext; +} + ++ (instancetype)wmf_sharedCPUContext { + static CIContext *sharedContext; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSDictionary *options = @{ + kCIContextPriorityRequestLow: @YES, + kCIContextUseSoftwareRenderer: @YES + }; + sharedContext = [CIContext contextWithOptions:options]; + }); + return sharedContext; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/CIDetector+WMFFaceDetection.h b/Apps/Wikipedia/Wikipedia/Code/CIDetector+WMFFaceDetection.h new file mode 100644 index 0000000..03ff683 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CIDetector+WMFFaceDetection.h @@ -0,0 +1,28 @@ +@import CoreImage; +#import + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const WMFFaceDetectionErrorDomain; + +typedef NS_ENUM(NSInteger, WMFFaceDectionError) { + WMFFaceDectionErrorUnknown = 0, + WMFFaceDectionErrorAppInBackground = 1 //face detection on GPU not allowed in the background +}; + +@interface CIDetector (WMFFaceDetection) + +/** + * Singleton `CIDetector` configured to detect faces. + */ ++ (instancetype)wmf_sharedGPUFaceDetector; ++ (instancetype)wmf_sharedCPUFaceDetector; + +/** + * Asynchronously detect faces in `image`, without doing extra processing for smiles or eyes. + */ +- (NSOperation *)wmf_detectFeaturelessFacesInImage:(UIImage *)image withFailure:(WMFErrorHandler)failure success:(WMFSuccessIdHandler)success; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/CIDetector+WMFFaceDetection.m b/Apps/Wikipedia/Wikipedia/Code/CIDetector+WMFFaceDetection.m new file mode 100644 index 0000000..43908a8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CIDetector+WMFFaceDetection.m @@ -0,0 +1,57 @@ +#import +#import +#import "UIImage+WMFImageProcessing.h" + +NSString *const WMFFaceDetectionErrorDomain = @"org.wikimedia.face-detection-error"; + +@implementation CIDetector (WMFFaceDetection) + ++ (instancetype)wmf_sharedGPUFaceDetector { + static CIDetector *defaultFaceDetector; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultFaceDetector = [CIDetector detectorOfType:CIDetectorTypeFace + context:[CIContext wmf_sharedGPUContext] + options:@{CIDetectorAccuracy: CIDetectorAccuracyLow}]; + }); + return defaultFaceDetector; +} + ++ (instancetype)wmf_sharedCPUFaceDetector { + static CIDetector *defaultFaceDetector; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultFaceDetector = [CIDetector detectorOfType:CIDetectorTypeFace + context:[CIContext wmf_sharedCPUContext] + options:@{CIDetectorAccuracy: CIDetectorAccuracyLow}]; + }); + return defaultFaceDetector; +} + ++ (NSDictionary *)wmf_featurelessFaceOptions { + static NSDictionary *featurelessFaceOptions; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + featurelessFaceOptions = @{ + CIDetectorAccuracy: CIDetectorAccuracyLow + }; + }); + return featurelessFaceOptions; +} + +- (NSOperation *)wmf_detectFeaturelessFacesInImage:(UIImage *)image withFailure:(WMFErrorHandler)failure success:(WMFSuccessIdHandler)success { + return [self wmf_detectFeaturesInImage:image + options:[CIDetector wmf_featurelessFaceOptions] + failure:failure + success:success]; +} + +- (NSOperation *)wmf_detectFeaturesInImage:(UIImage *)image options:(NSDictionary *)options failure:(WMFErrorHandler)failure success:(WMFSuccessIdHandler)success { + NSOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ + id features = [self featuresInImage:[image wmf_getOrCreateCIImage] options:options]; + success(features); + }]; + return blockOperation; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFBearing.h b/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFBearing.h new file mode 100644 index 0000000..2d42821 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFBearing.h @@ -0,0 +1,29 @@ +#import + +@interface CLLocation (WMFBearing) + +/** + * Calculate the bearing from the receiver to a given location, relative to the user's current heading. + * + * @param destination The target destination. + * @param currentHeading The heading reported by a location manger. + * + * @return The relative bearing in degrees adjusted for the current heading. + * + * @see -wmf_bearingToLocation: + */ +- (CLLocationDegrees)wmf_bearingToLocation:(CLLocation *)destination forCurrentHeading:(CLHeading *)currentHeading; + +/** + * Calculate the bearing from the receiver to a given location. + * + * Uses a special formula which accommodates for the Earth's approximate shape. Native port of JS function + * @c LatLon.prototype.bearingTo on http://www.movable-type.co.uk/scripts/latlong.html + * + * @param destination The target destination. + * + * @return The relative bearing in degrees, where a positive value goes in the clockwise direction. + */ +- (CLLocationDegrees)wmf_bearingToLocation:(CLLocation *)destination; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFBearing.m b/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFBearing.m new file mode 100644 index 0000000..262962b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFBearing.m @@ -0,0 +1,30 @@ +#import +#import + +@implementation CLLocation (WMFBearing) + +- (CLLocationDegrees)wmf_bearingToLocation:(CLLocation *)destination { + double const phiOrigin = DEGREES_TO_RADIANS(self.coordinate.latitude), + phiDest = DEGREES_TO_RADIANS(destination.coordinate.latitude), + deltaLambda = DEGREES_TO_RADIANS(destination.coordinate.longitude - self.coordinate.longitude), + y = sin(deltaLambda) * cos(phiDest), + x = cos(phiOrigin) * sin(phiDest) - sin(phiOrigin) * cos(phiDest) * cos(deltaLambda), + // bearing in radians in range [-180, 180] + veBearingRadians = atan2(y, x); + // convert to degrees and put in compass range [0, 360] + return fmod(RADIANS_TO_DEGREES(veBearingRadians) + 360.0, 360.0); +} + +- (CLLocationDegrees)wmf_bearingToLocation:(CLLocation *)location + forCurrentHeading:(CLHeading *)currentHeading { + CLLocationDegrees bearing = [self wmf_bearingToLocation:location]; + + // use true heading if available, otherwise fall back to magnetic heading + if (currentHeading.trueHeading >= 0) { + return bearing - currentHeading.trueHeading; + } else { + return bearing - currentHeading.magneticHeading; + } +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFComparison.h b/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFComparison.h new file mode 100644 index 0000000..302ba3d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFComparison.h @@ -0,0 +1,7 @@ +@import CoreLocation; + +@interface CLLocation (WMFComparison) + +- (BOOL)wmf_isEqual:(CLLocation *)location; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFComparison.m b/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFComparison.m new file mode 100644 index 0000000..c5b5db2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CLLocation+WMFComparison.m @@ -0,0 +1,16 @@ +#import +#import + +@implementation CLLocation (WMFComparison) + +- (BOOL)wmf_isEqual:(CLLocation *)rhs { + if (self == rhs) { + return YES; + } else if (![rhs isKindOfClass:[CLLocation class]]) { + return NO; + } else { + return [self distanceFromLocation:rhs] == 0 && self.horizontalAccuracy == rhs.horizontalAccuracy && self.verticalAccuracy == rhs.verticalAccuracy && [self.timestamp isEqualToDate:rhs.timestamp] && self.speed == rhs.speed && self.course == rhs.course; + } +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/Cancellable.swift b/Apps/Wikipedia/Wikipedia/Code/Cancellable.swift new file mode 100644 index 0000000..f350909 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Cancellable.swift @@ -0,0 +1,12 @@ +import Foundation + +@objc +public protocol Cancellable { + func cancel() +} + +extension Operation: Cancellable {} + +extension NSURLConnection: Cancellable {} + +extension URLSessionTask: Cancellable {} diff --git a/Apps/Wikipedia/Wikipedia/Code/CircledRankView.swift b/Apps/Wikipedia/Wikipedia/Code/CircledRankView.swift new file mode 100644 index 0000000..3dad3ea --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CircledRankView.swift @@ -0,0 +1,57 @@ +class CircledRankView: SizeThatFitsView { + fileprivate let label: UILabel = UILabel() + let padding = UIEdgeInsets(top: 3, left: 3, bottom: 3, right: 3) + + public var rankColor: UIColor = .blue600 { + didSet { + guard !label.textColor.isEqual(rankColor) else { + return + } + label.textColor = rankColor + layer.borderColor = rankColor.cgColor + } + } + + override func setup() { + super.setup() + layer.borderWidth = 1 + label.isOpaque = true + addSubview(label) + } + + var rank: Int = 0 { + didSet { + label.text = String.localizedStringWithFormat("%d", rank) + setNeedsLayout() + } + } + + var labelBackgroundColor: UIColor? { + didSet { + label.backgroundColor = labelBackgroundColor + } + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts(with: traitCollection) + } + + override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + label.font = UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + } + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let insetSize = CGRect(origin: .zero, size: size).inset(by: padding) + let labelSize = label.sizeThatFits(insetSize.size) + if apply { + layer.cornerRadius = 0.5*size.width + label.frame = CGRect(origin: CGPoint(x: 0.5*size.width - 0.5*labelSize.width, y: 0.5*size.height - 0.5*labelSize.height), size: labelSize) + } + let width = labelSize.width + padding.left + padding.right + let height = labelSize.height + padding.top + padding.bottom + let dimension = max(width, height) + return CGSize(width: dimension, height: dimension) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/CodemirrorSetupUserScript.swift b/Apps/Wikipedia/Wikipedia/Code/CodemirrorSetupUserScript.swift new file mode 100644 index 0000000..b677ded --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CodemirrorSetupUserScript.swift @@ -0,0 +1,27 @@ +import Foundation + +// sets the theme using wmf.applyTheme atDocumentEnd +class CodemirrorSetupUserScript: PageUserScript, WKScriptMessageHandler { + public enum CodemirrorDirection: String { + case ltr + case rtl + } + let messageHandlerName = "wmfCodemirrorReady" + let completion: () -> Void + + init(languageCode: String, direction: CodemirrorDirection, theme: Theme, textSizeAdjustment: Int, isSyntaxHighlighted: Bool, readOnly: Bool, completion: @escaping () -> Void) { + self.completion = completion + let source = """ + wmf.setup('\(languageCode)', '\(direction.rawValue)', '\(theme.webName)', \(textSizeAdjustment), \(isSyntaxHighlighted), \(readOnly), () => { + window.webkit.messageHandlers.\(messageHandlerName).postMessage({}) + }) + """ + super.init(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true) + } + + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + DispatchQueue.main.async { + self.completion() + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Collection.swift b/Apps/Wikipedia/Wikipedia/Code/Collection.swift new file mode 100644 index 0000000..f413246 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Collection.swift @@ -0,0 +1,162 @@ +protocol Collection: AnyObject { + var collectionView: UICollectionView { get set } +} + +protocol UpdatableCollection: Collection, CollectionViewUpdaterDelegate { + associatedtype T: NSManagedObject + var dataStore: MWKDataStore { get } + var collectionViewUpdater: CollectionViewUpdater? { get set } + var fetchedResultsController: NSFetchedResultsController? { get set } + var basePredicate: NSPredicate { get } + var baseSortDescriptors: [NSSortDescriptor] { get } + func setupFetchedResultsController() +} + +extension UpdatableCollection { + func setupCollectionViewUpdater() { + guard let fetchedResultsController = fetchedResultsController else { + return + } + collectionViewUpdater = CollectionViewUpdater(fetchedResultsController: fetchedResultsController, collectionView: collectionView) + collectionViewUpdater?.delegate = self + } + + func fetch() { + collectionViewUpdater?.performFetch() + } + + func reset() { + setupFetchedResultsController() + setupCollectionViewUpdater() + fetch() + } +} + +extension UpdatableCollection where Self: SearchableCollection { + func setupFetchedResultsController() { + guard let request = T.fetchRequest() as? NSFetchRequest else { + assertionFailure("Can't set up NSFetchRequest") + return + } + request.predicate = basePredicate + if let searchPredicate = searchPredicate { + request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [basePredicate, searchPredicate]) + } + + request.sortDescriptors = baseSortDescriptors + fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: dataStore.viewContext, sectionNameKeyPath: nil, cacheName: nil) + } +} + +protocol SearchableCollection: UpdatableCollection { + var searchString: String? { get set } + func updateSearchString(_ newSearchString: String) + var searchPredicate: NSPredicate? { get } +} + +extension SearchableCollection where Self: EditableCollection { + func updateSearchString(_ newSearchString: String) { + guard newSearchString != searchString else { + return + } + searchString = newSearchString.isEmpty ? nil : newSearchString + editController.close() + reset() + } +} + +enum SortActionType: Int { + case byTitle, byRecentlyAdded + + func action(with sortDescriptors: [NSSortDescriptor], handler: @escaping ([NSSortDescriptor], UIAlertAction, Int) -> Void) -> SortAction { + let title: String + switch self { + case .byTitle: + title = WMFLocalizedString("sort-by-title-action", value: "Title", comment: "Title of the sort action that allows sorting items by title.") + case .byRecentlyAdded: + title = WMFLocalizedString("sort-by-recently-added-action", value: "Recently added", comment: "Title of the sort action that allows sorting items by date added.") + } + + let alertAction = UIAlertAction(title: title, style: .default) { (alertAction) in + handler(sortDescriptors, alertAction, self.rawValue) + } + return SortAction(alertAction: alertAction, type: self, sortDescriptors: sortDescriptors) + } +} + +struct SortAction { + let alertAction: UIAlertAction + let type: SortActionType + let sortDescriptors: [NSSortDescriptor] +} + +protocol SortableCollection: UpdatableCollection { + var sort: (descriptors: [NSSortDescriptor], alertAction: UIAlertAction?) { get } + var defaultSortAction: SortAction? { get } + var defaultSortDescriptors: [NSSortDescriptor] { get } + var sortActions: [SortActionType: SortAction] { get } + var sortAlert: UIAlertController { get } + func presentSortAlert(from button: UIButton) + func updateSortActionCheckmark() +} + +extension SortableCollection where Self: UIViewController { + + func alert(title: String, message: String?) -> UIAlertController { + let alert = UIAlertController(title: title, message: message, preferredStyle: .actionSheet) + sortActions.values.forEach { alert.addAction($0.alertAction) } + let cancel = UIAlertAction(title: CommonStrings.cancelActionTitle, style: .cancel) + alert.addAction(cancel) + return alert + } + + func updateSortActionCheckmark() { + // hax https://stackoverflow.com/questions/40647039/how-to-add-uiactionsheet-button-check-mark + let checkedKey = "checked" + sortActions.values.forEach { $0.alertAction.setValue(false, forKey: checkedKey) } + let checkedAction = sort.alertAction ?? defaultSortAction?.alertAction + checkedAction?.setValue(true, forKey: checkedKey) + } + + func presentSortAlert(from button: UIButton) { + if let popoverController = sortAlert.popoverPresentationController { + popoverController.sourceView = button + popoverController.sourceRect = button.bounds + } + present(sortAlert, animated: true) + updateSortActionCheckmark() + } + + var baseSortDescriptors: [NSSortDescriptor] { + return sort.descriptors.isEmpty ? defaultSortDescriptors : sort.descriptors + } + + var defaultSortDescriptors: [NSSortDescriptor] { + guard let defaultSortAction = defaultSortAction else { + assertionFailure("Sort action not found") + return [] + } + return defaultSortAction.sortDescriptors + } +} + +protocol EditableCollection: Collection { + var editController: CollectionViewEditController! { get set } + var shouldShowEditButtonsForEmptyState: Bool { get } + func setupEditController() +} + +extension EditableCollection where Self: ActionDelegate { + func setupEditController() { + editController = CollectionViewEditController(collectionView: collectionView) + editController.delegate = self + editController.shouldShowEditButtonsForEmptyState = shouldShowEditButtonsForEmptyState + if let navigationDelegate = self as? CollectionViewEditControllerNavigationDelegate { + editController.navigationDelegate = navigationDelegate + } + } + + var shouldShowEditButtonsForEmptyState: Bool { + return false + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/CollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/CollectionViewCell.swift new file mode 100644 index 0000000..e09d44c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CollectionViewCell.swift @@ -0,0 +1,225 @@ +import UIKit + +// CollectionViewCell is the base class of collection view cells that use manual layout. +// These cells use a manual layout rather than auto layout for a few reasons: +// 1. A significant in-code implementation was required anyway for handling the complexity of +// hiding & showing different parts of the cells with auto layout +// 2. The performance advantage over auto layout for views that contain several article cells. +// (To further alleviate this performance issue, ColumnarCollectionViewLayout could be updated +// to not require a full layout pass for calculating the total collection view content size. Instead, +// it could do a rough estimate pass, and then update the content size as the user scrolls.) +// 3. Handling RTL content on LTR devices and vice versa + +open class CollectionViewCell: UICollectionViewCell { + // MARK: - Methods for subclassing + + // Subclassers should override setup instead of any of the initializers. Subclassers must call super.setup() + open func setup() { + contentView.translatesAutoresizingMaskIntoConstraints = false + preservesSuperviewLayoutMargins = false + contentView.preservesSuperviewLayoutMargins = false + insetsLayoutMarginsFromSafeArea = false + contentView.insetsLayoutMarginsFromSafeArea = false + autoresizesSubviews = false + contentView.autoresizesSubviews = false + backgroundView = UIView() + selectedBackgroundView = UIView() + reset() + setNeedsLayout() + } + + open func reset() { + + } + + public var labelBackgroundColor: UIColor? { + didSet { + updateBackgroundColorOfLabels() + } + } + + public func setBackgroundColors(_ deselected: UIColor, selected: UIColor) { + backgroundView?.backgroundColor = deselected + selectedBackgroundView?.backgroundColor = selected + let newColor = isSelectedOrHighlighted ? selected : deselected + if newColor != labelBackgroundColor { + labelBackgroundColor = newColor + } + } + + // Subclassers should call super + open func updateBackgroundColorOfLabels() { + + } + + var isSelectedOrHighlighted: Bool = false + + public func updateSelectedOrHighlighted() { + let newIsSelectedOrHighlighted = isSelected || isHighlighted + guard newIsSelectedOrHighlighted != isSelectedOrHighlighted else { + return + } + + isSelectedOrHighlighted = newIsSelectedOrHighlighted + + // It appears that background color changes aren't properly animated when set within the animation block around isHighlighted/isSelected state changes + // https://phabricator.wikimedia.org/T174341 + + // To work around this, first set the background to clear without animation so that it stays clear throughought the animation + UIView.performWithoutAnimation { + self.labelBackgroundColor = .clear + } + + // Then update the completion block to set the actual opaque color we want after the animation completes + let existingCompletionBlock = CATransaction.completionBlock() + CATransaction.setCompletionBlock { + if let block = existingCompletionBlock { + block() + } + self.labelBackgroundColor = self.isSelected || self.isHighlighted ? self.selectedBackgroundView?.backgroundColor : self.backgroundView?.backgroundColor + } + } + + open override var isHighlighted: Bool { + didSet { + updateSelectedOrHighlighted() + } + } + + open override var isSelected: Bool { + didSet { + updateSelectedOrHighlighted() + } + } + + // Subclassers should override sizeThatFits:apply: instead of layoutSubviews to lay out subviews. + // In this method, subclassers should calculate the appropriate layout size and if apply is `true`, + // apply the layout to the subviews. + open func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + return size + } + + // Subclassers should override updateAccessibilityElements to update any accessibility elements + // that should be updated after layout. Subclassers must call super.updateAccessibilityElements() + open func updateAccessibilityElements() { + + } + + // MARK: - Initializers + // Don't override these initializers, use setup() instead + + public override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + // MARK: - Cell lifecycle + + open override func prepareForReuse() { + super.prepareForReuse() + reset() + } + + // MARK: - Layout + + open override func layoutMarginsDidChange() { + super.layoutMarginsDidChange() + setNeedsLayout() + } + + final override public func layoutSubviews() { + super.layoutSubviews() + contentView.frame = bounds + backgroundView?.frame = bounds + selectedBackgroundView?.frame = bounds + let size = bounds.size + _ = sizeThatFits(size, apply: true) + updateAccessibilityElements() + #if DEBUG + for view in subviews { + guard view !== backgroundView, view !== selectedBackgroundView else { + continue + } + assert(view.autoresizingMask == []) + assert(view.constraints == []) + } + #endif + } + + final override public func sizeThatFits(_ size: CGSize) -> CGSize { + return sizeThatFits(size, apply: false) + } + + final override public func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { + if let attributesToFit = layoutAttributes as? ColumnarCollectionViewLayoutAttributes { + layoutMargins = attributesToFit.layoutMargins + if attributesToFit.precalculated { + return attributesToFit + } + } + + var sizeToFit = layoutAttributes.size + sizeToFit.height = UIView.noIntrinsicMetric + var fitSize = self.sizeThatFits(sizeToFit) + if fitSize == sizeToFit { + return layoutAttributes + } else if let attributes = layoutAttributes.copy() as? UICollectionViewLayoutAttributes { + fitSize.width = sizeToFit.width + if fitSize.height == CGFloat.greatestFiniteMagnitude || fitSize.height == UIView.noIntrinsicMetric { + fitSize.height = layoutAttributes.size.height + } + attributes.frame = CGRect(origin: layoutAttributes.frame.origin, size: fitSize) + return attributes + } else { + return layoutAttributes + } + } + + // MARK: - Dynamic Type + // Only applies new fonts if the content size category changes + + open override func setNeedsLayout() { + maybeUpdateFonts(with: traitCollection) + super.setNeedsLayout() + } + + override open func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + setNeedsLayout() + } + + var contentSizeCategory: UIContentSizeCategory? + fileprivate func maybeUpdateFonts(with traitCollection: UITraitCollection) { + guard contentSizeCategory == nil || contentSizeCategory != traitCollection.wmf_preferredContentSizeCategory else { + return + } + contentSizeCategory = traitCollection.wmf_preferredContentSizeCategory + updateFonts(with: traitCollection) + } + + // Override this method and call super + open func updateFonts(with traitCollection: UITraitCollection) { + + } + + // MARK: - Layout Margins + + public var layoutMarginsAdditions: UIEdgeInsets = .zero + public var layoutMarginsInteractiveAdditions: UIEdgeInsets = .zero + public func layoutWidth(for size: CGSize) -> CGFloat { // layoutWidth doesn't take into account interactive additions + return size.width - layoutMargins.left - layoutMargins.right - layoutMarginsAdditions.right - layoutMarginsAdditions.left + } + public var calculatedLayoutMargins: UIEdgeInsets { + let margins = self.layoutMargins + return UIEdgeInsets(top: margins.top + layoutMarginsAdditions.top + layoutMarginsInteractiveAdditions.top, + left: margins.left + layoutMarginsAdditions.left + layoutMarginsInteractiveAdditions.left, + bottom: margins.bottom + layoutMarginsAdditions.bottom + layoutMarginsInteractiveAdditions.bottom, + right: margins.right + layoutMarginsAdditions.right + layoutMarginsInteractiveAdditions.right) + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/CollectionViewContextMenuShowing.swift b/Apps/Wikipedia/Wikipedia/Code/CollectionViewContextMenuShowing.swift new file mode 100644 index 0000000..cb46f82 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CollectionViewContextMenuShowing.swift @@ -0,0 +1,7 @@ +import Foundation +import UIKit + +protocol CollectionViewContextMenuShowing { + func previewingViewController(for indexPath: IndexPath, at location: CGPoint) -> UIViewController? + var poppingIntoVCCompletion: () -> Void { get } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/CollectionViewEditControllerNavigationDelegate+Extensions.swift b/Apps/Wikipedia/Wikipedia/Code/CollectionViewEditControllerNavigationDelegate+Extensions.swift new file mode 100644 index 0000000..b497e39 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CollectionViewEditControllerNavigationDelegate+Extensions.swift @@ -0,0 +1,35 @@ +extension CollectionViewEditControllerNavigationDelegate where Self: UIViewController { + func didSetBatchEditToolbarHidden(_ batchEditToolbarViewController: BatchEditToolbarViewController, isHidden: Bool, with items: [UIButton]) { + + + if batchEditToolbarViewController.parent == nil { + addChild(batchEditToolbarViewController) + batchEditToolbarViewController.view.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(batchEditToolbarViewController.view) + batchEditToolbarViewController.didMove(toParent: self) + + let leadingConstraint = batchEditToolbarViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor) + let trailingConstraint = batchEditToolbarViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) + let bottomConstraint = batchEditToolbarViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor) + view.addConstraints([leadingConstraint, trailingConstraint, bottomConstraint]) + + batchEditToolbarViewController.view.alpha = 0 + + } + + UIView.transition(with: batchEditToolbarViewController.view, duration: 0.7, options: .transitionCrossDissolve, animations: { + batchEditToolbarViewController.view.alpha = isHidden ? 0 : 1 + }) { _ in + if isHidden { + batchEditToolbarViewController.view.removeFromSuperview() + batchEditToolbarViewController.willMove(toParent: nil) + batchEditToolbarViewController.removeFromParent() + } + } + batchEditToolbarViewController.apply(theme: currentTheme) + } + + func emptyStateDidChange(_ empty: Bool) { + // conforming types can provide their own implementations + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/CollectionViewFooter.swift b/Apps/Wikipedia/Wikipedia/Code/CollectionViewFooter.swift new file mode 100644 index 0000000..3ef04c5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CollectionViewFooter.swift @@ -0,0 +1,60 @@ +protocol CollectionViewFooterDelegate: AnyObject { + func collectionViewFooterButtonWasPressed(_ collectionViewFooter: CollectionViewFooter) +} + +class CollectionViewFooter: SizeThatFitsReusableView { + private let button = UIButton(type: .system) + weak var delegate: CollectionViewFooterDelegate? + + var buttonTitle: String? { + didSet { + button.setTitle(buttonTitle, for: .normal) + button.isHidden = buttonTitle == nil + } + } + + override func setup() { + super.setup() + button.layer.cornerRadius = 8 + button.contentEdgeInsets = contentEdgeInsets + button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside) + addSubview(button) + } + + var contentEdgeInsets: UIEdgeInsets = UIEdgeInsets(top: 12, left: 24, bottom: 12, right: 24) + + @objc private func buttonPressed() { + delegate?.collectionViewFooterButtonWasPressed(self) + } + + override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + button.titleLabel?.font = UIFont.wmf_font(.semiboldSubheadline, compatibleWithTraitCollection: traitCollection) + } + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let size = super.sizeThatFits(size, apply: apply) + let widthMinusMargins = size.width - layoutMargins.left - layoutMargins.right + var origin = CGPoint(x: layoutMargins.left, y: layoutMargins.top) + let additionalMargins = UIEdgeInsets(top: 40, left: 0, bottom: 40, right: 0) + + if !button.isHidden { + let buttonFrame = button.wmf_preferredFrame(at: origin, maximumWidth: widthMinusMargins, horizontalAlignment: .center, apply: apply) + button.center = CGPoint(x: size.width / 2, y: size.height / 2) + origin.y += buttonFrame.height + additionalMargins.top + additionalMargins.bottom + origin.y += layoutMargins.bottom + } else { + origin.y = 0 + } + + return CGSize(width: size.width, height: origin.y) + } +} + +extension CollectionViewFooter: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + button.setTitleColor(theme.colors.link, for: .normal) + button.backgroundColor = theme.colors.cardButtonBackground + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/CollectionViewHeader.swift b/Apps/Wikipedia/Wikipedia/Code/CollectionViewHeader.swift new file mode 100644 index 0000000..9b1cfba --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CollectionViewHeader.swift @@ -0,0 +1,145 @@ +import UIKit + +protocol CollectionViewHeaderDelegate: AnyObject { + func collectionViewHeaderButtonWasPressed(_ collectionViewHeader: CollectionViewHeader) +} + +class CollectionViewHeader: SizeThatFitsReusableView { + weak var delegate: CollectionViewHeaderDelegate? + + public enum Style { + case explore + case detail + case history + case recentSearches + case pageHistory + } + + public var style: Style = .explore { + didSet { + updateFonts(with: traitCollection) + } + } + + private let titleLabel: UILabel = UILabel() + private let subtitleLabel: UILabel = UILabel() + private let button: UIButton = UIButton() + private let spacing: CGFloat = 5 + + var title: String? { + get { + return titleLabel.text + } + set { + titleLabel.text = newValue + setNeedsLayout() + } + } + + var titleTextColorKeyPath: KeyPath = \Theme.colors.primaryText + + var subtitle: String? { + get { + return subtitleLabel.text + } + set { + subtitleLabel.text = newValue + subtitleLabel.isHidden = subtitleLabel.text == nil + setNeedsLayout() + } + } + + var buttonTitle: String? { + get { + return button.title(for: .normal) + } + set { + button.setTitle(newValue, for: .normal) + button.isHidden = newValue == nil + } + } + + override func setup() { + super.setup() + addSubview(button) + addSubview(subtitleLabel) + addSubview(titleLabel) + button.addTarget(self, action: #selector(buttonPressed(_:)), for: .touchUpInside) + button.isHidden = true + } + + @objc func buttonPressed(_ sender: UIButton?) { + delegate?.collectionViewHeaderButtonWasPressed(self) + } + + override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + let titleTextStyle: DynamicTextStyle + let subtitleTextStyle: DynamicTextStyle = .subheadline + let buttonTextStyle: DynamicTextStyle = .subheadline + switch style { + case .detail: + fallthrough + case .explore: + titleTextStyle = .boldTitle2 + default: + titleTextStyle = .semiboldHeadline + } + titleLabel.font = UIFont.wmf_font(titleTextStyle, compatibleWithTraitCollection: traitCollection) + subtitleLabel.font = UIFont.wmf_font(subtitleTextStyle, compatibleWithTraitCollection: traitCollection) + button.titleLabel?.font = UIFont.wmf_font(buttonTextStyle, compatibleWithTraitCollection: traitCollection) + } + + override func layoutMarginsDidChange() { + super.layoutMarginsDidChange() + setNeedsLayout() + } + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + let additionalMargins: UIEdgeInsets + switch style { + case .history: + additionalMargins = UIEdgeInsets(top: 30, left: 0, bottom: 10, right: 0) + case .recentSearches: + additionalMargins = UIEdgeInsets(top: 10, left: 0, bottom: 5, right: 0) + case .detail: + additionalMargins = UIEdgeInsets(top: 45, left: 0, bottom: 35, right: 0) + case .pageHistory: + additionalMargins = UIEdgeInsets(top: 10, left: 6, bottom: 30, right: 6) + default: + additionalMargins = .zero + } + let baseMargins = self.layoutMargins + let layoutMargins = UIEdgeInsets(top: baseMargins.top + additionalMargins.top, left: baseMargins.left + additionalMargins.left, bottom: baseMargins.bottom + additionalMargins.bottom, right: baseMargins.right + additionalMargins.right) + let size = super.sizeThatFits(size, apply: apply) + var widthMinusMargins = size.width - layoutMargins.left - layoutMargins.right + let isRTL = traitCollection.layoutDirection == .rightToLeft + let labelHorizontalAlignment: HorizontalAlignment = isRTL ? .right : .left + let buttonHorizontalAlignment: HorizontalAlignment = isRTL ? .left : .right + var origin = CGPoint(x: layoutMargins.left, y: layoutMargins.top) + if !button.isHidden { + let buttonFrame = button.wmf_preferredFrame(at: origin, maximumWidth: widthMinusMargins, horizontalAlignment: buttonHorizontalAlignment, apply: apply) + widthMinusMargins -= (buttonFrame.width + layoutMargins.right) + } + origin.y += titleLabel.wmf_preferredHeight(at: origin, maximumWidth:widthMinusMargins, horizontalAlignment: labelHorizontalAlignment, spacing: 0, apply: apply) + if subtitleLabel.text != nil { + origin.y += spacing + origin.y += subtitleLabel.wmf_preferredHeight(at: origin, maximumWidth: widthMinusMargins, horizontalAlignment: labelHorizontalAlignment, spacing: 0, apply: apply) + } + origin.y += layoutMargins.bottom + return CGSize(width: size.width, height: origin.y) + } + +} + +extension CollectionViewHeader: Themeable { + func apply(theme: Theme) { + titleLabel.textColor = theme[keyPath: titleTextColorKeyPath] + titleLabel.backgroundColor = theme.colors.paperBackground + subtitleLabel.textColor = theme.colors.secondaryText + subtitleLabel.backgroundColor = theme.colors.paperBackground + backgroundColor = theme.colors.paperBackground + tintColor = theme.colors.link + button.setTitleColor(theme.colors.link, for: .normal) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/CollectionViewUpdater.swift b/Apps/Wikipedia/Wikipedia/Code/CollectionViewUpdater.swift new file mode 100644 index 0000000..a973cb2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CollectionViewUpdater.swift @@ -0,0 +1,238 @@ +import Foundation +import CocoaLumberjackSwift + +protocol CollectionViewUpdaterDelegate: NSObjectProtocol { + func collectionViewUpdater(_ updater: CollectionViewUpdater, didUpdate collectionView: UICollectionView) + func collectionViewUpdater(_ updater: CollectionViewUpdater, updateItemAtIndexPath indexPath: IndexPath, in collectionView: UICollectionView) +} + +class CollectionViewUpdater: NSObject, NSFetchedResultsControllerDelegate { + + let fetchedResultsController: NSFetchedResultsController + let collectionView: UICollectionView + var isSlidingNewContentInFromTheTopEnabled: Bool = false + var sectionChanges: [WMFSectionChange] = [] + var objectChanges: [WMFObjectChange] = [] + weak var delegate: CollectionViewUpdaterDelegate? + + var isGranularUpdatingEnabled: Bool = true // when set to false, individual updates won't be pushed to the collection view, only reloadData() + + required init(fetchedResultsController: NSFetchedResultsController, collectionView: UICollectionView) { + self.fetchedResultsController = fetchedResultsController + self.collectionView = collectionView + super.init() + self.fetchedResultsController.delegate = self + } + + deinit { + self.fetchedResultsController.delegate = nil + } + + public func performFetch() { + do { + try fetchedResultsController.performFetch() + } catch let error { + assert(false) + DDLogError("Error fetching \(String(describing: fetchedResultsController.fetchRequest.predicate)) for \(String(describing: self.delegate)): \(error)") + } + sectionCounts = fetchSectionCounts() + collectionView.reloadData() + } + + @objc func controllerWillChangeContent(_ controller: NSFetchedResultsController) { + sectionChanges = [] + objectChanges = [] + } + + @objc func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { + let objectChange = WMFObjectChange() + objectChange.fromIndexPath = indexPath + objectChange.toIndexPath = newIndexPath + objectChange.type = type + objectChanges.append(objectChange) + } + + @objc func controller(_ controller: NSFetchedResultsController, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) { + let sectionChange = WMFSectionChange() + sectionChange.sectionIndex = sectionIndex + sectionChange.type = type + sectionChanges.append(sectionChange) + } + + private var previousSectionCounts: [Int] = [] + private var sectionCounts: [Int] = [] + private func fetchSectionCounts() -> [Int] { + let sections = fetchedResultsController.sections ?? [] + return sections.map { $0.numberOfObjects } + } + + @objc func controllerDidChangeContent(_ controller: NSFetchedResultsController) { + previousSectionCounts = sectionCounts + sectionCounts = fetchSectionCounts() + + guard isGranularUpdatingEnabled else { + collectionView.reloadData() + delegate?.collectionViewUpdater(self, didUpdate: self.collectionView) + return + } + + var didInsertFirstSection = false + var didOnlyChangeItems = true + var sectionDelta = 0 + var objectsInSectionDelta = 0 + var forceReload = false + + for sectionChange in sectionChanges { + didOnlyChangeItems = false + switch sectionChange.type { + case .delete: + guard sectionChange.sectionIndex < previousSectionCounts.count else { + forceReload = true + break + } + sectionDelta -= 1 + case .insert: + sectionDelta += 1 + objectsInSectionDelta += sectionCounts[sectionChange.sectionIndex] + if sectionChange.sectionIndex == 0 { + didInsertFirstSection = true + } + default: + break + } + } + + for objectChange in objectChanges { + switch objectChange.type { + case .delete: + guard let fromIndexPath = objectChange.fromIndexPath, + fromIndexPath.section < previousSectionCounts.count, + fromIndexPath.item < previousSectionCounts[fromIndexPath.section] else { + forceReload = true + break + } + + // there seems to be a very specific bug about deleting the item at index path 0,2 when there are 3 items in the section ¯\_(ツ)_/¯ + if fromIndexPath.section == 0 && fromIndexPath.item == 2 && previousSectionCounts[0] == 3 { + forceReload = true + break + } + + default: + break + } + } + + let sectionCountsMatch = (previousSectionCounts.count + sectionDelta) == sectionCounts.count + let currentNumberOfSections = collectionView.numberOfSections + let previousSectionCountsEqualCurrentNumberOfSections = previousSectionCounts.count == currentNumberOfSections + guard !forceReload, sectionCountsMatch, previousSectionCountsEqualCurrentNumberOfSections, objectChanges.count < 1000 && sectionChanges.count < 10 else { // reload data for invalid changes & larger changes + collectionView.reloadData() + delegate?.collectionViewUpdater(self, didUpdate: self.collectionView) + return + } + + guard isSlidingNewContentInFromTheTopEnabled else { + performBatchUpdates(consideredNumberOfSections: currentNumberOfSections) + return + } + + guard let columnarLayout = collectionView.collectionViewLayout as? ColumnarCollectionViewLayout else { + performBatchUpdates(consideredNumberOfSections: currentNumberOfSections) + return + } + + guard !previousSectionCounts.isEmpty && didInsertFirstSection && sectionDelta > 0 else { + if didOnlyChangeItems { + columnarLayout.animateItems = true + columnarLayout.slideInNewContentFromTheTop = false + UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .allowUserInteraction, animations: { + self.performBatchUpdates(consideredNumberOfSections: currentNumberOfSections) + }, completion: nil) + } else { + columnarLayout.animateItems = false + columnarLayout.slideInNewContentFromTheTop = false + performBatchUpdates(consideredNumberOfSections: currentNumberOfSections) + } + return + } + columnarLayout.animateItems = true + columnarLayout.slideInNewContentFromTheTop = true + UIView.animate(withDuration: 0.7 + 0.1 * TimeInterval(objectsInSectionDelta), delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .allowUserInteraction, animations: { + self.performBatchUpdates(consideredNumberOfSections: currentNumberOfSections) + }, completion: nil) + } + + func performBatchUpdates(consideredNumberOfSections: Int) { + let collectionView = self.collectionView + + // Here we are giving it one last chance to force reload, in case the numberOfSections have changed since the last time we considered it for force reloading, to try to avoid invalid update crashes. //https://phabricator.wikimedia.org/T253762 + guard consideredNumberOfSections == collectionView.numberOfSections else { + collectionView.reloadData() + delegate?.collectionViewUpdater(self, didUpdate: self.collectionView) + return + } + + collectionView.performBatchUpdates({ + DDLogDebug("=== WMFBU BATCH UPDATE START \(String(describing: self.delegate)) ===") + for objectChange in objectChanges { + switch objectChange.type { + case .delete: + if let fromIndexPath = objectChange.fromIndexPath { + DDLogDebug("WMFBU object delete: \(fromIndexPath)") + collectionView.deleteItems(at: [fromIndexPath]) + } else { + assert(false, "unhandled delete") + DDLogError("Unhandled delete: \(objectChange)") + } + case .insert: + if let toIndexPath = objectChange.toIndexPath { + DDLogDebug("WMFBU object insert: \(toIndexPath)") + collectionView.insertItems(at: [toIndexPath]) + } else { + assert(false, "unhandled insert") + DDLogError("Unhandled insert: \(objectChange)") + } + case .move: + if let fromIndexPath = objectChange.fromIndexPath, let toIndexPath = objectChange.toIndexPath { + DDLogDebug("WMFBU object move delete: \(fromIndexPath)") + collectionView.deleteItems(at: [fromIndexPath]) + DDLogDebug("WMFBU object move insert: \(toIndexPath)") + collectionView.insertItems(at: [toIndexPath]) + } else { + assert(false, "unhandled move") + DDLogError("Unhandled move: \(objectChange)") + } + break + case .update: + if let updatedIndexPath = objectChange.toIndexPath ?? objectChange.fromIndexPath { + delegate?.collectionViewUpdater(self, updateItemAtIndexPath: updatedIndexPath, in: collectionView) + } else { + assert(false, "unhandled update") + DDLogDebug("WMFBU unhandled update: \(objectChange)") + } + @unknown default: + break + } + } + + for sectionChange in sectionChanges { + switch sectionChange.type { + case .delete: + DDLogDebug("WMFBU section delete: \(sectionChange.sectionIndex)") + collectionView.deleteSections(IndexSet(integer: sectionChange.sectionIndex)) + case .insert: + DDLogDebug("WMFBU section insert: \(sectionChange.sectionIndex)") + collectionView.insertSections(IndexSet(integer: sectionChange.sectionIndex)) + default: + DDLogDebug("WMFBU section update: \(sectionChange.sectionIndex)") + collectionView.reloadSections(IndexSet(integer: sectionChange.sectionIndex)) + } + } + DDLogDebug("=== WMFBU BATCH UPDATE END ===") + }) { (finished) in + self.delegate?.collectionViewUpdater(self, didUpdate: collectionView) + } + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ColumnarCollectionViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ColumnarCollectionViewController.swift new file mode 100644 index 0000000..3ad1bff --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ColumnarCollectionViewController.swift @@ -0,0 +1,430 @@ +import UIKit +import WMF + +class ColumnarCollectionViewController: ViewController, ColumnarCollectionViewLayoutDelegate, UICollectionViewDataSourcePrefetching, CollectionViewFooterDelegate, HintPresenting { + + enum HeaderStyle { + case sections + case exploreFeedDetail + } + + open var headerStyle: HeaderStyle { + return .exploreFeedDetail + } + + lazy var layout: ColumnarCollectionViewLayout = { + return ColumnarCollectionViewLayout() + }() + + lazy var layoutCache: ColumnarCollectionViewControllerLayoutCache = { + return ColumnarCollectionViewControllerLayoutCache() + }() + + @objc lazy var collectionView: UICollectionView = { + let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) + cv.delegate = self + cv.dataSource = self + cv.isPrefetchingEnabled = true + cv.prefetchDataSource = self + cv.preservesSuperviewLayoutMargins = true + scrollView = cv + return cv + }() + + lazy var layoutManager: ColumnarCollectionViewLayoutManager = { + return ColumnarCollectionViewLayoutManager(view: view, collectionView: collectionView) + }() + + deinit { + NotificationCenter.default.removeObserver(self) + } + + override func viewDidLoad() { + super.viewDidLoad() + view.wmf_addSubviewWithConstraintsToEdges(collectionView) + layoutManager.register(CollectionViewHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: CollectionViewHeader.identifier, addPlaceholder: true) + layoutManager.register(CollectionViewFooter.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: CollectionViewFooter.identifier, addPlaceholder: true) + collectionView.alwaysBounceVertical = true + extendedLayoutIncludesOpaqueBars = true + } + + @objc open func contentSizeCategoryDidChange(_ notification: Notification?) { + collectionView.reloadData() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if isFirstAppearance { + isFirstAppearance = false + viewWillHaveFirstAppearance(animated) + updateEmptyState() + isEmptyDidChange() // perform initial update even though the value might not have changed + } else { + updateEmptyState() + } + if let selectedIndexPaths = collectionView.indexPathsForSelectedItems { + for selectedIndexPath in selectedIndexPaths { + collectionView.deselectItem(at: selectedIndexPath, animated: animated) + } + } + for cell in collectionView.visibleCells { + guard let cellWithSubItems = cell as? SubCellProtocol else { + continue + } + cellWithSubItems.deselectSelectedSubItems(animated: animated) + } + } + + open func viewWillHaveFirstAppearance(_ animated: Bool) { + // subclassers can override + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + if previousTraitCollection?.preferredContentSizeCategory != traitCollection.preferredContentSizeCategory { + contentSizeCategoryDidChange(nil) + } + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + coordinator.animate(alongsideTransition: { (context) in + let boundsChange = self.collectionView.bounds + guard self.layout.shouldInvalidateLayout(forBoundsChange: boundsChange) else { + return + } + let invalidationContext = self.layout.invalidationContext(forBoundsChange: boundsChange) + self.layout.invalidateLayout(with: invalidationContext) + }) + } + + // MARK: HintPresenting + + var hintController: HintController? + + // MARK: - UIScrollViewDelegate + + override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + super.scrollViewWillBeginDragging(scrollView) + hintController?.dismissHintDueToUserInteraction() + } + + // MARK: - Refresh Control + + final var isRefreshControlEnabled: Bool = false { + didSet { + if isRefreshControlEnabled { + let refreshControl = UIRefreshControl() + refreshControl.tintColor = theme.colors.refreshControlTint + refreshControl.layer.zPosition = -100 + refreshControl.addTarget(self, action: #selector(refreshControlActivated), for: .valueChanged) + collectionView.refreshControl = refreshControl + } else { + collectionView.refreshControl = nil + } + } + } + + var refreshStart: Date = Date() + @objc func refreshControlActivated() { + refreshStart = Date() + self.refresh() + } + + open func refresh() { + assert(false, "default implementation shouldn't be called") + self.endRefreshing() + } + + open func endRefreshing() { + let now = Date() + let timeInterval = 0.5 - now.timeIntervalSince(refreshStart) + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + timeInterval, execute: { + self.collectionView.refreshControl?.endRefreshing() + }) + } + + // MARK: - Empty State + + var emptyViewType: WMFEmptyViewType = .none + + final var isEmpty = true + final var showingEmptyViewType: WMFEmptyViewType? + final func updateEmptyState() { + let sectionCount = numberOfSections(in: collectionView) + + var isCurrentlyEmpty = true + for sectionIndex in 0.. 0 { + isCurrentlyEmpty = false + break + } + } + + guard isCurrentlyEmpty != isEmpty || showingEmptyViewType != emptyViewType else { + return + } + + isEmpty = isCurrentlyEmpty + + isEmptyDidChange() + } + + private var emptyViewFrame: CGRect { + let insets = scrollView?.contentInset ?? UIEdgeInsets.zero + let frame = view.bounds.inset(by: insets) + return frame + } + + open weak var emptyViewTarget: AnyObject? + open var emptyViewAction: Selector? + + open func isEmptyDidChange() { + if isEmpty { + wmf_showEmptyView(of: emptyViewType, target: emptyViewTarget, action: emptyViewAction, theme: theme, frame: emptyViewFrame) + showingEmptyViewType = emptyViewType + } else { + wmf_hideEmptyView() + showingEmptyViewType = nil + } + } + + override func scrollViewInsetsDidChange() { + super.scrollViewInsetsDidChange() + wmf_setEmptyViewFrame(emptyViewFrame) + } + + // MARK: - Themeable + + override func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.baseBackground + collectionView.backgroundColor = theme.colors.paperBackground + collectionView.indicatorStyle = theme.scrollIndicatorStyle + collectionView.reloadData() + wmf_applyTheme(toEmptyView: theme) + } + + + // MARK: - UICollectionViewDataSourcePrefetching + + private lazy var imageURLsCurrentlyBeingPrefetched: Set = { + return [] + }() + + open func imageURLsForItemAt(_ indexPath: IndexPath) -> Set? { + return nil + } + + func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) { + for indexPath in indexPaths { + guard let imageURLs = imageURLsForItemAt(indexPath) else { + continue + } + let imageURLsToPrefetch = imageURLs.subtracting(imageURLsCurrentlyBeingPrefetched) + // SINGLETONTODO + let imageController = MWKDataStore.shared().cacheController.imageCache + imageURLsCurrentlyBeingPrefetched.formUnion(imageURLsToPrefetch) + for imageURL in imageURLsToPrefetch { + imageController.prefetch(withURL: imageURL) { + self.imageURLsCurrentlyBeingPrefetched.remove(imageURL) + } + } + } + } + + // MARK: - Header + + var headerTitle: String? + var headerSubtitle: String? + + open func configure(header: CollectionViewHeader, forSectionAt sectionIndex: Int, layoutOnly: Bool) { + header.title = headerTitle + header.subtitle = headerSubtitle + header.style = .detail + header.apply(theme: theme) + } + + // MARK: - Footer + + var footerButtonTitle: String? + + open func configure(footer: CollectionViewFooter, forSectionAt sectionIndex: Int, layoutOnly: Bool) { + footer.buttonTitle = footerButtonTitle + footer.delegate = self + footer.apply(theme: theme) + } + + // MARK: - ColumnarCollectionViewLayoutDelegate + + func collectionView(_ collectionView: UICollectionView, estimatedHeightForHeaderInSection section: Int, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + var estimate = ColumnarCollectionViewLayoutHeightEstimate(precalculated: true, height: 0) + switch headerStyle { + case .exploreFeedDetail: + guard section == 0, headerTitle != nil else { + return estimate + } + case .sections: + guard self.collectionView(collectionView, numberOfItemsInSection: section) > 0 else { + return estimate + } + } + guard let placeholder = layoutManager.placeholder(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: CollectionViewHeader.identifier) as? CollectionViewHeader else { + return estimate + } + configure(header: placeholder, forSectionAt: section, layoutOnly: true) + estimate.height = placeholder.sizeThatFits(CGSize(width: columnWidth, height: UIView.noIntrinsicMetric), apply: false).height + estimate.precalculated = true + return estimate + } + + open func collectionView(_ collectionView: UICollectionView, estimatedHeightForFooterInSection section: Int, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + var estimate = ColumnarCollectionViewLayoutHeightEstimate(precalculated: true, height: 0) + guard footerButtonTitle != nil else { + return estimate + } + guard let placeholder = layoutManager.placeholder(forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: CollectionViewFooter.identifier) as? CollectionViewFooter else { + return estimate + } + configure(footer: placeholder, forSectionAt: section, layoutOnly: true) + estimate.height = placeholder.sizeThatFits(CGSize(width: columnWidth, height: UIView.noIntrinsicMetric), apply: false).height + estimate.precalculated = true + return estimate + } + + func collectionView(_ collectionView: UICollectionView, shouldShowFooterForSection section: Int) -> Bool { + return section == collectionView.numberOfSections - 1 + } + + open func collectionView(_ collectionView: UICollectionView, estimatedHeightForItemAt indexPath: IndexPath, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + return ColumnarCollectionViewLayoutHeightEstimate(precalculated: false, height: 0) + } + + func metrics(with size: CGSize, readableWidth: CGFloat, layoutMargins: UIEdgeInsets) -> ColumnarCollectionViewLayoutMetrics { + return ColumnarCollectionViewLayoutMetrics.tableViewMetrics(with: size, readableWidth: readableWidth, layoutMargins: layoutMargins) + } + + // MARK: - Event logging utiities + + var percentViewed: Double { + guard collectionView.contentSize.height > 0 else { + return 0 + } + return Double(((collectionView.contentOffset.y + collectionView.bounds.height) / collectionView.contentSize.height) * 100) + } + + var _maxViewed: Double = 0 + var maxViewed: Double { + return min(max(_maxViewed, percentViewed), 100) + } + + override func scrollViewDidScroll(_ scrollView: UIScrollView) { + super.scrollViewDidScroll(scrollView) + _maxViewed = max(_maxViewed, percentViewed) + } + + // MARK: - CollectionViewFooterDelegate + + func collectionViewFooterButtonWasPressed(_ collectionViewFooter: CollectionViewFooter) { + + } +} + +// MARK: - UICollectionViewDataSource +extension ColumnarCollectionViewController: UICollectionViewDataSource { + open func numberOfSections(in collectionView: UICollectionView) -> Int { + return 0 + } + + open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 0 + } + + open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + return collectionView.dequeueReusableCell(withReuseIdentifier: "", for: indexPath) + } + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + if kind == UICollectionView.elementKindSectionHeader { + let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CollectionViewHeader.identifier, for: indexPath) + guard let header = view as? CollectionViewHeader else { + return view + } + configure(header: header, forSectionAt: indexPath.section, layoutOnly: false) + return header + } else if kind == UICollectionView.elementKindSectionFooter { + let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CollectionViewFooter.identifier, for: indexPath) + guard let footer = view as? CollectionViewFooter else { + return view + } + configure(footer: footer, forSectionAt: indexPath.section, layoutOnly: false) + return footer + } + return UICollectionReusableView() + } +} + +extension ColumnarCollectionViewController: UICollectionViewDelegate { + +} + +extension ColumnarCollectionViewController { + func push(_ viewController: UIViewController, context: FeedFunnelContext?, index: Int?, animated: Bool) { + logFeedEventIfNeeded(for: context, index: index, pushedViewController: viewController) + push(viewController, animated: animated) + } + + func logFeedEventIfNeeded(for context: FeedFunnelContext?, index: Int?, pushedViewController: UIViewController) { + guard navigationController != nil, let viewControllers = navigationController?.viewControllers else { + return + } + let isFirstViewControllerExplore = viewControllers.first is ExploreViewController + let isPushedFromExplore = viewControllers.count == 1 && isFirstViewControllerExplore + let isPushedFromExploreDetail = viewControllers.count == 2 && isFirstViewControllerExplore + if isPushedFromExplore { + let isArticle = pushedViewController is ArticleViewController + if isArticle { + FeedFunnel.shared.logFeedCardReadingStarted(for: context, index: index) + } else { + FeedFunnel.shared.logFeedCardOpened(for: context) + } + } else if isPushedFromExploreDetail { + FeedFunnel.shared.logArticleInFeedDetailReadingStarted(for: context, index: index, maxViewed: maxViewed) + } + + } +} + +// MARK: - CollectionViewContextMenuShowing +extension ColumnarCollectionViewController { + func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { + guard let contextMenuCollectionVC = self as? CollectionViewContextMenuShowing, let vc = contextMenuCollectionVC.previewingViewController(for: indexPath, at: point) else { + return nil + } + let previewProvider: () -> UIViewController? = { + return vc + } + return UIContextMenuConfiguration(identifier: nil, previewProvider: previewProvider) { (suggestedActions) -> UIMenu? in + guard let previewActions = (vc as? ArticleViewController)?.contextMenuItems else { + return nil + } + return UIMenu(title: "", image: nil, identifier: nil, options: [], children: previewActions) + } + } + + func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { + + guard let previewedViewController = animator.previewViewController else { + assertionFailure("Should be able to find previewed VC") + return + } + animator.addCompletion { [weak self] in + (self as? CollectionViewContextMenuShowing)?.poppingIntoVCCompletion() + previewedViewController.wmf_removePeekableChildViewControllers() + self?.push(previewedViewController, animated: true) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ColumnarCollectionViewControllerLayoutCache.swift b/Apps/Wikipedia/Wikipedia/Code/ColumnarCollectionViewControllerLayoutCache.swift new file mode 100644 index 0000000..39f56f0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ColumnarCollectionViewControllerLayoutCache.swift @@ -0,0 +1,78 @@ +private extension CGFloat { + var roundedColumnWidth: Int { + return Int(self*100) + } +} +class ColumnarCollectionViewControllerLayoutCache { + private var cachedHeights: [String: [Int: CGFloat]] = [:] + private var cacheKeysByGroupKey: [WMFInMemoryURLKey: Set] = [:] + private var cacheKeysByArticleKey: [WMFInMemoryURLKey: Set] = [:] + private var groupKeysByArticleKey: [WMFInMemoryURLKey: Set] = [:] + + private func cacheKeyForCellWithIdentifier(_ identifier: String, userInfo: String) -> String { + return "\(identifier)-\(userInfo)" + } + + public func setHeight(_ height: CGFloat, forCellWithIdentifier identifier: String, columnWidth: CGFloat, groupKey: WMFInMemoryURLKey? = nil, articleKey: WMFInMemoryURLKey? = nil, userInfo: String) { + let cacheKey = cacheKeyForCellWithIdentifier(identifier, userInfo: userInfo) + if let groupKey = groupKey { + cacheKeysByGroupKey[groupKey, default: []].insert(cacheKey) + } + if let articleKey = articleKey { + cacheKeysByArticleKey[articleKey, default: []].insert(cacheKey) + if let groupKey = groupKey { + groupKeysByArticleKey[articleKey, default: []].insert(groupKey) + } + } + cachedHeights[cacheKey, default: [:]][columnWidth.roundedColumnWidth] = height + } + + public func cachedHeightForCellWithIdentifier(_ identifier: String, columnWidth: CGFloat, userInfo: String) -> CGFloat? { + let cacheKey = cacheKeyForCellWithIdentifier(identifier, userInfo: userInfo) + return cachedHeights[cacheKey]?[columnWidth.roundedColumnWidth] + } + + public func removeCachedHeightsForCellWithIdentifier(_ identifier: String, userInfo: String) { + let cacheKey = cacheKeyForCellWithIdentifier(identifier, userInfo: userInfo) + cachedHeights.removeValue(forKey: cacheKey) + } + + public func reset() { + cachedHeights.removeAll(keepingCapacity: true) + cacheKeysByArticleKey.removeAll(keepingCapacity: true) + cacheKeysByGroupKey.removeAll(keepingCapacity: true) + } + + @discardableResult public func invalidateArticleKey(_ articleKey: WMFInMemoryURLKey?) -> Bool { + guard let articleKey = articleKey else { + return false + } + + if let cacheKeys = cacheKeysByArticleKey[articleKey] { + for cacheKey in cacheKeys { + cachedHeights.removeValue(forKey: cacheKey) + } + cacheKeysByArticleKey.removeValue(forKey: articleKey) + } + + guard let groupKeys = groupKeysByArticleKey[articleKey] else { + return false + } + + for groupKey in groupKeys { + invalidateGroupKey(groupKey) + } + + return true + } + + public func invalidateGroupKey(_ groupKey: WMFInMemoryURLKey?) { + guard let groupKey = groupKey, let cacheKeys = cacheKeysByGroupKey[groupKey] else { + return + } + for cacheKey in cacheKeys { + cachedHeights.removeValue(forKey: cacheKey) + } + cacheKeysByGroupKey.removeValue(forKey: groupKey) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ColumnarCollectionViewLayoutManager.swift b/Apps/Wikipedia/Wikipedia/Code/ColumnarCollectionViewLayoutManager.swift new file mode 100644 index 0000000..17ba427 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ColumnarCollectionViewLayoutManager.swift @@ -0,0 +1,84 @@ +import UIKit + +class ColumnarCollectionViewLayoutManager { + private var placeholders: [String:UICollectionReusableView] = [:] + + weak var view: UIView! + weak var collectionView: UICollectionView! + + required init(view: UIView, collectionView: UICollectionView) { + self.view = view + self.collectionView = collectionView + } + + // MARK: - Cell & View Registration + + final private func placeholderForIdentifier(_ identifier: String) -> UICollectionReusableView? { + let view = placeholders[identifier] + view?.prepareForReuse() + return view + } + + final public func placeholder(forCellWithReuseIdentifier identifier: String) -> UICollectionViewCell? { + return placeholderForIdentifier(identifier) as? UICollectionViewCell + } + + final public func placeholder(forSupplementaryViewOfKind elementKind: String, withReuseIdentifier identifier: String) -> UICollectionReusableView? { + return placeholderForIdentifier("\(elementKind)-\(identifier)") + } + + @objc(registerCellClass:forCellWithReuseIdentifier:addPlaceholder:) + final func register(_ cellClass: Swift.AnyClass?, forCellWithReuseIdentifier identifier: String, addPlaceholder: Bool) { + collectionView.register(cellClass, forCellWithReuseIdentifier: identifier) + guard addPlaceholder else { + return + } + guard let cellClass = cellClass as? UICollectionViewCell.Type else { + return + } + let cell = cellClass.init(frame: view.bounds) + cell.isHidden = true + view.insertSubview(cell, at: 0) // so that the trait collections are updated + placeholders[identifier] = cell + } + + @objc(registerNib:forCellWithReuseIdentifier:) + final func register(_ nib: UINib?, forCellWithReuseIdentifier identifier: String) { + collectionView.register(nib, forCellWithReuseIdentifier: identifier) + guard let cell = nib?.instantiate(withOwner: nil, options: nil).first as? UICollectionViewCell else { + return + } + cell.isHidden = true + view.insertSubview(cell, at: 0) // so that the trait collections are updated + placeholders[identifier] = cell + } + + @objc(registerViewClass:forSupplementaryViewOfKind:withReuseIdentifier:addPlaceholder:) + final func register(_ viewClass: Swift.AnyClass?, forSupplementaryViewOfKind elementKind: String, withReuseIdentifier identifier: String, addPlaceholder: Bool) { + collectionView.register(viewClass, forSupplementaryViewOfKind: elementKind, withReuseIdentifier: identifier) + guard addPlaceholder else { + return + } + guard let viewClass = viewClass as? UICollectionReusableView.Type else { + return + } + let reusableView = viewClass.init(frame: view.bounds) + reusableView.isHidden = true + view.insertSubview(reusableView, at: 0) // so that the trait collections are updated + placeholders["\(elementKind)-\(identifier)"] = reusableView + } + + @objc(registerNib:forSupplementaryViewOfKind:withReuseIdentifier:addPlaceholder:) + final func register(_ nib: UINib?, forSupplementaryViewOfKind elementKind: String, withReuseIdentifier identifier: String, addPlaceholder: Bool) { + collectionView.register(nib, forSupplementaryViewOfKind: elementKind, withReuseIdentifier: identifier) + guard addPlaceholder else { + return + } + guard let reusableView = nib?.instantiate(withOwner: nil, options: nil).first as? UICollectionReusableView else { + return + } + reusableView.isHidden = true + view.insertSubview(reusableView, at: 0) // so that the trait collections are updated + placeholders["\(elementKind)-\(identifier)"] = reusableView + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ContextualHighlightEditToolbarView.swift b/Apps/Wikipedia/Wikipedia/Code/ContextualHighlightEditToolbarView.swift new file mode 100644 index 0000000..c4c81e7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ContextualHighlightEditToolbarView.swift @@ -0,0 +1,44 @@ +class ContextualHighlightEditToolbarView: EditToolbarView { + @IBOutlet private weak var stackView: UIStackView! + @IBOutlet private weak var showMoreButton: TextFormattingButton! + + override func awakeFromNib() { + super.awakeFromNib() + stackView.isLayoutMarginsRelativeArrangement = true + stackView.layoutMargins = UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 0) + + showMoreButton.tintColorKeyPath = \Theme.colors.link + } + + @IBAction private func toggleBoldSelection(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapBold() + } + + @IBAction private func toggleItalicSelection(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapItalics() + } + + @IBAction private func formatHeader(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapTextStyleFormatting() + } + + @IBAction private func toggleReferenceSelection(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapReference() + } + + @IBAction private func toggleAnchorSelection(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapLink() + } + + @IBAction private func toggleTemplate(sender: UIButton) { + delegate?.textFormattingProvidingDidTapTemplate() + } + + @IBAction private func clearFormatting(sender: UIButton) { + delegate?.textFormattingProvidingDidTapClearFormatting() + } + + @IBAction private func formatText(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapTextFormatting() + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ContextualHighlightEditToolbarView.xib b/Apps/Wikipedia/Wikipedia/Code/ContextualHighlightEditToolbarView.xib new file mode 100644 index 0000000..149d5f6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ContextualHighlightEditToolbarView.xib @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/Controllers/ArticleDescriptionControlling.swift b/Apps/Wikipedia/Wikipedia/Code/Controllers/ArticleDescriptionControlling.swift new file mode 100644 index 0000000..83675eb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Controllers/ArticleDescriptionControlling.swift @@ -0,0 +1,43 @@ +import Foundation +import WMF + +struct ArticleDescriptionWarningTypes: OptionSet { + let rawValue: Int + + static let length = ArticleDescriptionWarningTypes(rawValue: 1 << 0) + static let casing = ArticleDescriptionWarningTypes(rawValue: 1 << 1) +} + +struct ArticleDescriptionPublishResult { + let newRevisionID: UInt64? + let newDescription: String +} + +protocol ArticleDescriptionControlling { + var descriptionSource: ArticleDescriptionSource { get } + var article: WMFArticle { get } + var articleLanguageCode: String { get } + func publishDescription(_ description: String, completion: @escaping (Result) -> Void) + func currentDescription(completion: @escaping (String?, MediaWikiAPIDisplayError?) -> Void) + func errorCodeFromError(_ error: Error) -> String + func learnMoreViewControllerWithTheme(_ theme: Theme) -> UIViewController? + func warningTypesForDescription(_ description: String?) -> ArticleDescriptionWarningTypes +} + +extension ArticleDescriptionControlling { + var articleDisplayTitle: String? { return article.displayTitle } + var descriptionMaxLength: Int { return 90 } + + func descriptionIsTooLong(_ description: String?) -> Bool { + let isDescriptionLong = (description?.count ?? 0) > descriptionMaxLength + return isDescriptionLong + } + + func descriptionIsUppercase(_ description: String?) -> Bool { + guard let firstCharacter = description?.first else { + return false + } + + return firstCharacter.isLetter && firstCharacter.isUppercase + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Controllers/ShortDescriptionController.swift b/Apps/Wikipedia/Wikipedia/Code/Controllers/ShortDescriptionController.swift new file mode 100644 index 0000000..b0e9a98 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Controllers/ShortDescriptionController.swift @@ -0,0 +1,262 @@ +import Foundation +import WMF + +enum ShortDescriptionControllerError: Error { + case failureConstructingRegexExpression + case missingSelf +} + +protocol ShortDescriptionControllerDelegate: AnyObject { + func currentDescription(completion: @escaping (String?) -> Void) +} + +class ShortDescriptionController: ArticleDescriptionControlling { + + private let sectionFetcher: SectionFetcher + private let sectionUploader: WikiTextSectionUploader + + private let articleURL: URL + let article: WMFArticle + let articleLanguageCode: String + + private let sectionID: Int = 0 + + let descriptionSource: ArticleDescriptionSource + private weak var delegate: ShortDescriptionControllerDelegate? + + fileprivate static let templateRegex = "(\\{\\{\\s*[sS]hort description\\|(?:1=)?)([^}|]+)([^}]*\\}\\})" + +// MARK: Public + + /// Inits for use of updating EN Wikipedia article description + /// - Parameters: + /// - sectionFetcher: section fetcher that fetches the first section of wikitext. Injectable for unit tests. + /// - sectionUploader: section uploader that uploads the new section wikitext. Injectable for unit tests. + /// - article: WMFArticle from ArticleViewController + /// - articleLanguageCode: Language code of article that we want to update (from ArticleViewController) + /// - articleURL: URL of article that we want to update (from ArticleViewController) + /// - descriptionSource: ArticleDescriptionSource determined via .edit action across ArticleViewController js bridge + /// - delegate: Delegate that can extract the current description from the article content + init(sectionFetcher: SectionFetcher = SectionFetcher(), sectionUploader: WikiTextSectionUploader = WikiTextSectionUploader(), article: WMFArticle, articleLanguageCode: String, articleURL: URL, descriptionSource: ArticleDescriptionSource, delegate: ShortDescriptionControllerDelegate) { + self.sectionFetcher = sectionFetcher + self.sectionUploader = sectionUploader + self.article = article + self.articleURL = articleURL + self.articleLanguageCode = articleLanguageCode + self.descriptionSource = descriptionSource + self.delegate = delegate + } + + /// Publishes a new article description to article wikitext. Detects the existence of the {{Short description}} template in the first section and replaces the text within or prepends the section with the new template. + /// - Parameters: + /// - description: The new description to insert into the wikitext + /// - completion: Completion called when updated section upload call is successful. + func publishDescription(_ description: String, completion: @escaping (Result) -> Void) { + + sectionFetcher.fetchSection(with: sectionID, articleURL: articleURL) { [weak self] (result) in + + guard let self = self else { + completion(.failure(ShortDescriptionControllerError.missingSelf)) + return + } + + switch result { + case .success(let response): + + let wikitext = response.wikitext + let revisionID = response.revisionID + + self.uploadNewDescriptionToWikitext(wikitext, baseRevisionID: revisionID, newDescription: description, completion: completion) + + case .failure(let error): + completion(.failure(error)) + } + } + } + + func currentDescription(completion: @escaping (String?, MediaWikiAPIDisplayError?) -> Void) { + + let group = DispatchGroup() + + var blockedError: MediaWikiAPIDisplayError? + var currentDescription: String? + + // Populate current description + group.enter() + delegate?.currentDescription(completion: { description in + currentDescription = description + group.leave() + }) + + // Populate blocked error + group.enter() + sectionFetcher.fetchSection(with: sectionID, articleURL: articleURL) { (result) in + + defer { + group.leave() + } + + switch result { + case .success(let result): + blockedError = result.apiError + case .failure: + break + } + } + + group.notify(queue: DispatchQueue.main) { + completion(currentDescription, blockedError) + } + } + + func errorCodeFromError(_ error: Error) -> String { + let errorText = "\((error as NSError).domain)-\((error as NSError).code)" + return errorText + } + + func learnMoreViewControllerWithTheme(_ theme: Theme) -> UIViewController? { + guard let url = URL(string: "https://en.wikipedia.org/wiki/Wikipedia:Short_description") else { + return nil + } + + return SinglePageWebViewController(url: url, theme: theme, doesUseSimpleNavigationBar: true) + } + + func warningTypesForDescription(_ description: String?) -> ArticleDescriptionWarningTypes { + return descriptionIsTooLong(description) ? [.length] : [] + } +} + +// MARK: Private helpers + +private extension ShortDescriptionController { + + func uploadNewDescriptionToWikitext(_ wikitext: String, baseRevisionID: Int, newDescription: String, completion: @escaping (Result) -> Void) { + + do { + guard try wikitext.containsShortDescription() else { + + prependNewDescriptionToWikitextAndUpload(wikitext, baseRevisionID: baseRevisionID, newDescription: newDescription, completion: completion) + return + } + + replaceDescriptionInWikitextAndUpload(wikitext, newDescription: newDescription, baseRevisionID: baseRevisionID, completion: completion) + + } catch let error { + completion(.failure(error)) + } + } + + func prependNewDescriptionToWikitextAndUpload(_ wikitext: String, baseRevisionID: Int, newDescription: String, completion: @escaping (Result) -> Void) { + + let newTemplateToPrepend = "{{Short description|\(newDescription)}}\n" + + sectionUploader.prepend(toSectionID: "\(sectionID)", text: newTemplateToPrepend, forArticleURL: articleURL, isMinorEdit: true, baseRevID: baseRevisionID as NSNumber, completion: { [weak self] (result, error) in + + guard let self = self else { + completion(.failure(ShortDescriptionControllerError.missingSelf)) + return + } + + if let error = error { + completion(.failure(error)) + return + } + + guard let result = result, + let revisionID = self.revisionIDFromResult(result: result) else { + completion(.failure(RequestError.unexpectedResponse)) + return + } + + completion(.success(ArticleDescriptionPublishResult(newRevisionID: revisionID, newDescription: newDescription))) + }) + } + + func replaceDescriptionInWikitextAndUpload(_ wikitext: String, newDescription: String, baseRevisionID: Int, completion: @escaping (Result) -> Void) { + + do { + + let updatedWikitext = try wikitext.replacingShortDescription(with: newDescription) + + sectionUploader.uploadWikiText(updatedWikitext, forArticleURL: articleURL, section: "\(sectionID)", summary: nil, isMinorEdit: true, addToWatchlist: false, baseRevID: baseRevisionID as NSNumber, captchaId: nil, captchaWord: nil, completion: { [weak self] (result, error) in + + guard let self = self else { + completion(.failure(ShortDescriptionControllerError.missingSelf)) + return + } + + if let error = error { + completion(.failure(error)) + return + } + + guard let result = result, + let revisionID = self.revisionIDFromResult(result: result) else { + completion(.failure(RequestError.unexpectedResponse)) + return + } + + completion(.success(ArticleDescriptionPublishResult(newRevisionID: revisionID, newDescription: newDescription))) + }) + + } catch let error { + completion(.failure(error)) + } + } + + func revisionIDFromResult(result: [AnyHashable: Any]) -> UInt64? { + guard let fetchedData = result as? [String: Any], + let newRevID = fetchedData["newrevid"] as? UInt64 else { + assertionFailure("Could not extract revisionID as UInt64") + return nil + } + + return newRevID + } +} + +private extension String { + + /// Detects if the message receiver contains a {{short description}} template or not + /// - Throws: If short description NSRegularExpression fails to instantiate + /// - Returns: Boolean indicating whether the message receiver contains a {{short description}} template or not + func containsShortDescription() throws -> Bool { + + guard let regex = try? NSRegularExpression(pattern: ShortDescriptionController.templateRegex) else { + throw ShortDescriptionControllerError.failureConstructingRegexExpression + } + + let matches = regex.matches(in: self, range: NSRange(self.startIndex..., in: self)) + + return matches.count > 0 + } + + /// Replaces the {{short description}} template value in message receiver with the new description. + /// Assumes the {{short description}} template already exists. Does not insert a {{short description}} template if it doesn't exist. + /// - Parameter newShortDescription: new short description value to replace existing with + /// - Throws: If short description NSRegularExpression fails to instantiate + /// - Returns: Message receiver with short description template within replaced. + func replacingShortDescription(with newShortDescription: String) throws -> String { + + guard let regex = try? NSRegularExpression(pattern: ShortDescriptionController.templateRegex) else { + throw ShortDescriptionControllerError.failureConstructingRegexExpression + } + + return regex.stringByReplacingMatches(in: self, range: NSRange(self.startIndex..., in: self), withTemplate: "$1\(newShortDescription)$3") + } +} + +#if TEST + +extension String { + func testContainsShortDescription() throws -> Bool { + return try containsShortDescription() + } + + func testReplacingShortDescription(with newShortDescription: String) throws -> String { + return try replacingShortDescription(with: newShortDescription) + } +} + +#endif diff --git a/Apps/Wikipedia/Wikipedia/Code/Controllers/WikidataDescriptionController.swift b/Apps/Wikipedia/Wikipedia/Code/Controllers/WikidataDescriptionController.swift new file mode 100644 index 0000000..8d86b24 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Controllers/WikidataDescriptionController.swift @@ -0,0 +1,82 @@ +import Foundation +import WMF + +class WikidataDescriptionController: ArticleDescriptionControlling { + + private let fetcher: WikidataFetcher + private let wikidataDescription: String? + private let wikiDataID: String + let article: WMFArticle + let articleLanguageCode: String + let descriptionSource: ArticleDescriptionSource + + init?(article: WMFArticle, articleLanguageCode: String, descriptionSource: ArticleDescriptionSource, fetcher: WikidataFetcher = WikidataFetcher()) { + self.fetcher = fetcher + self.wikidataDescription = article.wikidataDescription + self.article = article + self.articleLanguageCode = articleLanguageCode + self.descriptionSource = descriptionSource + + guard let wikiDataID = article.wikidataID else { + return nil + } + + self.wikiDataID = wikiDataID + } + + func currentDescription(completion: @escaping (String?, MediaWikiAPIDisplayError?) -> Void) { + + fetcher.wikidataBlockedInfo(forEntity: wikiDataID) { blockedError in + DispatchQueue.main.async { + completion(self.wikidataDescription, blockedError) + } + } + } + + func publishDescription(_ description: String, completion: @escaping (Result) -> Void) { + + fetcher.publish(newWikidataDescription: description, from: descriptionSource, forWikidataID: wikiDataID, languageCode: articleLanguageCode) { (error) in + if let error = error { + completion(.failure(error)) + return + } + + completion(.success(ArticleDescriptionPublishResult(newRevisionID: nil, newDescription: description))) + } + } + + func errorCodeFromError(_ error: Error) -> String { + var apiErrorCode: String? + if let publishError = error as? WikidataFetcher.WikidataPublishingError { + switch publishError { + case .apiOther(let error): + apiErrorCode = error.code + case .apiBlocked(let blockedError): + apiErrorCode = blockedError.code + default: + break + } + } + let errorText = apiErrorCode ?? "\((error as NSError).domain)-\((error as NSError).code)" + return errorText + } + + func learnMoreViewControllerWithTheme(_ theme: Theme) -> UIViewController? { + return DescriptionHelpViewController.init(theme: theme) + } + + func warningTypesForDescription(_ description: String?) -> ArticleDescriptionWarningTypes { + + var warningTypes: ArticleDescriptionWarningTypes = [] + + if descriptionIsTooLong(description) { + warningTypes.insert(.length) + } + + if descriptionIsUppercase(description) { + warningTypes.insert(.casing) + } + + return warningTypes + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/CreateAccountFunnel.h b/Apps/Wikipedia/Wikipedia/Code/CreateAccountFunnel.h new file mode 100644 index 0000000..344b56c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CreateAccountFunnel.h @@ -0,0 +1,13 @@ +@import WMF.EventLoggingFunnel; + +@interface CreateAccountFunnel : EventLoggingFunnel + +@property NSString *createAccountSessionToken; + +- (void)logStartFromLogin:(NSString *)loginSessionToken; +- (void)logSuccess; +- (void)logCaptchaShown; +- (void)logCaptchaFailure; +- (void)logError:(NSString *)code; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/CreateAccountFunnel.m b/Apps/Wikipedia/Wikipedia/Code/CreateAccountFunnel.m new file mode 100644 index 0000000..48cac5f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CreateAccountFunnel.m @@ -0,0 +1,50 @@ +#import "CreateAccountFunnel.h" + +static NSString *const kAppInstallIdKey = @"appInstallID"; +static NSString *const kTimestampKey = @"ts"; + +@implementation CreateAccountFunnel + +- (id)init { + // https://meta.wikimedia.org/wiki/Schema:MobileWikiAppCreateAccount + self = [self initWithSchema:@"MobileWikiAppCreateAccount" + version:17836914]; + if (self) { + self.createAccountSessionToken = [self singleUseUUID]; + } + return self; +} + +- (NSDictionary *)preprocessData:(NSDictionary *)eventData { + NSMutableDictionary *dict = [eventData mutableCopy]; + dict[@"sessionToken"] = self.createAccountSessionToken; + dict[kAppInstallIdKey] = self.appInstallID; + dict[kTimestampKey] = self.timestamp; + return [NSDictionary dictionaryWithDictionary:dict]; +} + +#pragma mark - CreateAccountFunnel methods + +- (void)logStartFromLogin:(NSString *)loginSessionToken { + [self log:@{@"action": @"start", + @"loginSessionToken": (loginSessionToken ? loginSessionToken : @"")}]; +} + +- (void)logSuccess { + [self log:@{@"action": @"success"}]; +} + +- (void)logCaptchaShown { + [self log:@{@"action": @"captchaShown"}]; +} + +- (void)logCaptchaFailure { + [self log:@{@"action": @"captchaFailure"}]; +} + +- (void)logError:(NSString *)code { + [self log:@{@"action": @"error", + @"errorText": (code ? code : @"")}]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/CreateNewReadingListButtonView.swift b/Apps/Wikipedia/Wikipedia/Code/CreateNewReadingListButtonView.swift new file mode 100644 index 0000000..79c0d9a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CreateNewReadingListButtonView.swift @@ -0,0 +1,37 @@ +import UIKit + +public class CreateNewReadingListButtonView: UIView { + @IBOutlet weak var button: AlignedImageButton! + @IBOutlet private weak var separator: UIView! + + public override func awakeFromNib() { + super.awakeFromNib() + button.setImage(#imageLiteral(resourceName: "plus"), for: .normal) + updateFonts() + button.horizontalSpacing = 7 + } + + public var title: String? { + didSet { + button.setTitle(title, for: .normal) + } + } + + public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + button.titleLabel?.font = UIFont.wmf_font(.semiboldBody, compatibleWithTraitCollection: traitCollection) + } + +} + +extension CreateNewReadingListButtonView: Themeable { + public func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + button.tintColor = theme.colors.link + separator.backgroundColor = theme.colors.border + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/CreateNewReadingListButtonView.xib b/Apps/Wikipedia/Wikipedia/Code/CreateNewReadingListButtonView.xib new file mode 100644 index 0000000..7d70db2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CreateNewReadingListButtonView.xib @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/CreateReadingListViewController.swift b/Apps/Wikipedia/Wikipedia/Code/CreateReadingListViewController.swift new file mode 100644 index 0000000..d5ac0b3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CreateReadingListViewController.swift @@ -0,0 +1,398 @@ +import UIKit +import WMF + +struct ImportedReadingList: Codable { + let name: String? + let description: String? + let list: [String: [Int]] +} + +enum ImportReadingListError: Error { + case failureDecodingPayload + case failureFetchingPageURLs + case failureFetchingArticleObjects + case missingDataStore + case missingArticles +} + +protocol CreateReadingListDelegate: NSObjectProtocol { + func createReadingListViewController(_ createReadingListViewController: CreateReadingListViewController, didCreateReadingListWith name: String, description: String?, articles: [WMFArticle]) +} + +class CreateReadingListViewController: WMFScrollViewController, UITextFieldDelegate { + + @IBOutlet weak var readingListNameLabel: UILabel! + @IBOutlet weak var descriptionLabel: UILabel! + @IBOutlet weak var readingListNameErrorLabel: UILabel! + @IBOutlet weak var readingListNameTextField: ThemeableTextField! + @IBOutlet weak var descriptionTextField: ThemeableTextField! + + @IBOutlet weak var createReadingListButton: WMFAuthButton! + + fileprivate var theme: Theme = Theme.standard + fileprivate var articles: [WMFArticle] + public let moveFromReadingList: ReadingList? + + weak var delegate: CreateReadingListDelegate? + + // Import shared reading list properties + private let encodedPageIds: String? + private var importedReadingList: ImportedReadingList? + private let dataStore: MWKDataStore? + private let pageIdsFetcher = PageIDToURLFetcher() + + var isInImportingMode: Bool { + encodedPageIds != nil + } + + lazy var importLoadingView: UIView = { + let view = UIView(frame: .zero) + view.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(importSpinner) + view.backgroundColor = theme.colors.paperBackground + NSLayoutConstraint.activate([ + importSpinner.centerXAnchor.constraint(equalTo: view.centerXAnchor), + importSpinner.centerYAnchor.constraint(equalTo: view.centerYAnchor) + ]) + view.isHidden = true + return view + }() + + lazy var importSpinner: UIActivityIndicatorView = { + let activityIndicator = UIActivityIndicatorView(style: .medium) + activityIndicator.translatesAutoresizingMaskIntoConstraints = false + activityIndicator.hidesWhenStopped = true + activityIndicator.color = theme.colors.primaryText + return activityIndicator + }() + + // MARK: Lifecycle + + init(theme: Theme, articles: [WMFArticle], moveFromReadingList: ReadingList? = nil, encodedPageIds: String? = nil, dataStore: MWKDataStore? = nil) { + self.theme = theme + self.articles = articles + self.moveFromReadingList = moveFromReadingList + self.encodedPageIds = encodedPageIds + self.dataStore = dataStore + super.init(nibName: "CreateReadingListViewController", bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + apply(theme: theme) + readingListNameTextField.delegate = self + descriptionTextField.delegate = self + descriptionTextField.returnKeyType = .next + readingListNameTextField.returnKeyType = .next + readingListNameTextField.enablesReturnKeyAutomatically = true + + navigationItem.title = CommonStrings.createNewListTitle + readingListNameLabel.text = WMFLocalizedString("reading-list-create-new-list-reading-list-name", value: "Reading list name", comment: "Title for label above text field for entering new list name.") + descriptionLabel.text = WMFLocalizedString("reading-list-create-new-list-description", value: "Description", comment: "Title for label above text field for entering new list description.") + readingListNameTextField.placeholder = WMFLocalizedString("reading-list-new-list-name-placeholder", value: "reading list title", comment: "Placeholder text appearing in text field for entering new list name") + descriptionTextField.placeholder = WMFLocalizedString("reading-list-new-list-description-placeholder", value: "optional short description", comment: "Placeholder text appearing in text field for entering new list description") + createReadingListButton.setTitle(WMFLocalizedString("reading-list-create-new-list-button-title", value: "Create reading list", comment: "Title for button allowing the user to create a new reading list."), for: .normal) + + createReadingListButton.isEnabled = false + + if isInImportingMode { + setupForImportingReadingList() + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if !isInImportingMode { + readingListNameTextField.becomeFirstResponder() + } + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + view.endEditing(false) + } + +// MARK: Public + + func handleReadingListNameError(_ error: ReadingListError) { + readingListNameTextField.textColor = theme.colors.error + readingListNameErrorLabel.isHidden = false + readingListNameErrorLabel.text = error.localizedDescription + createReadingListButton.isEnabled = false + } + +// MARK: Actions + + @objc func closeButtonTapped(_ sender: UIButton) { + + if isInImportingMode { + ReadingListsFunnel.shared.logCancelImport() + } + + navigationController?.dismiss(animated: true) + } + + @IBAction func createReadingListButtonPressed() { + guard !isReadingListNameFieldEmpty, let trimmedName = readingListNameTextField.text?.trimmingCharacters(in: .whitespaces) else { + return + } + let trimmedDescription = descriptionTextField.text?.trimmingCharacters(in: .whitespaces) + delegate?.createReadingListViewController(self, didCreateReadingListWith: trimmedName, description: trimmedDescription, articles: articles) + } + + override func accessibilityPerformEscape() -> Bool { + dismiss(animated: true) + return true + } + +// MARK: Private + + private func setupForImportingReadingList() { + + guard isInImportingMode else { + return + } + + self.title = WMFLocalizedString("import-shared-reading-list-title", value: "Import shared reading list", comment: "Title of screen that imports a shared reading list.") + let closeButton = UIBarButtonItem.wmf_buttonType(WMFButtonType.X, target: self, action: #selector(closeButtonTapped(_:))) + navigationItem.leftBarButtonItem = closeButton + + view.wmf_addSubviewWithConstraintsToEdges(importLoadingView) + + if let encodedPageIds = encodedPageIds { + + importSpinner.startAnimating() + importLoadingView.isHidden = false + + articlesFromEncodedPageIds(encodedPageIds) { [weak self] result in + + guard let self = self else { + return + } + + self.importSpinner.stopAnimating() + self.importLoadingView.isHidden = true + + switch result { + case .success(let articles): + self.articles = articles + self.readingListNameTextField.text = WMFLocalizedString("import-shared-reading-list-default-title", value: "My Reading List", comment: "Default title of a reading list imported through a shared link.") + self.createReadingListButton.isEnabled = !self.isReadingListNameFieldEmpty && self.readingListNameErrorLabel.isHidden + case .failure(let error): + self.readingListNameTextField.isEnabled = false + self.descriptionTextField.isEnabled = false + self.createReadingListButton.isEnabled = false + WMFAlertManager.sharedInstance.showErrorAlert(error, sticky: true, dismissPreviousAlerts: true, tapCallBack: nil) + } + } + } + } + + private func hideReadingListError() { + guard !readingListNameErrorLabel.isHidden else { + return + } + readingListNameErrorLabel.isHidden = true + readingListNameTextField.textColor = theme.colors.primaryText + } + + private var shouldEnableCreateReadingListButton: Bool { + return (!isReadingListNameFieldEmpty && readingListNameTextField.isFirstResponder) && readingListNameErrorLabel.isHidden + } + + // MARK: - UITextFieldDelegate + + fileprivate var isReadingListNameFieldEmpty: Bool { + return !readingListNameTextField.wmf_hasNonWhitespaceText + } + + fileprivate var isDescriptionFieldEmpty: Bool { + return !descriptionTextField.wmf_hasNonWhitespaceText + } + + @IBAction func textFieldDidChange(_ textField: UITextField) { + if readingListNameTextField.isFirstResponder { + hideReadingListError() + } + createReadingListButton.isEnabled = !isReadingListNameFieldEmpty && readingListNameErrorLabel.isHidden + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + guard !descriptionTextField.isFirstResponder else { + readingListNameTextField.becomeFirstResponder() + return true + } + if readingListNameTextField.isFirstResponder { + descriptionTextField.becomeFirstResponder() + } + return true + } + + func textFieldShouldClear(_ textField: UITextField) -> Bool { + if readingListNameTextField.isFirstResponder { + hideReadingListError() + } + createReadingListButton.isEnabled = !isReadingListNameFieldEmpty && !readingListNameTextField.isFirstResponder && readingListNameErrorLabel.isHidden + return true + } +} + +// MARK: Themeable + +extension CreateReadingListViewController: Themeable { + func apply(theme: Theme) { + self.theme = theme + + guard viewIfLoaded != nil else { + return + } + + view.backgroundColor = theme.colors.paperBackground + view.tintColor = theme.colors.link + + readingListNameTextField.apply(theme: theme) + descriptionTextField.apply(theme: theme) + + readingListNameLabel.textColor = theme.colors.secondaryText + descriptionLabel.textColor = theme.colors.secondaryText + readingListNameErrorLabel.textColor = theme.colors.error + + createReadingListButton.apply(theme: theme) + + } +} + +// MARK: Importing Reading Lists + +private extension CreateReadingListViewController { + + func articlesFromEncodedPageIds(_ encodedPageIds: String, completion: @escaping (Result<[WMFArticle], Error>) -> Void) { + + guard let importedReadingList = decodedReadingListFromEncodedPageIds(encodedPageIds) else { + completion(.failure(ImportReadingListError.failureDecodingPayload)) + return + } + + self.importedReadingList = importedReadingList + + var loggingArticleCount = 0 + for (_, value) in importedReadingList.list { + loggingArticleCount = loggingArticleCount + value.count + } + + ReadingListsFunnel.shared.logStartImport(articlesCount: loggingArticleCount) + + pageURLsFromImportedReadingList(importedReadingList) { [weak self] result in + switch result { + case .success(let urls): + self?.articleObjectsFromArticleURLs(urls, completion: completion) + case .failure(let error): + completion(.failure(error)) + } + } + } + + func decodedReadingListFromEncodedPageIds(_ encodedPageIds: String) -> ImportedReadingList? { + guard let data = Data(base64Encoded: encodedPageIds), + let result = try? JSONDecoder().decode(ImportedReadingList.self, from: data) else { + return nil + } + + return result + } + + func pageURLsFromImportedReadingList(_ importedReadingList: ImportedReadingList, completion: @escaping ((Result<[URL], Error>) -> Void)) { + + // https://phabricator.wikimedia.org/T316822#8366987 + // Has the format: + // { + // "en":[59874,31883,24868,14381], + // "ru":[59874,31883,24868,14381] + // } + let listDict = importedReadingList.list + + // Turn into format: + // { + // https://en.wikipedia.org: [59874,31883,24868,14381], + // https://ru.wikipedia.org: [59874,31883,24868,14381] + // } + var siteURLDict: [URL: [Int]] = [:] + for (key, value) in listDict { + if let siteURL = NSURL.wmf_URL(withDefaultSiteAndLanguageCode: key) { + siteURLDict[siteURL] = value + } + } + + // Fetch page URLs for each site, combine into single array of page URLs + let group = DispatchGroup() + var finalPageURLs: [URL] = [] + var errors: [Error] = [] + for (key, value) in siteURLDict { + group.enter() + pageIdsFetcher.fetchPageURLs(key, pageIDs: value) { error in + DispatchQueue.main.async { + errors.append(error) + group.leave() + } + } success: { urls in + DispatchQueue.main.async { + finalPageURLs.append(contentsOf: urls) + group.leave() + } + } + } + + group.notify(queue: .main) { + + if let error = errors.first { + completion(.failure(error)) + return + } + + guard !finalPageURLs.isEmpty else { + completion(.failure(ImportReadingListError.failureFetchingPageURLs)) + return + } + + completion(.success(finalPageURLs)) + } + } + + func articleObjectsFromArticleURLs(_ articleURLs: [URL], completion: @escaping ((Result<[WMFArticle], Error>) -> Void)) { + + guard let dataStore = dataStore else { + completion(.failure(ImportReadingListError.missingDataStore)) + return + } + + let keys = articleURLs.compactMap { $0.wmf_inMemoryKey } + + let articleFetcher = ArticleFetcher() + articleFetcher.fetchArticleSummaryResponsesForArticles(withKeys: keys) { result in + + DispatchQueue.main.async { + var articles: [WMFArticle] = [] + do { + let articleSummaries = try dataStore.viewContext.wmf_createOrUpdateArticleSummmaries(withSummaryResponses: result) + + for (_, value) in articleSummaries { + articles.append(value) + } + + guard !articles.isEmpty else { + completion(.failure(ImportReadingListError.failureFetchingArticleObjects)) + return + } + + completion(.success(articles)) + } catch let error { + completion(.failure(error)) + } + } + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/CreateReadingListViewController.xib b/Apps/Wikipedia/Wikipedia/Code/CreateReadingListViewController.xib new file mode 100644 index 0000000..5d087bc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/CreateReadingListViewController.xib @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DDLog+WMFLogger.h b/Apps/Wikipedia/Wikipedia/Code/DDLog+WMFLogger.h new file mode 100644 index 0000000..2b3c257 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DDLog+WMFLogger.h @@ -0,0 +1,10 @@ +@import WMF.WMFLogging; + +@interface DDLog (WMFLogger) + ++ (void)wmf_addLoggersForCurrentConfiguration; + ++ (NSString *)wmf_currentLogFile; ++ (NSString *)wmf_currentLogFilePath; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/DDLog+WMFLogger.m b/Apps/Wikipedia/Wikipedia/Code/DDLog+WMFLogger.m new file mode 100644 index 0000000..b96f1d8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DDLog+WMFLogger.m @@ -0,0 +1,37 @@ +#import "DDLog+WMFLogger.h" +#import "WMFLogFormatter.h" +#import "Wikipedia-Swift.h" + +@implementation DDLog (WMFLogger) + ++ (void)load { + [self wmf_addLoggersForCurrentConfiguration]; + [self wmf_setSwiftDefaultLogLevel:LOG_LEVEL_DEF]; +} + ++ (void)wmf_addLoggersForCurrentConfiguration { + [self wmf_addWMFFormattedLogger:[BasicLogger new]]; + DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; + fileLogger.logFileManager.maximumNumberOfLogFiles = 7; + [self wmf_addWMFFormattedLogger:fileLogger]; +} + ++ (void)wmf_addWMFFormattedLogger:(id)logger { + [logger setLogFormatter:[WMFLogFormatter new]]; + [DDLog addLogger:logger]; +} + ++ (NSString *)wmf_currentLogFilePath { + DDFileLogger *logger = [[DDLog allLoggers] wmf_match:^BOOL(id obj) { + return [obj isKindOfClass:[DDFileLogger class]]; + }]; + + return [[logger.logFileManager sortedLogFilePaths] firstObject]; +} + ++ (NSString *)wmf_currentLogFile { + NSString *logContents = [NSString stringWithContentsOfFile:[self wmf_currentLogFilePath] encoding:NSUTF8StringEncoding error:nil]; + return logContents; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/Date+Extensions.swift b/Apps/Wikipedia/Wikipedia/Code/Date+Extensions.swift new file mode 100644 index 0000000..107c148 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Date+Extensions.swift @@ -0,0 +1,23 @@ +import Foundation + +extension Date { + + /// Determine if `self` is before the specified `date`. + /// - Parameters: + /// - date: The date to compare with + /// - inclusive: Whether to include `self` in comparison range (i.e. <=) + /// - Returns: A boolean indicating if `self` is before specified `date`. + public func isBefore(_ date: Date, inclusive: Bool = false) -> Bool { + return inclusive ? self <= date : self < date + } + + /// Determine if `self` is after the specified `date`. + /// - Parameters: + /// - date: The date to compare with + /// - inclusive: Whether to include `self` in comparison range (i.e. >=) + /// - Returns: A boolean indicating if `self` is after specified `date`. + public func isAfter(_ date: Date, inclusive: Bool = false) -> Bool { + return inclusive ? self >= date : self > date + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DebugReadingListsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DebugReadingListsViewController.swift new file mode 100644 index 0000000..207e730 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DebugReadingListsViewController.swift @@ -0,0 +1,67 @@ +import UIKit + +class DebugReadingListsViewController: UIViewController, UITextFieldDelegate, Themeable { + + @IBOutlet weak var listLimitTextField: UITextField! + @IBOutlet weak var entryLimitTextField: UITextField! + @IBOutlet weak var addEntriesSwitch: UISwitch! + @IBOutlet weak var createListsSwitch: UISwitch! + @IBOutlet weak var activityIndicator: UIActivityIndicatorView! + @IBOutlet weak var randomizeAcrossLanguagesSwitch: UISwitch! + @IBOutlet weak var deleteAllListsSwitch: UISwitch! + @IBOutlet weak var deleteAllEntriesSwitch: UISwitch! + @IBOutlet weak var fullSyncSwitch: UISwitch! + + @IBAction func addEntriesSwitchChanged(_ sender: UISwitch) { + if !sender.isOn { + randomizeAcrossLanguagesSwitch.isOn = false + } + } + override func viewDidLoad() { + super.viewDidLoad() + let moc = MWKDataStore.shared().viewContext + entryLimitTextField.returnKeyType = .done + listLimitTextField.returnKeyType = .done + entryLimitTextField.delegate = self + listLimitTextField.delegate = self + listLimitTextField.text = "\(moc.wmf_numberValue(forKey: "WMFCountOfListsToCreate")?.int64Value ?? 10)" + entryLimitTextField.text = "\(moc.wmf_numberValue(forKey: "WMFCountOfEntriesToCreate")?.int64Value ?? 100)" + + navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "close"), style: .plain, target: self, action: #selector(close)) + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.endEditing(true) + return true + } + + @IBAction func doit(_ sender: UIButton?) { + let dataStore = MWKDataStore.shared() + let readingListsController = dataStore.readingListsController + + let listLimit = Int64(listLimitTextField.text ?? "10") ?? 10 + let entryLimit = Int64(entryLimitTextField.text ?? "100") ?? 100 + + activityIndicator.startAnimating() + sender?.isEnabled = false + readingListsController.debugSync(createLists: createListsSwitch.isOn, listCount: listLimit, addEntries: addEntriesSwitch.isOn, randomizeLanguageEntries:randomizeAcrossLanguagesSwitch.isOn, entryCount: entryLimit, deleteLists: deleteAllListsSwitch.isOn, deleteEntries: deleteAllEntriesSwitch.isOn, doFullSync: fullSyncSwitch.isOn, completion: { + DispatchQueue.main.async { + sender?.isEnabled = true + self.activityIndicator.stopAnimating() + } + }) + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + view.endEditing(true) + } + + @objc private func close() { + dismiss(animated: true) + } + + func apply(theme: Theme) { + view.backgroundColor = theme.colors.paperBackground + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DebugReadingListsViewController.xib b/Apps/Wikipedia/Wikipedia/Code/DebugReadingListsViewController.xib new file mode 100644 index 0000000..e2076de --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DebugReadingListsViewController.xib @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DefaultEditToolbarView.swift b/Apps/Wikipedia/Wikipedia/Code/DefaultEditToolbarView.swift new file mode 100644 index 0000000..aaf263a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DefaultEditToolbarView.swift @@ -0,0 +1,141 @@ +import UIKit + +class DefaultEditToolbarView: EditToolbarView { + @IBOutlet weak var scrollView: UIScrollView! + @IBOutlet weak var stackView: UIStackView! + @IBOutlet weak var chevronButton: UIButton! + + override func awakeFromNib() { + super.awakeFromNib() + chevronButton.imageView?.contentMode = .scaleAspectFit + stackView.isLayoutMarginsRelativeArrangement = true + stackView.layoutMargins = UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 0) + chevronButton.isAccessibilityElement = false + } + + // MARK: Button actions + + @IBAction private func formatText(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapTextFormatting() + } + + @IBAction private func formatTextStyle(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapTextStyleFormatting() + } + + @IBAction private func toggleCitation(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapReference() + } + + @IBAction private func toggleLink(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapLink() + } + + @IBAction private func toggleUnorderedList(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapUnorderedList() + } + + @IBAction private func toggleOrderedList(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapOrderedList() + } + + @IBAction private func decreaseIndentation(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapDecreaseIndent() + } + + @IBAction private func increaseIndentation(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapIncreaseIndent() + } + + @IBAction private func moveCursorUp(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapCursorUp() + } + + @IBAction private func moveCursorDown(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapCursorDown() + } + + @IBAction private func moveCursorLeft(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapCursorLeft() + } + + @IBAction private func moveCursorRight(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapCursorRight() + } + + @IBAction private func toggleTemplate(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapTemplate() + } + + @IBAction private func showFindInPage(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapFindInPage() + } + + @IBAction private func insertMedia(_ sender: UIButton) { + delegate?.textFormattingProvidingDidTapMediaInsert() + } + + private enum ActionsType: CGFloat { + case `default` + case secondary + + static func visible(rawValue: RawValue) -> ActionsType { + if rawValue == 0 { + return .default + } else { + return .secondary + } + } + + static func next(rawValue: RawValue) -> ActionsType { + if rawValue == 0 { + return .secondary + } else { + return .default + } + } + } + + @IBAction private func revealMoreActions(_ sender: UIButton) { + let generator = UIImpactFeedbackGenerator(style: .light) + generator.impactOccurred() + let offsetX = scrollView.contentOffset.x + let actionsType = ActionsType.next(rawValue: offsetX) + revealMoreActions(ofType: actionsType, with: sender, animated: true) + } + + private func revealMoreActions(ofType actionsType: ActionsType, with sender: UIButton, animated: Bool) { + let transform = CGAffineTransform.identity + let buttonTransform: () -> Void + let newOffsetX: CGFloat + + switch actionsType { + case .default: + buttonTransform = { + sender.transform = transform + } + newOffsetX = 0 + case .secondary: + buttonTransform = { + sender.transform = transform.rotated(by: 180 * CGFloat.pi) + sender.transform = transform.rotated(by: -1 * CGFloat.pi) + } + newOffsetX = stackView.bounds.width / 2 + } + + let scrollViewContentOffsetChange = { + self.scrollView.setContentOffset(CGPoint(x: newOffsetX , y: 0), animated: false) + } + + if animated { + let buttonAnimator = UIViewPropertyAnimator(duration: 0.4, dampingRatio: 0.7, animations: buttonTransform) + let scrollViewAnimator = UIViewPropertyAnimator(duration: 0.2, curve: .easeInOut, animations: scrollViewContentOffsetChange) + + buttonAnimator.startAnimation() + scrollViewAnimator.startAnimation() + } else { + buttonTransform() + scrollViewContentOffsetChange() + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DefaultEditToolbarView.xib b/Apps/Wikipedia/Wikipedia/Code/DefaultEditToolbarView.xib new file mode 100644 index 0000000..e5c881d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DefaultEditToolbarView.xib @@ -0,0 +1,371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DescriptionEditViewController.storyboard b/Apps/Wikipedia/Wikipedia/Code/DescriptionEditViewController.storyboard new file mode 100644 index 0000000..b743ddf --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DescriptionEditViewController.storyboard @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DescriptionEditViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DescriptionEditViewController.swift new file mode 100644 index 0000000..d217730 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DescriptionEditViewController.swift @@ -0,0 +1,416 @@ +import UIKit +import WMF + +protocol DescriptionEditViewControllerDelegate: AnyObject { + func descriptionEditViewControllerEditSucceeded(_ descriptionEditViewController: DescriptionEditViewController, result: ArticleDescriptionPublishResult) +} + +@objc class DescriptionEditViewController: WMFScrollViewController, Themeable, UITextViewDelegate { + @objc public static let didPublishNotification = NSNotification.Name("DescriptionEditViewControllerDidPublishNotification") + + @IBOutlet private var learnMoreButton: UIButton! + @IBOutlet private var subTitleLabel: UILabel! + @IBOutlet private var descriptionTextView: UITextView! + @IBOutlet private var descriptionPlaceholderLabel: UILabel! + @IBOutlet private var licenseLabel: UILabel! + @IBOutlet private var loginLabel: UILabel! + @IBOutlet private var divider: UIView! + @IBOutlet private var cc0ImageView: UIImageView! + @IBOutlet private var publishDescriptionButton: WMFAuthButton! + @IBOutlet private var lengthWarningLabel: UILabel! + @IBOutlet private weak var casingWarningLabel: UILabel! + @IBOutlet private var warningCharacterCountLabel: UILabel! + private var theme = Theme.standard + + var delegate: DescriptionEditViewControllerDelegate? = nil + + // MARK: Event logging + @objc var editFunnel: EditFunnel? + @objc var editFunnelSource: EditFunnelSource = .unknown + + private var articleDescriptionController: ArticleDescriptionControlling! + + // These would be better as let's and a required initializer but it's not an opportune time to ditch the storyboard + // Convert these to non-force unwrapped if there's some way to ditch the storyboard or provide an initializer with the storyboard + var isAddingNewTitleDescription: Bool! + var dataStore: MWKDataStore! + static func with(dataStore: MWKDataStore, theme: Theme, articleDescriptionController: ArticleDescriptionControlling) -> DescriptionEditViewController { + let vc = wmf_initialViewControllerFromClassStoryboard()! + vc.isAddingNewTitleDescription = articleDescriptionController.descriptionSource == .none + vc.dataStore = dataStore + vc.articleDescriptionController = articleDescriptionController + return vc + } + + override func viewDidLoad() { + super.viewDidLoad() + + navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named:"close"), style: .plain, target:self, action:#selector(closeButtonPushed(_:))) + navigationItem.leftBarButtonItem?.accessibilityLabel = CommonStrings.closeButtonAccessibilityLabel + + lengthWarningLabel.text = WMFLocalizedString("description-edit-warning", value:"Try to keep descriptions short so users can understand the article's subject at a glance", comment:"Title text for label reminding users to keep descriptions concise") + casingWarningLabel.text = WMFLocalizedString("description-edit-warning-casing", value:"Only proper nouns should be capitalized, even at the start of the sentence.", comment:"Title text for label reminding users to begin article descriptions with a lowercase letter for non-EN wikis.") + + publishDescriptionButton.setTitle(WMFLocalizedString("description-edit-publish", value:"Publish description", comment:"Title for publish description button"), for: .normal) + + learnMoreButton.setTitle(WMFLocalizedString("description-edit-learn-more", value:"Learn more", comment:"Title text for description editing learn more button"), for: .normal) + + descriptionPlaceholderLabel.text = WMFLocalizedString("description-edit-placeholder-title", value:"Short descriptions are best", comment:"Placeholder text shown inside description field until user taps on it") + + view.wmf_configureSubviewsForDynamicType() + apply(theme: theme) + + isPlaceholderLabelHidden = false + hideAllWarningLabels() + articleDescriptionController.currentDescription { [weak self] (description, blockedError) in + + guard let self = self else { + return + } + + if let currentDescription = description { + self.descriptionTextView.text = currentDescription + self.title = WMFLocalizedString("description-edit-title", value:"Edit description", comment:"Title text for description editing screen") + } else { + self.title = WMFLocalizedString("description-add-title", value:"Add description", comment:"Title text for description addition screen") + } + + self.isPlaceholderLabelHidden = self.shouldHidePlaceholder() + self.updateWarningLabels() + + if let blockedError { + self.disableTextFieldAndPublish() + self.presentBlockedPanel(error: blockedError) + } + } + + descriptionTextView.textContainer.lineFragmentPadding = 0 + descriptionTextView.textContainerInset = .zero + + updateFonts() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + enableProgressiveButton(false) + loginLabel.isHidden = dataStore.authenticationManager.isLoggedIn + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + descriptionTextView.becomeFirstResponder() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + enableProgressiveButton(false) + } + + private var isPlaceholderLabelHidden = true { + didSet { + descriptionPlaceholderLabel.isHidden = isPlaceholderLabelHidden + } + } + + public func textViewDidBeginEditing(_ textView: UITextView) { + isPlaceholderLabelHidden = shouldHidePlaceholder() + } + + public func textViewDidEndEditing(_ textView: UITextView) { + isPlaceholderLabelHidden = shouldHidePlaceholder() + } + + private func shouldHidePlaceholder() -> Bool { + return descriptionTextView.nilTextSafeCount() > 0 + } + + public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + guard let range = Range(range, in: textView.text) else { + return true + } + let newText = textView.text.replacingCharacters(in: range, with: text) + isPlaceholderLabelHidden = !newText.isEmpty + return true + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + subTitleLabel.attributedText = subTitleLabelAttributedString + licenseLabel.attributedText = licenseLabelAttributedString + loginLabel.attributedText = loginLabelAttributedString + } + + private var subTitleLabelAttributedString: NSAttributedString { + let formatString = WMFLocalizedString("description-edit-for-article", value: "Article description for %1$@", comment: "String describing which article description is being edited. %1$@ is replaced with the article title") + return String.localizedStringWithFormat(formatString, articleDescriptionController.articleDisplayTitle ?? "").byAttributingHTML(with: .semiboldSubheadline, matching: traitCollection) + } + + private func characterCountWarningString(for descriptionCharacterCount: Int) -> String? { + return String.localizedStringWithFormat(WMFLocalizedString("description-edit-length-warning", value: "%1$@ / %2$@", comment: "Displayed to indicate how many description characters were entered. Separator can be customized depending on the language. %1$@ is replaced with the number of characters entered, %2$@ is replaced with the recommended maximum number of characters."), String(descriptionCharacterCount), String(articleDescriptionController.descriptionMaxLength)) + } + + private var licenseLabelAttributedString: NSAttributedString { + let formatString = WMFLocalizedString("description-edit-license", value: "By changing the article description, I agree to the %1$@ and to irrevocably release my contributions under the %2$@ license.", comment: "Button text for information about the Terms of Use and edit licenses. Parameters:\n* %1$@ - 'Terms of Use' link, %2$@ - license name link") + + let baseAttributes: [NSAttributedString.Key: Any] = [ + .foregroundColor : theme.colors.secondaryText, + .font : licenseLabel.font as Any // Grab font so we get font updated for current dynamic type size + ] + let linkAttributes: [NSAttributedString.Key: Any] = [ + .foregroundColor : theme.colors.link + ] + return formatString.attributedString(attributes: baseAttributes, substitutionStrings: [Licenses.localizedSaveTermsTitle, Licenses.localizedCCZEROTitle], substitutionAttributes: [linkAttributes, linkAttributes]) + } + + private var loginLabelAttributedString: NSAttributedString { + let formatString = CommonStrings.editAttribution + + let baseAttributes: [NSAttributedString.Key: Any] = [ + .foregroundColor : theme.colors.secondaryText, + .font : loginLabel.font as Any // Grab font so we get font updated for current dynamic type size + ] + let linkAttributes: [NSAttributedString.Key: Any] = [ + .foregroundColor : theme.colors.link + ] + return formatString.attributedString(attributes: baseAttributes, substitutionStrings: [CommonStrings.editSignIn], substitutionAttributes: [linkAttributes]) + } + + @IBAction private func descriptionPlaceholderLabelTapped() { + isPlaceholderLabelHidden = true + } + + @IBAction func showAboutWikidataPage() { + + guard let vc = articleDescriptionController.learnMoreViewControllerWithTheme(theme) else { + return + } + + let navVC = WMFThemeableNavigationController.init(rootViewController: vc, theme: theme) + present(navVC, animated: true, completion: nil) + } + + @IBAction func licenseTapped() { + let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .alert) + sheet.addAction(UIAlertAction(title: Licenses.localizedSaveTermsTitle, style: .default, handler: { _ in + self.navigate(to: Licenses.saveTermsURL) + })) + sheet.addAction(UIAlertAction(title: Licenses.localizedCCZEROTitle, style: .default, handler: { _ in + self.navigate(to: Licenses.CCZEROURL) + })) + sheet.addAction(UIAlertAction(title: CommonStrings.cancelActionTitle, style: .cancel, handler: nil)) + present(sheet, animated: true, completion: nil) + } + + @IBAction func loginTapped() { + wmf_showLoginViewController(theme: theme) + } + + @IBAction private func publishDescriptionButton(withSender sender: UIButton) { + editFunnel?.logTitleDescriptionSaveAttempt(source: editFunnelSource, isAddingNewTitleDescription: isAddingNewTitleDescription, language: articleDescriptionController.articleLanguageCode) + save() + } + + @objc func closeButtonPushed(_ : UIBarButtonItem) { + dismiss(animated: true, completion: nil) + } + + private func enableProgressiveButton(_ highlight: Bool) { + publishDescriptionButton.isEnabled = highlight + } + + private func save() { + enableProgressiveButton(false) + wmf_hideKeyboard() + + guard + let descriptionToSave = descriptionTextView.normalizedWhitespaceText(), + !descriptionToSave.isEmpty + else { + descriptionTextView.text = nil + // manually call `textViewDidChange` since it's not called when UITextView text is changed programmatically + textViewDidChange(descriptionTextView) + return + } + + articleDescriptionController.publishDescription(descriptionToSave) { [weak self] (result) in + + DispatchQueue.main.async { + + guard let self else { + return + } + + let presentingVC = self.presentingViewController + self.enableProgressiveButton(true) + switch result { + case .success(let result): + self.editFunnel?.logTitleDescriptionSaved(source: self.editFunnelSource, isAddingNewTitleDescription: self.isAddingNewTitleDescription, language: self.articleDescriptionController.articleLanguageCode) + self.delegate?.descriptionEditViewControllerEditSucceeded(self, result: result) + self.dismiss(animated: true) { + presentingVC?.wmf_showDescriptionPublishedPanelViewController(theme: self.theme) + NotificationCenter.default.post(name: DescriptionEditViewController.didPublishNotification, object: nil) + } + case .failure(let error): + + let nsError = error as NSError + let errorCode = self.articleDescriptionController.errorCodeFromError(nsError) + self.editFunnel?.logTitleDescriptionSaveError(source: self.editFunnelSource, isAddingNewTitleDescription: self.isAddingNewTitleDescription, language: self.articleDescriptionController.articleLanguageCode, errorText: errorCode) + + if let wikidataError = error as? WikidataFetcher.WikidataPublishingError { + switch wikidataError { + case .apiBlocked(let blockedError): + self.presentBlockedPanel(error: blockedError) + return + case .apiAbuseFilterDisallow(let error): + self.presentAbuseFilterDisallowedPanel(error: error) + return + case .apiAbuseFilterWarn(let error), .apiAbuseFilterOther(let error): + self.presentAbuseFilterWarningPanel(error: error) + return + default: + break + } + } + + let errorType = WikiTextSectionUploaderErrorType.init(rawValue: nsError.code) ?? .unknown + + guard let displayError = nsError.userInfo[NSErrorUserInfoDisplayError] as? MediaWikiAPIDisplayError else { + WMFAlertManager.sharedInstance.showErrorAlert(nsError as NSError, sticky: true, dismissPreviousAlerts: true, tapCallBack: nil) + return + } + + switch errorType { + case .blocked: + self.presentBlockedPanel(error: displayError) + return + case .abuseFilterDisallowed: + self.presentAbuseFilterDisallowedPanel(error: displayError) + case .abuseFilterWarning, .abuseFilterOther: + self.presentAbuseFilterWarningPanel(error: displayError) + default: + WMFAlertManager.sharedInstance.showErrorAlert(nsError as NSError, sticky: true, dismissPreviousAlerts: true, tapCallBack: nil) + } + + + } + } + } + } + + private func presentBlockedPanel(error: MediaWikiAPIDisplayError) { + + guard let currentTitle = self.articleDescriptionController?.articleDisplayTitle else { + return + } + + wmf_showBlockedPanel(messageHtml: error.messageHtml, linkBaseURL: error.linkBaseURL, currentTitle: currentTitle, theme: theme) + + } + + private func presentAbuseFilterDisallowedPanel(error: MediaWikiAPIDisplayError) { + + guard let currentTitle = self.articleDescriptionController?.articleDisplayTitle else { + return + } + + wmf_showAbuseFilterDisallowPanel(messageHtml: error.messageHtml, linkBaseURL: error.linkBaseURL, currentTitle: currentTitle, theme: theme, goBackIsOnlyDismiss: true) + + } + + private func presentAbuseFilterWarningPanel(error: MediaWikiAPIDisplayError) { + + guard let currentTitle = self.articleDescriptionController?.articleDisplayTitle else { + return + } + + wmf_showAbuseFilterWarningPanel(messageHtml: error.messageHtml, linkBaseURL: error.linkBaseURL, currentTitle: currentTitle, theme: theme, goBackIsOnlyDismiss: true, publishAnywayTapHandler: { [weak self] _ in + + self?.dismiss(animated: true) { + self?.save() + } + + }) + + } + + private func hideAllWarningLabels() { + warningCharacterCountLabel.isHidden = true + lengthWarningLabel.isHidden = true + casingWarningLabel.isHidden = true + } + + private func updateWarningLabels() { + + warningCharacterCountLabel.text = characterCountWarningString(for: descriptionTextView.nilTextSafeCount()) + + let warningTypes = articleDescriptionController.warningTypesForDescription(descriptionTextView.text) + + warningCharacterCountLabel.textColor = warningTypes.contains(.length) ? theme.colors.descriptionWarning : theme.colors.secondaryText + lengthWarningLabel.isHidden = !warningTypes.contains(.length) + casingWarningLabel.isHidden = !warningTypes.contains(.casing) + } + + private func disableTextFieldAndPublish() { + self.descriptionTextView.isEditable = false + self.publishDescriptionButton.isEnabled = false + } + + public func textViewDidChange(_ textView: UITextView) { + let hasText = !descriptionTextView.text.isEmpty + enableProgressiveButton(hasText) + updateWarningLabels() + isPlaceholderLabelHidden = hasText + } + + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.midBackground + view.tintColor = theme.colors.link + subTitleLabel.textColor = theme.colors.secondaryText + cc0ImageView.tintColor = theme.colors.primaryText + descriptionTextView.textColor = theme.colors.primaryText + divider.backgroundColor = theme.colors.border + descriptionPlaceholderLabel.textColor = theme.colors.unselected + lengthWarningLabel.textColor = theme.colors.descriptionWarning + casingWarningLabel.textColor = theme.colors.error + warningCharacterCountLabel.textColor = theme.colors.descriptionWarning + publishDescriptionButton.apply(theme: theme) + descriptionTextView.keyboardAppearance = theme.keyboardAppearance + } +} + +private var whiteSpaceNormalizationRegex: NSRegularExpression? = { + guard let regex = try? NSRegularExpression(pattern: "\\s+", options: []) else { + assertionFailure("Unexpected failure to create regex") + return nil + } + return regex +}() + +private extension UITextView { + func nilTextSafeCount() -> Int { + guard let text = text else { + return 0 + } + return text.count + } + + // Text with no leading and trailing space and with repeating internal spaces reduced to single spaces + func normalizedWhitespaceText() -> String? { + if let text = text, let whiteSpaceNormalizationRegex = whiteSpaceNormalizationRegex { + return whiteSpaceNormalizationRegex.stringByReplacingMatches(in: text, options: [], range: text.fullRange, withTemplate: " ").trimmingCharacters(in: .whitespacesAndNewlines) + } + return text + } +} + +extension DescriptionEditViewController: EditingFlowViewController { + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DescriptionHelpViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DescriptionHelpViewController.swift new file mode 100644 index 0000000..d037a6e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DescriptionHelpViewController.swift @@ -0,0 +1,125 @@ +import UIKit + +class DescriptionHelpViewController: ViewController { + + @IBOutlet private weak var helpScrollView: UIScrollView! + + @IBOutlet private weak var aboutTitleLabel: UILabel! + @IBOutlet private weak var aboutDescriptionLabel: UILabel! + + @IBOutlet private weak var tipsTitleLabel: UILabel! + @IBOutlet private weak var tipsDescriptionLabel: UILabel! + @IBOutlet private weak var tipsForExampleLabel: UILabel! + + @IBOutlet private weak var exampleOneTitleLabel: UILabel! + @IBOutlet private weak var exampleOneDescriptionLabel: UILabel! + + @IBOutlet private weak var exampleTwoTitleLabel: UILabel! + @IBOutlet private weak var exampleTwoDescriptionLabel: UILabel! + + @IBOutlet private weak var moreInfoTitleLabel: UILabel! + @IBOutlet private weak var moreInfoDescriptionLabel: UILabel! + + @IBOutlet private weak var aboutWikidataLabel: UILabel! + @IBOutlet private weak var wikidataGuideLabel: UILabel! + + @IBOutlet private var allLabels: [UILabel]! + @IBOutlet private var headingLabels: [UILabel]! + @IBOutlet private var italicLabels: [UILabel]! + @IBOutlet private var exampleBackgroundViews: [UIView]! + + @IBOutlet private var imageViews: [UIImageView]! + @IBOutlet private var dividerViews: [UIView]! + + required convenience init?(coder aDecoder: NSCoder) { + self.init(theme: Theme.standard) + } + + public override func viewDidLoad() { + scrollView = helpScrollView + super.viewDidLoad() + + navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named:"close"), style: .plain, target:self, action:#selector(closeButtonPushed(_:))) + navigationItem.leftBarButtonItem?.accessibilityLabel = CommonStrings.closeButtonAccessibilityLabel + + title = WMFLocalizedString("description-help-title", value:"Article description help", comment:"Title for description editing help page") + + aboutTitleLabel.text = WMFLocalizedString("description-help-about-title", value:"About", comment:"Description editing about label text") + aboutDescriptionLabel.text = WMFLocalizedString("description-help-about-description", value:"Article descriptions summarize an article to help readers understand the subject at a glance.", comment:"Description editing details label text") + + tipsTitleLabel.text = WMFLocalizedString("description-help-tips-title", value:"Tips for creating descriptions", comment:"Description editing tips label text") + tipsDescriptionLabel.text = WMFLocalizedString("description-help-tips-description", value:"Descriptions should ideally fit on one line, and are between two to twelve words long. They are not capitalized unless the first word is a proper noun.", comment:"Description editing tips details label text") + tipsForExampleLabel.text = WMFLocalizedString("description-help-tips-for-example", value:"For example:", comment:"Examples label text") + + exampleOneTitleLabel.text = WMFLocalizedString("description-help-tips-example-title-one", value:"painting by Leonardo Da Vinci", comment:"First example label text") + exampleOneDescriptionLabel.text = WMFLocalizedString("description-help-tips-example-description-one", value:"article description for an article about the Mona Lisa", comment:"First example description text") + + exampleTwoTitleLabel.text = WMFLocalizedString("description-help-tips-example-title-two", value:"Earth’s highest mountain", comment:"Second example label text") + exampleTwoDescriptionLabel.text = WMFLocalizedString("description-help-tips-example-description-two", value:"article description for an article about Mount Everest", comment:"Second example description text") + + moreInfoTitleLabel.text = WMFLocalizedString("description-help-more-info-title", value:"More information", comment:"Article descriptions more info heading text") + moreInfoDescriptionLabel.text = WMFLocalizedString("description-help-more-info-description", value:"Descriptions are stored and maintained on Wikidata, a project of the Wikimedia Foundation which provides a free, collaborative, multilingual, secondary database supporting Wikipedia and other projects.", comment:"Article descriptions more info details text") + + aboutWikidataLabel.text = WMFLocalizedString("description-help-about-wikidata", value:"About Wikidata", comment:"About Wikidata label text") + wikidataGuideLabel.text = WMFLocalizedString("description-help-wikidata-guide", value:"Wikidata guide for writing descriptions", comment:"Wikidata guide label text") + updateFonts() + } + + @objc func closeButtonPushed(_ : UIBarButtonItem) { + dismiss(animated: true, completion: nil) + } + + override func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.midBackground + imageViews.forEach { + $0.tintColor = theme.colors.primaryText + } + allLabels.forEach { + $0.textColor = theme.colors.primaryText + } + exampleBackgroundViews.forEach { + $0.backgroundColor = theme.colors.descriptionBackground + } + headingLabels.forEach { + $0.textColor = theme.colors.secondaryText + } + dividerViews.forEach { + $0.backgroundColor = theme.colors.border + } + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + allLabels.forEach { + $0.set(dynamicTextStyle: .body) + } + headingLabels.forEach { + $0.set(dynamicTextStyle: .headline) + } + italicLabels.forEach { + $0.set(dynamicTextStyle: .italicBody) + } + } + + @IBAction func showAboutWikidataPage() { + navigate(to: URL(string: "https://m.wikidata.org/wiki/Wikidata:Introduction")) + } + + @IBAction func showWikidataGuidePage() { + navigate(to: URL(string: "https://m.wikidata.org/wiki/Help:Description#Guidelines_for_descriptions_in_English")) + } +} + +private extension UILabel { + func set(dynamicTextStyle: DynamicTextStyle) { + font = UIFont.wmf_font(dynamicTextStyle, compatibleWithTraitCollection: traitCollection) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DescriptionHelpViewController.xib b/Apps/Wikipedia/Wikipedia/Code/DescriptionHelpViewController.xib new file mode 100644 index 0000000..354464b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DescriptionHelpViewController.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcome.storyboard b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcome.storyboard new file mode 100644 index 0000000..70d9da5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcome.storyboard @@ -0,0 +1,407 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeContainerViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeContainerViewController.swift new file mode 100644 index 0000000..99c0fc9 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeContainerViewController.swift @@ -0,0 +1,73 @@ +class DescriptionWelcomeContainerViewController: UIViewController, Themeable { + private var theme = Theme.standard + func apply(theme: Theme) { + self.theme = theme + } + + @IBOutlet private var topContainerView:UIView! + @IBOutlet private var bottomContainerView:UIView! + @IBOutlet private var overallContainerStackView:UIStackView! + @IBOutlet private var overallContainerStackViewCenterYConstraint:NSLayoutConstraint! + @IBOutlet private var topContainerViewHeightConstraint:NSLayoutConstraint! + + var pageType:DescriptionWelcomePageType = .intro + weak var welcomeNavigationDelegate:DescriptionWelcomeNavigationDelegate? = nil + + private var hasAlreadyFadedInAndUp = false + private var needsDeviceAdjustments = true + + var nextButtonAction: ((UIButton) -> Void)? + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if shouldFadeInAndUp() && !hasAlreadyFadedInAndUp { + view.wmf_zeroLayerOpacity() + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if shouldFadeInAndUp() && !hasAlreadyFadedInAndUp { + view.wmf_fadeInAndUpWithDuration(0.4, delay: 0.1) + hasAlreadyFadedInAndUp = true + } + } + + private func shouldFadeInAndUp() -> Bool { + switch pageType { + case .intro: + return false + case .exploration: + return true + } + } + + override func updateViewConstraints() { + super.updateViewConstraints() + if needsDeviceAdjustments { + useBottomAlignmentIfPhone() + needsDeviceAdjustments = false + } + } + + private func useBottomAlignmentIfPhone() { + assert(Int(overallContainerStackViewCenterYConstraint.priority.rawValue) == 999, "The Y centering constraint must not have required '1000' priority because on non-tablets we add a required bottom alignment constraint on overallContainerView which we want to be favored when present.") + if UIDevice.current.userInterfaceIdiom == .phone { + overallContainerStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true + } + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if let vc = segue.destination as? DescriptionWelcomePanelViewController { + vc.nextButtonAction = nextButtonAction + vc.pageType = pageType + vc.apply(theme: theme) + } else if let vc = segue.destination as? DescriptionWelcomeImageViewController { + vc.pageType = pageType + } + } + + @IBAction private func next(withSender sender: AnyObject) { + welcomeNavigationDelegate?.showNextWelcomePage(self) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeContentsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeContentsViewController.swift new file mode 100644 index 0000000..afa3ea5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeContentsViewController.swift @@ -0,0 +1,30 @@ +class DescriptionWelcomeContentsViewController: UIViewController, Themeable { + private var theme = Theme.standard + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + descriptionLabel.textColor = theme.colors.primaryText + } + + @IBOutlet private var descriptionLabel:UILabel! + + var pageType:DescriptionWelcomePageType = .intro + + override func viewDidLoad() { + super.viewDidLoad() + updateUIStrings() + apply(theme: theme) + view.wmf_configureSubviewsForDynamicType() + } + + private func updateUIStrings() { + switch pageType { + case .intro: + descriptionLabel?.text = WMFLocalizedString("description-welcome-descriptions-sub-title", value:"Summarizes an article to help readers understand the subject at a glance", comment:"Subtitle text explaining article descriptions") + case .exploration: + descriptionLabel?.text = WMFLocalizedString("description-welcome-concise-sub-title", value:"Ideally one line, between two to twelve words", comment:"Subtitle text explaining descriptions should be concise") + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeImageViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeImageViewController.swift new file mode 100644 index 0000000..0021954 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeImageViewController.swift @@ -0,0 +1,26 @@ +class DescriptionWelcomeImageViewController: UIViewController { + var pageType:DescriptionWelcomePageType = .intro + + private lazy var imageForWelcomePageType: UIImage? = { + switch pageType { + case .intro: + return UIImage(named: "description-cat") + case .exploration: + return UIImage(named: "description-planet") + } + }() + + private var image: UIImage? { + return imageForWelcomePageType + } + + override func viewDidLoad() { + super.viewDidLoad() + guard let image = image else { + return + } + let imageView = UIImageView(image: image) + imageView.contentMode = .scaleAspectFit + view.wmf_addSubviewWithConstraintsToEdges(imageView) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeInitialViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeInitialViewController.swift new file mode 100644 index 0000000..2eaa54e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomeInitialViewController.swift @@ -0,0 +1,37 @@ +// A lightweight way to provide iPhone X friendly constraints when using a UIPageViewController +// is to simply embed it in a container view which uses such constraints. No need to modify the +// UIPageViewController subclass at all. DescriptionWelcomeInitialViewController embeds a UIPageViewController +// in such a container view. +class DescriptionWelcomeInitialViewController: UIViewController, Themeable { + private var theme = Theme.standard + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.midBackground + } + + @objc var completionBlock: (() -> Void)? + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + guard let vc = segue.destination as? DescriptionWelcomePageViewController else { + assertionFailure("Expected a DescriptionWelcomePageViewController") + return + } + vc.apply(theme: theme) + vc.completionBlock = completionBlock + } + + override func viewDidLoad() { + super.viewDidLoad() + apply(theme: theme) + } + + override var prefersStatusBarHidden: Bool { + return false + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return theme.preferredStatusBarStyle + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomePageViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomePageViewController.swift new file mode 100644 index 0000000..64b0a09 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomePageViewController.swift @@ -0,0 +1,204 @@ +import Foundation +import UIKit + +enum DescriptionWelcomePageType { + case intro + case exploration +} + +public protocol DescriptionWelcomeNavigationDelegate: AnyObject { + func showNextWelcomePage(_ sender: AnyObject) +} + +class DescriptionWelcomePageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, DescriptionWelcomeNavigationDelegate, Themeable { + private var theme = Theme.standard + func apply(theme: Theme) { + self.theme = theme + for pageController in pageControllers { + if let pageController = pageController as? Themeable { + pageController.apply(theme: theme) + } + } + guard viewIfLoaded != nil else { + return + } + + skipButton.setTitleColor(UIColor.gray400, for: .normal) + nextButton.setTitleColor(theme.colors.link, for: .normal) + nextButton.setTitleColor(theme.colors.disabledText, for: .disabled) + nextButton.setTitleColor(theme.colors.link, for: .highlighted) + } + + @objc var completionBlock: (() -> Void)? + + func showNextWelcomePage(_ sender: AnyObject) { + guard let sender = sender as? UIViewController, let index = pageControllers.firstIndex(of: sender), index != pageControllers.count - 1 else { + dismiss(animated: true, completion:completionBlock) + return + } + view.isUserInteractionEnabled = false + let nextIndex = index + 1 + let direction:UIPageViewController.NavigationDirection = UIApplication.shared.wmf_isRTL ? .reverse : .forward + let nextVC = pageControllers[nextIndex] + hideButtons(for: nextVC) + setViewControllers([nextVC], direction: direction, animated: true, completion: {(Bool) in + self.view.isUserInteractionEnabled = true + }) + } + + private func containerControllerForWelcomePageType(_ type: DescriptionWelcomePageType) -> DescriptionWelcomeContainerViewController { + let controller = DescriptionWelcomeContainerViewController.wmf_viewControllerFromDescriptionWelcomeStoryboard() + controller.welcomeNavigationDelegate = self + controller.pageType = type + controller.nextButtonAction = { [weak self] sender in + self?.skipButtonTapped(sender) + } + return controller + } + + private lazy var pageControllers: [UIViewController] = { + var controllers:[UIViewController] = [] + controllers.append(containerControllerForWelcomePageType(.intro)) + controllers.append(containerControllerForWelcomePageType(.exploration)) + return controllers + }() + + private lazy var pageControl: UIPageControl? = { + return view.wmf_firstSubviewOfType(UIPageControl.self) + }() + + let nextButton = UIButton() + let skipButton = UIButton() + let buttonHeight: CGFloat = 40.0 + let buttonSidePadding: CGFloat = 10.0 + let buttonCenterXOffset: CGFloat = 88.0 + + override func viewDidLoad() { + super.viewDidLoad() + dataSource = self + delegate = self + + let direction:UIPageViewController.NavigationDirection = UIApplication.shared.wmf_isRTL ? .forward : .reverse + + setViewControllers([pageControllers.first!], direction: direction, animated: true, completion: nil) + + configureAndAddNextButton() + configureAndAddSkipButton() + + if let scrollView = view.wmf_firstSubviewOfType(UIScrollView.self) { + scrollView.clipsToBounds = false + } + + updateFonts() + apply(theme: theme) + } + + private func configureAndAddNextButton() { + nextButton.translatesAutoresizingMaskIntoConstraints = false + nextButton.addTarget(self, action: #selector(nextButtonTapped), for: .touchUpInside) + nextButton.isUserInteractionEnabled = true + nextButton.setContentCompressionResistancePriority(.required, for: .horizontal) + nextButton.titleLabel?.numberOfLines = 1 + nextButton.setTitle(CommonStrings.nextTitle, for: .normal) + view.addSubview(nextButton) + nextButton.heightAnchor.constraint(equalToConstant: buttonHeight).isActive = true + view.addConstraint(NSLayoutConstraint(item: nextButton, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)) + + let leading = NSLayoutConstraint(item: nextButton, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: buttonCenterXOffset) + leading.priority = .defaultHigh + let trailing = NSLayoutConstraint(item: nextButton, attribute: .trailing, relatedBy: .lessThanOrEqual, toItem: view, attribute: .trailing, multiplier: 1, constant: buttonSidePadding) + trailing.priority = .required + view.addConstraints([leading, trailing]) + } + + private func configureAndAddSkipButton() { + skipButton.translatesAutoresizingMaskIntoConstraints = false + skipButton.addTarget(self, action: #selector(skipButtonTapped), for: .touchUpInside) + skipButton.isUserInteractionEnabled = true + skipButton.setContentCompressionResistancePriority(.required, for: .horizontal) + skipButton.titleLabel?.numberOfLines = 1 + skipButton.setTitle(CommonStrings.skipTitle, for: .normal) + view.addSubview(skipButton) + skipButton.heightAnchor.constraint(equalToConstant: buttonHeight).isActive = true + view.addConstraint(NSLayoutConstraint(item: skipButton, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)) + + let leading = NSLayoutConstraint(item: skipButton, attribute: .leading, relatedBy: .greaterThanOrEqual, toItem: view, attribute: .leading, multiplier: 1, constant: buttonSidePadding) + leading.priority = .required + let trailing = NSLayoutConstraint(item: skipButton, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: -buttonCenterXOffset) + trailing.priority = .defaultHigh + view.addConstraints([leading, trailing]) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + skipButton.titleLabel?.font = UIFont.wmf_font(.semiboldFootnote, compatibleWithTraitCollection: traitCollection) + nextButton.titleLabel?.font = UIFont.wmf_font(.semiboldFootnote, compatibleWithTraitCollection: traitCollection) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if let pageControl = pageControl { + pageControl.isUserInteractionEnabled = false + pageControl.pageIndicatorTintColor = theme.colors.pageIndicator + pageControl.currentPageIndicatorTintColor = theme.colors.pageIndicatorCurrent + } + } + + @objc func nextButtonTapped(_ sender: UIButton) { + if let currentVC = viewControllers?.first { + showNextWelcomePage(currentVC) + } + } + + @objc func skipButtonTapped(_ sender: UIButton) { + dismiss(animated: true, completion:completionBlock) + } + + func presentationCount(for pageViewController: UIPageViewController) -> Int { + return pageControllers.count + } + + func presentationIndex(for pageViewController: UIPageViewController) -> Int { + guard let viewControllers = viewControllers, let currentVC = viewControllers.first, let presentationIndex = pageControllers.firstIndex(of: currentVC) else { + return 0 + } + return presentationIndex + } + + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { + guard let index = pageControllers.firstIndex(of: viewController) else { + return nil + } + return index >= pageControllers.count - 1 ? nil : pageControllers[index + 1] + } + + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + guard let index = pageControllers.firstIndex(of: viewController) else { + return nil + } + return index == 0 ? nil : pageControllers[index - 1] + } + + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + if completed { + hideButtons(for: pageControllers[presentationIndex(for: pageViewController)]) + } + } + + func hideButtons(for vc: UIViewController) { + let isLastPage = pageControllers.firstIndex(of: vc) == pageControllers.count - 1 + let newAlpha:CGFloat = isLastPage ? 0.0 : 1.0 + let alphaChanged = pageControl?.alpha != newAlpha + nextButton.isEnabled = !isLastPage // Gray out the next button when transitioning to last page (per design) + guard alphaChanged else { return } + UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveLinear, animations: { + self.nextButton.alpha = newAlpha + self.skipButton.alpha = newAlpha + self.pageControl?.alpha = newAlpha + }, completion: nil) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomePanelViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomePanelViewController.swift new file mode 100644 index 0000000..00a475f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DescriptionWelcomePanelViewController.swift @@ -0,0 +1,127 @@ +class DescriptionWelcomePanelViewController: UIViewController, Themeable { + private var theme = Theme.standard + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + scrollViewGradientView.apply(theme: theme) + titleLabel.textColor = theme.colors.primaryText + bottomLabel.textColor = theme.colors.primaryText + nextButton.backgroundColor = theme.colors.link + } + + @IBOutlet private var containerView:UIView! + @IBOutlet private var titleLabel:UILabel! + @IBOutlet private var bottomLabel:UILabel! + @IBOutlet private var nextButton:AutoLayoutSafeMultiLineButton! + @IBOutlet private var scrollView:UIScrollView! + @IBOutlet private var scrollViewGradientView:WelcomePanelScrollViewGradient! + @IBOutlet private var nextButtonContainerView:UIView! + + var nextButtonAction: ((UIButton) -> Void)? + + private var viewControllerForContainerView:UIViewController? = nil + var pageType:DescriptionWelcomePageType = .intro + + override func viewDidLoad() { + super.viewDidLoad() + apply(theme: theme) + embedContainerControllerView() + updateUIStrings() + + // If the button itself was directly an arranged stackview subview we couldn't + // set padding contraints and also get clean collapsing when enabling isHidden. + nextButtonContainerView.isHidden = pageType != .exploration + + view.wmf_configureSubviewsForDynamicType() + + nextButton.addTarget(self, action: #selector(performNextButtonAction(_:)), for: .touchUpInside) + } + + private func embedContainerControllerView() { + let containerController = DescriptionWelcomeContentsViewController.wmf_viewControllerFromDescriptionWelcomeStoryboard() + containerController.pageType = pageType + addChild(containerController) + containerView.wmf_addSubviewWithConstraintsToEdges(containerController.view) + containerController.apply(theme: theme) + containerController.didMove(toParent: self) + } + + private func updateUIStrings() { + switch pageType { + case .intro: + titleLabel.text = WMFLocalizedString("description-welcome-descriptions-title", value:"Article descriptions", comment:"Title text explaining article descriptions") + case .exploration: + titleLabel.text = WMFLocalizedString("description-welcome-concise-title", value:"Keep it short", comment:"Title text explaining descriptions should be concise") + } + + bottomLabel.text = CommonStrings.welcomePromiseTitle + + nextButton.setTitle(WMFLocalizedString("description-welcome-start-editing-button", value:"Start editing", comment:"Text for button for dismissing description editing welcome screens"), for: .normal) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if scrollView.wmf_contentSizeHeightExceedsBoundsHeight() { + scrollView.wmf_flashVerticalScrollIndicatorAfterDelay(1.5) + } + } + + @objc private func performNextButtonAction(_ sender: UIButton) { + nextButtonAction?(sender) + } +} + +private extension UIScrollView { + func wmf_contentSizeHeightExceedsBoundsHeight() -> Bool { + return contentSize.height - bounds.size.height > 0 + } + func wmf_flashVerticalScrollIndicatorAfterDelay(_ delay: TimeInterval) { + dispatchOnMainQueueAfterDelayInSeconds(delay) { + self.flashScrollIndicators() + } + } +} + +class WelcomePanelScrollViewGradient : UIView, Themeable { + private var theme = Theme.standard + func apply(theme: Theme) { + self.theme = theme + layer.backgroundColor = theme.colors.midBackground.cgColor + } + + private let fadeHeight = 6.0 + private var normalizedFadeHeight: Double { + return bounds.size.height > 0 ? fadeHeight / Double(bounds.size.height) : 0 + } + + private lazy var gradientMask: CAGradientLayer = { + let mask = CAGradientLayer() + mask.startPoint = .zero + mask.endPoint = CGPoint(x: 0, y: 1) + mask.colors = [ + UIColor.black.cgColor, + UIColor.clear.cgColor, + UIColor.clear.cgColor, + UIColor.black.cgColor + ] + layer.mask = mask + return mask + }() + + override func layoutSublayers(of layer: CALayer) { + super.layoutSublayers(of: layer) + guard layer == gradientMask.superlayer else { + assertionFailure("Unexpected superlayer") + return + } + gradientMask.locations = [ // Keep fade heights fixed to `fadeHeight` regardless of text view height + 0.0, + NSNumber(value: normalizedFadeHeight), // upper stop + NSNumber(value: 1.0 - normalizedFadeHeight), // lower stop + 1.0 + ] + gradientMask.frame = bounds + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DetailTransition.swift b/Apps/Wikipedia/Wikipedia/Code/DetailTransition.swift new file mode 100644 index 0000000..a208332 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DetailTransition.swift @@ -0,0 +1,196 @@ +import UIKit + +protocol DetailTransitionSourceProviding { + var detailTransitionSourceRect: CGRect? { get } +} + +@objc(WMFImageScaleTransitionProviding) +protocol ImageScaleTransitionProviding { + var imageScaleTransitionView: UIImageView? { get } + @objc optional func prepareForIncomingImageScaleTransition() // before views load + @objc(prepareViewsForIncomingImageScaleTransitionWithImageView:) + optional func prepareViewsForIncomingImageScaleTransition(with imageView: UIImageView?) // after views load + @objc optional func prepareForOutgoingImageScaleTransition() +} + +class DetailTransition: NSObject, UIViewControllerAnimatedTransitioning { + + let detailSourceViewController: DetailTransitionSourceProviding & ViewController + + var theme: Theme { + return detailSourceViewController.theme + } + + required init(detailSourceViewController: DetailTransitionSourceProviding & ViewController, incomingImageScaleTransitionProvider: ImageScaleTransitionProviding?, outgoingImageScaleTransitionProvider: ImageScaleTransitionProviding?) { + self.detailSourceViewController = detailSourceViewController + incomingImageScaleTransitionProvider?.prepareForIncomingImageScaleTransition?() + outgoingImageScaleTransitionProvider?.prepareForOutgoingImageScaleTransition?() + } + + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + return 0.3 + } + + + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + guard + let toViewController = transitionContext.viewController(forKey: .to), + let fromViewController = transitionContext.viewController(forKey: .from) + else { + transitionContext.completeTransition(false) + return + } + + let maybeToISP = (toViewController as? UITabBarController)?.selectedViewController ?? toViewController + let maybeFromISP = (fromViewController as? UITabBarController)?.selectedViewController ?? fromViewController + + let isEnteringDetail: Bool = maybeFromISP === detailSourceViewController + let containerView = transitionContext.containerView + + let toFrame = transitionContext.finalFrame(for: toViewController) + toViewController.view.frame = toFrame + containerView.addSubview(toViewController.view) + + let fromImageView: UIImageView? + let toImageView: UIImageView? + let isImageScaleTransitioning: Bool + + if let fromISTP = maybeFromISP as? ImageScaleTransitionProviding, let toISTP = maybeToISP as? ImageScaleTransitionProviding { + fromImageView = fromISTP.imageScaleTransitionView + toImageView = toISTP.imageScaleTransitionView + isImageScaleTransitioning = fromImageView != nil && toImageView != nil && fromImageView?.image != nil + if isImageScaleTransitioning { + toISTP.prepareViewsForIncomingImageScaleTransition?(with: fromImageView) + } + } else { + fromImageView = nil + toImageView = nil + isImageScaleTransitioning = false + } + + let fromFrame = transitionContext.initialFrame(for: fromViewController) + + guard + let toSnapshot = maybeToISP.view.snapshotView(afterScreenUpdates: true), + let fromSnapshot = maybeFromISP.view.snapshotView(afterScreenUpdates: false) + else { + transitionContext.completeTransition(true) + return + } + + let backgroundView = UIView() + backgroundView.backgroundColor = detailSourceViewController.theme.colors.paperBackground + backgroundView.frame = CGRect(origin: .zero, size: containerView.bounds.size) + containerView.addSubview(backgroundView) + + toSnapshot.frame = toFrame + fromSnapshot.frame = fromFrame + + containerView.addSubview(fromSnapshot) + + if isEnteringDetail { + containerView.insertSubview(toSnapshot, belowSubview: fromSnapshot) + } else { + containerView.addSubview(toSnapshot) + } + + let transform: CGAffineTransform + + if isImageScaleTransitioning, let detailImageView = isEnteringDetail ? toImageView : fromImageView, let sourceImageView = isEnteringDetail ? fromImageView : toImageView { + let sourceFrame = containerView.convert(sourceImageView.frame, from: sourceImageView.superview) + let detailFrame = containerView.convert(detailImageView.frame, from: detailImageView.superview) + let deltaX = detailFrame.midX - sourceFrame.midX + let deltaY = detailFrame.midY - sourceFrame.midY + let scaleX = detailFrame.size.width / sourceFrame.size.width + let scaleY = detailFrame.size.height / sourceFrame.size.height + if abs(scaleX - scaleY) > 0.1 || max(scaleX, scaleY) > 1.5 || min(scaleX, scaleY) < 0.5 { + let scale = CGAffineTransform(scaleX: 1.25, y: 1.25) + let delta = CGAffineTransform(translationX: deltaX, y: deltaY) + transform = scale.concatenating(delta) + } else { + let scale = CGAffineTransform(scaleX: scaleX, y: scaleY) + let delta = CGAffineTransform(translationX: deltaX, y: deltaY) + transform = scale.concatenating(delta) + } + } else if let rect = detailSourceViewController.detailTransitionSourceRect { + let scaleUp = CGAffineTransform(scaleX: 1.25, y: 1.25) + let translation = CGAffineTransform(translationX: detailSourceViewController.view.bounds.midX - rect.midX, y: detailSourceViewController.view.bounds.midY - rect.midY) + transform = scaleUp.concatenating(translation) + } else { + transform = CGAffineTransform(scaleX: 1.25, y: 1.25) + } + + if isEnteringDetail { + toSnapshot.transform = transform.inverted() + } else { + toSnapshot.alpha = 0 + toSnapshot.transform = transform + } + + let totalHeight = containerView.bounds.size.height + let tabBar = self.detailSourceViewController.tabBarController?.tabBar + let tabBarSnapshot: UITabBar? + if let tb = tabBar { + tabBarSnapshot = UITabBar(frame: tb.frame) + var selectedItem: UITabBarItem? = nil + let copiedItems: [UITabBarItem]? = tb.items?.compactMap { (item: UITabBarItem) -> UITabBarItem in + let copiedItem = UITabBarItem(title: item.title, image: item.image, selectedImage: item.selectedImage) + copiedItem.badgeValue = item.badgeValue + if item === tb.selectedItem { + selectedItem = copiedItem + } + return copiedItem + } + tabBarSnapshot?.items = copiedItems + tabBarSnapshot?.apply(theme: theme) + tabBarSnapshot?.selectedItem = selectedItem + } else { + tabBarSnapshot = nil + } + + let tabBarDeltaY = totalHeight - (tabBar?.frame.minY ?? totalHeight) + let tabBarHiddenTransform = CGAffineTransform(translationX: 0, y: tabBarDeltaY) + if let tb = tabBar, let tbs = tabBarSnapshot { + tabBar?.alpha = 0 + tbs.alpha = 1 + tbs.frame = CGRect(x: 0, y: containerView.frame.height - tb.frame.height, width: tb.frame.width, height: tb.frame.height) // hack, it's already positioned off screen here + if isEnteringDetail { + tbs.transform = .identity + } else { + tbs.transform = tabBarHiddenTransform + } + containerView.addSubview(tbs) + } + + let duration = self.transitionDuration(using: transitionContext) + UIView.animateKeyframes(withDuration: duration, delay: 0, options: [], animations: { + toSnapshot.transform = .identity + if isEnteringDetail { + fromSnapshot.alpha = 0 + fromSnapshot.transform = transform + } else { + toSnapshot.alpha = 1 + fromSnapshot.transform = transform.inverted() + } + if let tbs = tabBarSnapshot { + if isEnteringDetail { + tbs.transform = tabBarHiddenTransform + } else { + tbs.transform = .identity + } + } + }) { (finished) in + if let tbs = tabBarSnapshot { + tbs.transform = .identity + tbs.removeFromSuperview() + } + tabBar?.alpha = 1 + backgroundView.removeFromSuperview() + toSnapshot.removeFromSuperview() + fromSnapshot.removeFromSuperview() + transitionContext.completeTransition(true) + } + } + +} + diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffContainerViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DiffContainerViewController.swift new file mode 100644 index 0000000..ae4c15b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffContainerViewController.swift @@ -0,0 +1,1182 @@ +import UIKit +import CocoaLumberjackSwift + +struct StubRevisionModel { + let revisionId: Int + let summary: String + let username: String + let timestamp: Date +} + +protocol DiffRevisionRetrieving: AnyObject { + func retrievePreviousRevision(with sourceRevision: WMFPageHistoryRevision) -> WMFPageHistoryRevision? + func retrieveNextRevision(with sourceRevision: WMFPageHistoryRevision) -> WMFPageHistoryRevision? +} + +class DiffContainerViewController: ViewController { + + struct NextPrevModel { + let from: WMFPageHistoryRevision + let to: WMFPageHistoryRevision + } + + private var containerViewModel: DiffContainerViewModel + private var headerExtendedView: DiffHeaderExtendedView? + private var headerTitleView: DiffHeaderTitleView? + private var scrollingEmptyViewController: EmptyViewController? + private var diffListViewController: DiffListViewController? + private var diffToolbarView: DiffToolbarView? + private let diffController: DiffController + private var fromModel: WMFPageHistoryRevision? + private var fromModelRevisionID: Int? + private var toModel: WMFPageHistoryRevision? + private let toModelRevisionID: Int? + private let siteURL: URL + private var articleTitle: String? + private let needsSetNavDelegate: Bool + private let safeAreaBottomAlignView = UIView() + + private let type: DiffContainerViewModel.DiffType + + private let revisionRetrievingDelegate: DiffRevisionRetrieving? + private var firstRevision: WMFPageHistoryRevision? + + var animateDirection: DiffRevisionTransition.Direction? + + lazy private(set) var fakeProgressController: FakeProgressController = { + let progressController = FakeProgressController(progress: navigationBar, delegate: navigationBar) + progressController.delay = 0.0 + return progressController + }() + + var hintController: HintController? + + private var prevModel: NextPrevModel? { + didSet { + diffToolbarView?.setPreviousButtonState(isEnabled: prevModel != nil) + } + } + private var nextModel: NextPrevModel? { + didSet { + diffToolbarView?.setNextButtonState(isEnabled: nextModel != nil) + } + } + + private var isOnFirstRevisionInHistory: Bool { + if type == .single, + let toModel = toModel, + let firstRevision = firstRevision, + fromModel == nil, + toModel.revisionID == firstRevision.revisionID { + return true + } + + return false + } + + private var isOnSecondRevisionInHistory: Bool { + if type == .single, + toModel != nil, + let fromModel = fromModel, + let firstRevision = firstRevision, + fromModel.revisionID == firstRevision.revisionID { + return true + } + + return false + } + + private var byteDifference: Int? { + guard let toModel = toModel, + type == .single else { + return nil + } + + if toModel.revisionSize == 0 { // indication that this has not been calculated yet from Page History, need fromModel for calculation + + guard let fromModel = fromModel else { + return isOnFirstRevisionInHistory ? toModel.articleSizeAtRevision : nil + } + + return toModel.articleSizeAtRevision - fromModel.articleSizeAtRevision + } else { + return toModel.revisionSize + } + } + + init(siteURL: URL, theme: Theme, fromRevisionID: Int?, toRevisionID: Int?, type: DiffContainerViewModel.DiffType, articleTitle: String?, needsSetNavDelegate: Bool = false) { + + self.siteURL = siteURL + self.type = type + self.articleTitle = articleTitle + self.toModelRevisionID = toRevisionID + self.fromModelRevisionID = fromRevisionID + + self.diffController = DiffController(siteURL: siteURL, pageHistoryFetcher: nil, revisionRetrievingDelegate: nil, type: type) + self.containerViewModel = DiffContainerViewModel(type: type, fromModel: nil, toModel: nil, listViewModel: nil, articleTitle: articleTitle, byteDifference: nil, theme: theme) + + self.firstRevision = nil + self.revisionRetrievingDelegate = nil + self.toModel = nil + self.fromModel = nil + self.needsSetNavDelegate = needsSetNavDelegate + + super.init() + + self.theme = theme + + self.containerViewModel.stateHandler = { [weak self] oldState in + self?.evaluateState(oldState: oldState) + } + } + + init(articleTitle: String, siteURL: URL, type: DiffContainerViewModel.DiffType, fromModel: WMFPageHistoryRevision?, toModel: WMFPageHistoryRevision, pageHistoryFetcher: PageHistoryFetcher? = nil, theme: Theme, revisionRetrievingDelegate: DiffRevisionRetrieving?, firstRevision: WMFPageHistoryRevision?, needsSetNavDelegate: Bool = false) { + + self.type = type + + self.fromModel = fromModel + self.toModel = toModel + self.toModelRevisionID = toModel.revisionID + self.articleTitle = articleTitle + self.revisionRetrievingDelegate = revisionRetrievingDelegate + self.siteURL = siteURL + self.firstRevision = firstRevision + + self.diffController = DiffController(siteURL: siteURL, pageHistoryFetcher: pageHistoryFetcher, revisionRetrievingDelegate: revisionRetrievingDelegate, type: type) + + self.containerViewModel = DiffContainerViewModel(type: type, fromModel: fromModel, toModel: toModel, listViewModel: nil, articleTitle: articleTitle, byteDifference: nil, theme: theme) + + self.needsSetNavDelegate = needsSetNavDelegate + + super.init() + + self.theme = theme + + self.containerViewModel.stateHandler = { [weak self] oldState in + self?.evaluateState(oldState: oldState) + } + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + setupBackButton() + + let onLoad = { [weak self] in + + guard let self = self else { return } + + if self.fromModel == nil { + self.fetchFromModelAndFinishSetup() + } else if self.toModel == nil { + self.fetchToModelAndFinishSetup() + } else { + self.midSetup() + self.completeSetup() + } + } + + startSetup() + if toModel == nil { + populateModelsFromDeepLink { [weak self] (error) in + + guard let self = self else { return } + + if let error = error { + self.containerViewModel.state = .error(error: error) + return + } + + onLoad() + } + } else { + onLoad() + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + if let toolbarView = diffToolbarView { + view.bringSubviewToFront(toolbarView) + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + switch type { + case .compare: + self.showDiffPanelOnce() + case .single: + break + } + + resetPrevNextAnimateState() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + if let scrollView = diffListViewController?.scrollView { + configureExtendedViewSquishing(scrollView: scrollView) + } + + if let emptyViewController = scrollingEmptyViewController { + navigationBar.setNeedsLayout() + navigationBar.layoutSubviews() + let bottomSafeAreaHeight = safeAreaBottomAlignView.frame.height + let bottomHeight = bottomSafeAreaHeight + let targetRect = CGRect(x: 0, y: navigationBar.visibleHeight, width: emptyViewController.view.frame.width, height: emptyViewController.view.frame.height - navigationBar.visibleHeight - bottomHeight) + + let convertedTargetRect = view.convert(targetRect, to: emptyViewController.view) + emptyViewController.centerEmptyView(within: convertedTargetRect) + } + } + + override func apply(theme: Theme) { + + super.apply(theme: theme) + + guard isViewLoaded else { + return + } + + switch containerViewModel.state { + case .empty, .error: + view.backgroundColor = theme.colors.midBackground + default: + view.backgroundColor = theme.colors.paperBackground + } + + headerTitleView?.apply(theme: theme) + headerExtendedView?.apply(theme: theme) + diffListViewController?.apply(theme: theme) + scrollingEmptyViewController?.apply(theme: theme) + diffToolbarView?.apply(theme: theme) + } + + private func setupBackButton() { + let buttonTitle: String + switch type { + case .compare: buttonTitle = CommonStrings.compareRevisionsTitle + case .single: + guard let toDate = toModel?.revisionDate as NSDate? else { + return + } + let dateString = toDate.wmf_fullyLocalizedRelativeDateStringFromLocalDateToNow() + buttonTitle = String.localizedStringWithFormat(CommonStrings.revisionMadeFormat, dateString.lowercased()) + } + navigationItem.backButtonTitle = buttonTitle + navigationItem.backButtonDisplayMode = .generic + } +} + +// MARK: Private + +private extension DiffContainerViewController { + + func populateNewHeaderViewModel() { + guard let toModel = toModel, + let articleTitle = articleTitle else { + assertionFailure("tomModel and articleTitle need to be in place for generating header.") + return + } + + self.containerViewModel.headerViewModel = DiffHeaderViewModel(diffType: type, fromModel: self.fromModel, toModel: toModel, articleTitle: articleTitle, byteDifference: byteDifference, theme: self.theme) + } + + func resetPrevNextAnimateState() { + animateDirection = nil + } + + func populateModelsFromDeepLink(completion: @escaping (_ error: Error?) -> Void) { + guard toModel == nil else { + assertionFailure("Why are you calling this if you already have toModel?") + return + } + + diffController.populateModelsFromDeepLink(fromRevisionID: fromModelRevisionID, toRevisionID: toModelRevisionID, articleTitle: articleTitle) { (result) in + switch result { + case .success(let response): + DispatchQueue.main.async { + self.fromModel = response.from + self.toModel = response.to + self.firstRevision = response.first + self.articleTitle = response.articleTitle + completion(nil) + } + case .failure(let error): + DispatchQueue.main.async { + completion(error) + } + } + } + } + + func fetchToModelAndFinishSetup() { + guard let fromModel = fromModel, + let articleTitle = articleTitle else { + assertionFailure("fromModel and articleTitle must be populated for fetching toModel") + return + } + + diffController.fetchAdjacentRevisionModel(sourceRevision: fromModel, direction: .next, articleTitle: articleTitle) { (result) in + switch result { + case .success(let revision): + DispatchQueue.main.async { + self.toModel = revision + self.midSetup() + self.completeSetup() + } + case .failure(let error): + DispatchQueue.main.async { + self.containerViewModel.state = .error(error: error) + } + } + } + } + + func fetchFromModelAndFinishSetup() { + + guard let toModel = toModel, + let articleTitle = articleTitle else { + assertionFailure("toModel and articleTitle must be populated for fetching fromModel") + return + } + + // early exit for first revision + // there is no previous revision from toModel in this case + if isOnFirstRevisionInHistory { + midSetup() + completeSetup() + return + } + + diffController.fetchAdjacentRevisionModel(sourceRevision: toModel, direction: .previous, articleTitle: articleTitle) { (result) in + switch result { + case .success(let revision): + DispatchQueue.main.async { + self.fromModel = revision + + self.midSetup() + self.completeSetup() + } + case .failure(let error): + DispatchQueue.main.async { + self.containerViewModel.state = .error(error: error) + } + } + } + } + + func startSetup() { + setupToolbarIfNeeded() + containerViewModel.state = .loading + + // For some reason this is needed when coming from Article As A Living Document screens + if needsSetNavDelegate { + navigationController?.delegate = self + } + } + + func midSetup() { + guard toModel != nil else { + assertionFailure("Expecting at least toModel to be populated for this method.") + return + } + + populateNewHeaderViewModel() + setupHeaderViewIfNeeded() + setupDiffListViewControllerIfNeeded() + fetchIntermediateCountIfNeeded() + fetchEditCountIfNeeded() + setupBackButton() + apply(theme: theme) + } + + func completeSetup() { + + guard toModel != nil, + (fromModel != nil || isOnFirstRevisionInHistory) else { + assertionFailure("Both models must be populated at this point or needs to be on the first revision.") + return + } + + if isOnFirstRevisionInHistory { + fetchFirstDiff() + } else { + fetchDiff() + } + + // Still need models for enabling/disabling prev/next buttons + populatePrevNextModelsForToolbar() + } + + func setThankAndShareState(isEnabled: Bool) { + diffToolbarView?.setThankButtonState(isEnabled: isEnabled) + diffToolbarView?.setShareButtonState(isEnabled: isEnabled) + diffToolbarView?.apply(theme: theme) + } + + func populatePrevNextModelsForToolbar() { + + guard let toModel = toModel, + let articleTitle = articleTitle, + (fromModel != nil || isOnFirstRevisionInHistory) else { + assertionFailure("Both models and articleTitle must be populated at this point or needs to be on the first revision.") + return + } + + // populate nextModel for enabling previous/next button + let nextFromModel = toModel + var nextToModel: WMFPageHistoryRevision? + diffController.fetchAdjacentRevisionModel(sourceRevision: nextFromModel, direction: .next, articleTitle: articleTitle) { [weak self] (result) in + + guard let self = self else { + return + } + + switch result { + case .success(let revision): + DispatchQueue.main.async { + nextToModel = revision + if let nextToModel = nextToModel { + self.nextModel = NextPrevModel(from: nextFromModel, to: nextToModel) + } + } + case .failure: + break + } + } + + // if on first or second revision, fromModel will be nil and attempting to fetch previous revision will fail. + if isOnFirstRevisionInHistory { + diffToolbarView?.setNextButtonState(isEnabled: true) + return + } + + if isOnSecondRevisionInHistory { + diffToolbarView?.setPreviousButtonState(isEnabled: true) + return + } + + guard let fromModel = fromModel else { + return + } + + // populate prevModel for enabling previous/next button + var prevFromModel: WMFPageHistoryRevision? + let prevToModel = fromModel + diffController.fetchAdjacentRevisionModel(sourceRevision: prevToModel, direction: .previous, articleTitle: articleTitle) { [weak self] (result) in + + guard let self = self else { + return + } + + switch result { + case .success(let revision): + DispatchQueue.main.async { + prevFromModel = revision + if let prevFromModel = prevFromModel { + self.prevModel = NextPrevModel(from: prevFromModel, to: prevToModel) + } + } + case .failure(let error): + DDLogError("error fetching revision: \(error)") + break + } + } + } + + func fullRevisionDiffURL() -> URL? { + + guard let toModel = toModel else { + return nil + } + + var components = URLComponents(url: siteURL, resolvingAgainstBaseURL: false) + components?.path = "/w/index.php" + components?.queryItems = [ + URLQueryItem(name: "title", value: articleTitle), + URLQueryItem(name: "diff", value: String(toModel.revisionID)), + URLQueryItem(name: "oldid", value: String(toModel.parentID)) + ] + return components?.url + } + + func evaluateState(oldState: DiffContainerViewModel.State) { + + // need up update background color if state is error/empty or not + switch containerViewModel.state { + case .error, .empty: + switch oldState { + case .error, .empty: + break + default: + apply(theme: theme) + diffToolbarView?.parentViewState = containerViewModel.state + } + default: + switch oldState { + case .error, .empty: + apply(theme: theme) + diffToolbarView?.parentViewState = containerViewModel.state + default: + break + } + } + + switch containerViewModel.state { + + case .loading: + fakeProgressController.start() + scrollingEmptyViewController?.view.isHidden = true + diffListViewController?.view.isHidden = true + setThankAndShareState(isEnabled: false) + case .empty: + fakeProgressController.stop() + setupScrollingEmptyViewControllerIfNeeded() + switch type { + case .compare: + scrollingEmptyViewController?.type = .diffCompare + case .single: + scrollingEmptyViewController?.type = .diffSingle + } + + if let direction = animateDirection { + animateInOut(viewController: scrollingEmptyViewController, direction: direction) + } else { + scrollingEmptyViewController?.view.isHidden = false + } + + diffListViewController?.view.isHidden = true + setThankAndShareState(isEnabled: true) + case .error(let error): + fakeProgressController.stop() + showNoInternetConnectionAlertOrOtherWarning(from: error) + setupScrollingEmptyViewControllerIfNeeded() + switch type { + case .compare: + scrollingEmptyViewController?.type = .diffErrorCompare + case .single: + scrollingEmptyViewController?.type = .diffErrorSingle + } + scrollingEmptyViewController?.view.isHidden = false + diffListViewController?.view.isHidden = true + setThankAndShareState(isEnabled: false) + case .data: + fakeProgressController.stop() + scrollingEmptyViewController?.view.isHidden = true + + if let direction = animateDirection { + animateInOut(viewController: diffListViewController, direction: direction) + } else { + diffListViewController?.view.isHidden = false + } + + setThankAndShareState(isEnabled: true) + } + } + + func animateInOut(viewController: UIViewController?, direction: DiffRevisionTransition.Direction) { + viewController?.view.alpha = 0 + viewController?.view.isHidden = false + + if let oldFrame = viewController?.view.frame { + let newY = direction == .down ? oldFrame.maxY : oldFrame.minY - oldFrame.height + let newFrame = CGRect(x: oldFrame.minX, y: newY, width: oldFrame.width, height: oldFrame.height) + viewController?.view.frame = newFrame + + UIView.animate(withDuration: DiffRevisionTransition.duration, delay: 0.0, options: .curveEaseInOut, animations: { + viewController?.view.alpha = 1 + viewController?.view.frame = oldFrame + }, completion: nil) + } + } + + func fetchEditCountIfNeeded() { + + guard let toModel = toModel else { + return + } + + switch type { + case .single: + if let username = toModel.user { + diffController.fetchEditCount(guiUser: username) { [weak self] (result) in + + guard let self = self else { + return + } + + DispatchQueue.main.async { + switch result { + case .success(let editCount): + self.updateHeaderWithEditCount(editCount) + case .failure: + break + } + } + } + } + case .compare: + break + } + } + + func fetchIntermediateCountIfNeeded() { + + guard let toModel = toModel, + let articleTitle = articleTitle else { + return + } + + switch type { + case .compare: + if let fromModel = fromModel { + let fromID = fromModel.revisionID + let toID = toModel.revisionID + diffController.fetchIntermediateCounts(for: articleTitle, pageURL: siteURL, from: fromID, to: toID) { [weak self] (result) in + switch result { + case .success(let editCounts): + guard let self = self else { + return + } + DispatchQueue.main.async { + self.updateHeaderWithIntermediateCounts(editCounts) + self.diffListViewController?.updateScrollViewInsets() + } + default: + break + } + } + } else { + assertionFailure("Expect compare type to have fromModel for fetching intermediate count") + } + case .single: + break + } + } + + func updateHeaderWithIntermediateCounts(_ editCounts: EditCountsGroupedByType) { + switch type { + case .compare: + guard let headerViewModel = containerViewModel.headerViewModel, + let articleTitle = articleTitle else { + return + } + let newTitleViewModel = DiffHeaderViewModel.generateTitleViewModelForCompare(articleTitle: articleTitle, editCounts: editCounts) + headerViewModel.title = newTitleViewModel + headerTitleView?.update(newTitleViewModel) + case .single: + assertionFailure("Should not call this method for the compare type.") + } + } + + func updateHeaderWithEditCount(_ editCount: Int) { + + // update view model + guard let headerViewModel = containerViewModel.headerViewModel else { + return + } + + switch headerViewModel.headerType { + case .single(let editorViewModel, _): + editorViewModel.numberOfEdits = editCount + case .compare: + assertionFailure("Should not call this method for the compare type.") + return + } + + // update view + headerExtendedView?.update(headerViewModel) + } + + func fetchFirstDiff() { + guard let toModel = toModel, + isOnFirstRevisionInHistory else { + return + } + + view.setNeedsLayout() + view.layoutIfNeeded() + let width = diffListViewController?.collectionView.frame.width + + diffController.fetchFirstRevisionDiff(revisionId: toModel.revisionID, siteURL: siteURL, theme: theme, traitCollection: traitCollection) { [weak self] (result) in + + guard let self = self else { + return + } + + switch result { + case .success(let listViewModel): + + self.updateListViewController(with: listViewModel, collectionViewWidth: width) + + case .failure(let error): + DispatchQueue.main.async { + self.containerViewModel.state = .error(error: error) + } + } + } + } + + func updateListViewController(with listViewModels: [DiffListGroupViewModel], collectionViewWidth: CGFloat?) { + + assert(!Thread.isMainThread, "diffListViewController.updateListViewModels could take a while, would be better from a background thread.") + + self.containerViewModel.listViewModel = listViewModels + self.diffListViewController?.updateListViewModels(listViewModel: listViewModels, updateType: .initialLoad(width: collectionViewWidth ?? 0)) + + DispatchQueue.main.async { + self.diffListViewController?.applyListViewModelChanges(updateType: .initialLoad(width: collectionViewWidth ?? 0)) + + self.diffListViewController?.updateScrollViewInsets() + + self.containerViewModel.state = listViewModels.count == 0 ? .empty : .data + } + } + + func fetchDiff() { + + guard let toModel = toModel, + let fromModel = fromModel else { + return + } + + view.setNeedsLayout() + view.layoutIfNeeded() + let width = diffListViewController?.collectionView.frame.width + + diffController.fetchDiff(fromRevisionId: fromModel.revisionID, toRevisionId: toModel.revisionID, theme: theme, traitCollection: traitCollection) { [weak self] (result) in + + guard let self = self else { + return + } + + switch result { + case .success(let listViewModel): + + self.updateListViewController(with: listViewModel, collectionViewWidth: width) + + case .failure(let error): + DispatchQueue.main.async { + self.containerViewModel.state = .error(error: error) + } + } + } + } + + func configureExtendedViewSquishing(scrollView: UIScrollView) { + guard let headerTitleView = headerTitleView, + let headerExtendedView = headerExtendedView else { + return + } + + let beginSquishYOffset = headerTitleView.frame.height + let scrollYOffset = scrollView.contentOffset.y + scrollView.adjustedContentInset.top + headerExtendedView.configureHeight(beginSquishYOffset: beginSquishYOffset, scrollYOffset: scrollYOffset) + } + + func setupHeaderViewIfNeeded() { + + guard let headerViewModel = containerViewModel.headerViewModel else { + return + } + + if self.headerTitleView == nil { + let headerTitleView = DiffHeaderTitleView(frame: .zero) + headerTitleView.translatesAutoresizingMaskIntoConstraints = false + + navigationBar.isUnderBarViewHidingEnabled = true + navigationBar.allowsUnderbarHitsFallThrough = true + navigationBar.addUnderNavigationBarView(headerTitleView) + navigationBar.underBarViewPercentHiddenForShowingTitle = 0.6 + navigationBar.isShadowShowing = false + + self.headerTitleView = headerTitleView + } + + if self.headerExtendedView == nil { + let headerExtendedView = DiffHeaderExtendedView(frame: .zero) + headerExtendedView.translatesAutoresizingMaskIntoConstraints = false + + navigationBar.allowsUnderbarHitsFallThrough = true + navigationBar.allowsExtendedHitsFallThrough = true + navigationBar.addExtendedNavigationBarView(headerExtendedView) + headerExtendedView.delegate = self + + self.headerExtendedView = headerExtendedView + } + + navigationBar.isBarHidingEnabled = false + useNavigationBarVisibleHeightForScrollViewInsets = true + + switch headerViewModel.headerType { + case .compare(_, let navBarTitle): + navigationBar.title = navBarTitle + default: + break + } + + headerTitleView?.update(headerViewModel.title) + headerExtendedView?.update(headerViewModel) + navigationBar.isExtendedViewHidingEnabled = headerViewModel.isExtendedViewHidingEnabled + } + + func setupScrollingEmptyViewControllerIfNeeded() { + + guard scrollingEmptyViewController == nil else { + return + } + + scrollingEmptyViewController = EmptyViewController(nibName: "EmptyViewController", bundle: nil) + if let emptyViewController = scrollingEmptyViewController, + let emptyView = emptyViewController.view { + emptyViewController.canRefresh = false + emptyViewController.theme = theme + + setupSafeAreaBottomAlignView() + + addChild(emptyViewController) + emptyView.translatesAutoresizingMaskIntoConstraints = false + view.insertSubview(emptyView, belowSubview: navigationBar) + let bottomAnchor = diffToolbarView?.topAnchor ?? safeAreaBottomAlignView.bottomAnchor + let bottom = emptyView.bottomAnchor.constraint(equalTo: bottomAnchor) + let leading = view.leadingAnchor.constraint(equalTo: emptyView.leadingAnchor) + let trailing = view.trailingAnchor.constraint(equalTo: emptyView.trailingAnchor) + let top = view.topAnchor.constraint(equalTo: emptyView.topAnchor) + NSLayoutConstraint.activate([top, leading, trailing, bottom]) + emptyViewController.didMove(toParent: self) + + emptyViewController.view.isHidden = true + emptyViewController.delegate = self + + } + } + + func setupSafeAreaBottomAlignView() { + // add alignment view view + safeAreaBottomAlignView.translatesAutoresizingMaskIntoConstraints = false + safeAreaBottomAlignView.isHidden = true + view.addSubview(safeAreaBottomAlignView) + let leadingConstraint = view.leadingAnchor.constraint(equalTo: safeAreaBottomAlignView.leadingAnchor) + let bottomAnchor = diffToolbarView?.topAnchor ?? view.safeAreaLayoutGuide.bottomAnchor + let bottomConstraint = safeAreaBottomAlignView.bottomAnchor.constraint(equalTo: bottomAnchor) + let widthAnchor = safeAreaBottomAlignView.widthAnchor.constraint(equalToConstant: 1) + let heightAnchor = safeAreaBottomAlignView.heightAnchor.constraint(equalToConstant: 1) + NSLayoutConstraint.activate([leadingConstraint, bottomConstraint, widthAnchor, heightAnchor]) + } + + func setupToolbarIfNeeded() { + + switch type { + case .single: + if diffToolbarView == nil { + let toolbarView = DiffToolbarView(frame: .zero) + self.diffToolbarView = toolbarView + toolbarView.delegate = self + toolbarView.translatesAutoresizingMaskIntoConstraints = false + view.insertSubview(toolbarView, aboveSubview: navigationBar) + let bottom = view.bottomAnchor.constraint(equalTo: toolbarView.bottomAnchor) + let leading = view.leadingAnchor.constraint(equalTo: toolbarView.leadingAnchor) + let trailing = view.trailingAnchor.constraint(equalTo: toolbarView.trailingAnchor) + NSLayoutConstraint.activate([bottom, leading, trailing]) + toolbarView.apply(theme: theme) + toolbarView.setPreviousButtonState(isEnabled: false) + toolbarView.setNextButtonState(isEnabled: false) + } + default: + break + } + + } + + func setupDiffListViewControllerIfNeeded() { + if diffListViewController == nil { + let diffListViewController = DiffListViewController(theme: theme, delegate: self, type: type) + self.diffListViewController = diffListViewController + + switch type { + case .single: + if let listView = diffListViewController.view, + let toolbarView = diffToolbarView { + addChild(diffListViewController) + listView.translatesAutoresizingMaskIntoConstraints = false + view.insertSubview(listView, belowSubview: navigationBar) + let bottom = toolbarView.topAnchor.constraint(equalTo: listView.bottomAnchor) + let leading = view.leadingAnchor.constraint(equalTo: listView.leadingAnchor) + let trailing = view.trailingAnchor.constraint(equalTo: listView.trailingAnchor) + let top = view.topAnchor.constraint(equalTo: listView.topAnchor) + NSLayoutConstraint.activate([top, leading, trailing, bottom]) + diffListViewController.didMove(toParent: self) + } + case .compare: + wmf_add(childController: diffListViewController, andConstrainToEdgesOfContainerView: view, belowSubview: navigationBar) + } + + + } + } + + func showDiffPanelOnce() { + let key = "didShowDiffPanel" + if UserDefaults.standard.bool(forKey: key) { + return + } + let panelVC = DiffEducationalPanelViewController(showCloseButton: false, primaryButtonTapHandler: { [weak self] (action) in + self?.presentedViewController?.dismiss(animated: true) + }, secondaryButtonTapHandler: nil, dismissHandler: nil, discardDismissHandlerOnPrimaryButtonTap: true, theme: theme) + present(panelVC, animated: true) + UserDefaults.standard.set(true, forKey: key) + } + + func showNoInternetConnectionAlertOrOtherWarning(from error: Error, noInternetConnectionAlertMessage: String = CommonStrings.noInternetConnection) { + + if (error as NSError).wmf_isNetworkConnectionError() { + + if UIAccessibility.isVoiceOverRunning { + UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: noInternetConnectionAlertMessage) + } else { + WMFAlertManager.sharedInstance.showErrorAlertWithMessage(noInternetConnectionAlertMessage, sticky: true, dismissPreviousAlerts: true) + } + + } else if let diffError = error as? DiffError { + + if UIAccessibility.isVoiceOverRunning { + UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: diffError.localizedDescription) + } else { + WMFAlertManager.sharedInstance.showWarningAlert(diffError.localizedDescription, sticky: true, dismissPreviousAlerts: true) + } + + } else { + + if UIAccessibility.isVoiceOverRunning { + UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: error.localizedDescription) + } else { + WMFAlertManager.sharedInstance.showErrorAlertWithMessage(error.localizedDescription, sticky: true, dismissPreviousAlerts: true) + } + + } + } +} + +extension DiffContainerViewController: DiffListDelegate { + func diffListScrollViewDidScroll(_ scrollView: UIScrollView) { + self.scrollViewDidScroll(scrollView) + + configureExtendedViewSquishing(scrollView: scrollView) + } +} + +extension DiffContainerViewController: EmptyViewControllerDelegate { + func triggeredRefresh(refreshCompletion: @escaping () -> Void) { + // no refreshing + } + + func emptyViewScrollViewDidScroll(_ scrollView: UIScrollView) { + self.scrollViewDidScroll(scrollView) + } +} + +extension DiffContainerViewController: DiffHeaderActionDelegate { + func tappedUsername(username: String) { + if let username = username.normalizedPageTitle { + let userPageURL = siteURL.wmf_URL(withPath: "/wiki/User:\(username)", isMobile: true) + navigate(to: userPageURL) + } + } + + func tappedRevision(revisionID: Int) { + + guard let fromModel = fromModel, + let toModel = toModel, + let articleTitle = articleTitle else { + assertionFailure("Revision tapping is not supported on a page without models or articleTitle.") + return + } + + let revision: WMFPageHistoryRevision + if revisionID == fromModel.revisionID { + revision = fromModel + } else if revisionID == toModel.revisionID { + revision = toModel + } else { + assertionFailure("Trouble determining revision model to push on next") + return + } + + EditHistoryCompareFunnel.shared.logRevisionView(url: siteURL) + + let singleDiffVC = DiffContainerViewController(articleTitle: articleTitle, siteURL: siteURL, type: .single, fromModel: nil, toModel: revision, theme: theme, revisionRetrievingDelegate: revisionRetrievingDelegate, firstRevision: firstRevision, needsSetNavDelegate: needsSetNavDelegate) + push(singleDiffVC, animated: true) + } +} + +extension DiffContainerViewController: ThanksGiving { + var url: URL? { + return siteURL + } + + var bottomSpacing: CGFloat? { + return diffToolbarView?.toolbarHeight + } + + func didLogIn() { + self.apply(theme: self.theme) + } + + func wereThanksSent(for revisionID: Int) -> Bool { + return diffToolbarView?.isThankSelected ?? false + } + + func thanksWereSent(for revisionID: Int) { + diffToolbarView?.isThankSelected = true + } +} + +class AuthorAlreadyThankedHintVC: HintViewController { + override func configureSubviews() { + viewType = .warning + warningLabel.text = WMFLocalizedString("diff-thanks-sent-already", value: "You’ve already sent a ‘Thanks’ for this edit", comment: "Message indicating thanks was already sent") + warningSubtitleLabel.text = WMFLocalizedString("diff-thanks-sent-cannot-unsend", value: "Thanks cannot be unsent", comment: "Message indicating thanks cannot be unsent") + } +} + +class AnonymousUsersCannotBeThankedHintVC: HintViewController { + override func configureSubviews() { + viewType = .warning + warningLabel.text = WMFLocalizedString("diff-thanks-anonymous-no-thanks", value: "Anonymous users cannot be thanked", comment: "Message indicating anonymous users cannot be thanked") + warningSubtitleLabel.text = nil + } +} + +class RevisionAuthorThankedHintVC: HintViewController { + var recipient: String + init(recipient: String) { + self.recipient = recipient + super.init(nibName: nil, bundle: nil) + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func configureSubviews() { + viewType = .default + let thanksMessage = WMFLocalizedString("diff-thanks-sent", value: "Your 'Thanks' was sent to %1$@", comment: "Message indicating thanks was sent. Parameters:\n* %1$@ - name of user who was thanked") + let thanksMessageWithRecipient = String.localizedStringWithFormat(thanksMessage, recipient) + defaultImageView.image = UIImage(named: "selected") + defaultLabel.text = thanksMessageWithRecipient + } +} + +class RevisionAuthorThanksErrorHintVC: HintViewController { + var error: Error + init(error: Error) { + self.error = error + super.init(nibName: nil, bundle: nil) + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func configureSubviews() { + viewType = .warning + warningLabel.text = (error as NSError).alertMessage() + warningSubtitleLabel.text = nil + } +} + +extension DiffContainerViewController: DiffToolbarViewDelegate { + + private func replaceLastAndPush(with viewController: UIViewController) { + if var newViewControllers = navigationController?.viewControllers { + newViewControllers.removeLast() + newViewControllers.append(viewController) + navigationController?.setViewControllers(newViewControllers, animated: true) + } + } + + func tappedPrevious() { + + animateDirection = .down + + guard prevModel != nil || + isOnSecondRevisionInHistory else { + assertionFailure("Expecting either a prevModel populated to push or a firstRevision to push.") + return + } + + let fromModel = prevModel?.from + let maybeToModel = prevModel?.to ?? firstRevision + + guard let toModel = maybeToModel, + let articleTitle = articleTitle else { + assertionFailure("toModel and articleTitle before pushing on new DiffContainerVC") + return + } + + let singleDiffVC = DiffContainerViewController(articleTitle: articleTitle, siteURL: siteURL, type: .single, fromModel: fromModel, toModel: toModel, theme: theme, revisionRetrievingDelegate: revisionRetrievingDelegate, firstRevision: firstRevision, needsSetNavDelegate: needsSetNavDelegate) + replaceLastAndPush(with: singleDiffVC) + } + + func tappedNext() { + + animateDirection = .up + + guard let nextModel = nextModel, + let articleTitle = articleTitle else { + assertionFailure("Expecting nextModel and articleTitle to be populated. Next button should have been disabled if there's no model.") + return + } + + let singleDiffVC = DiffContainerViewController(articleTitle: articleTitle, siteURL: siteURL, type: .single, fromModel: nextModel.from, toModel: nextModel.to, theme: theme, revisionRetrievingDelegate: revisionRetrievingDelegate, firstRevision: firstRevision, needsSetNavDelegate: needsSetNavDelegate) + replaceLastAndPush(with: singleDiffVC) + } + + func tappedShare(_ sender: UIBarButtonItem) { + guard let diffURL = fullRevisionDiffURL() else { + assertionFailure("Couldn't get full revision diff URL") + return + } + + let activityViewController = UIActivityViewController(activityItems: [diffURL], applicationActivities: [TUSafariActivity()]) + activityViewController.popoverPresentationController?.barButtonItem = sender + activityViewController.excludedActivityTypes = [.addToReadingList] + + present(activityViewController, animated: true) + } + + func tappedThankButton() { + guard type == .single else { + return + } + let isUserAnonymous = toModel?.isAnon ?? true + tappedThank(for: toModelRevisionID, isUserAnonymous: isUserAnonymous) + } +} + +extension DiffContainerViewController: UINavigationControllerDelegate { + func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { + + if let direction = animateDirection { + return DiffRevisionTransition(direction: direction) + } + + return nil + } +} + +extension DiffContainerViewController: DiffRevisionAnimating { + var embeddedViewController: UIViewController? { + switch containerViewModel.state { + case .data, .loading: + return diffListViewController + case .empty, .error: + return scrollingEmptyViewController + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffContainerViewModel.swift b/Apps/Wikipedia/Wikipedia/Code/DiffContainerViewModel.swift new file mode 100644 index 0000000..1ef97ce --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffContainerViewModel.swift @@ -0,0 +1,40 @@ +import Foundation + +final class DiffContainerViewModel { + + enum DiffType { + case single + case compare + } + + enum State { + case loading + case empty + case data + case error(error: Error) + } + + var headerViewModel: DiffHeaderViewModel? + let type: DiffType + var listViewModel: [DiffListGroupViewModel]? + + var state: State = .loading { + didSet { + stateHandler?(oldValue) + } + } + var stateHandler: ((_ oldState: DiffContainerViewModel.State) -> Void)? + + init(type: DiffType, fromModel: WMFPageHistoryRevision?, toModel: WMFPageHistoryRevision?, listViewModel: [DiffListGroupViewModel]?, articleTitle: String?, byteDifference: Int?, theme: Theme) { + self.type = type + + if let toModel = toModel, + let articleTitle = articleTitle { + self.headerViewModel = DiffHeaderViewModel(diffType: type, fromModel: fromModel, toModel: toModel, articleTitle: articleTitle, byteDifference: byteDifference, theme: theme) + } else { + self.headerViewModel = nil + } + + self.listViewModel = listViewModel + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffController.swift b/Apps/Wikipedia/Wikipedia/Code/DiffController.swift new file mode 100644 index 0000000..6bf166d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffController.swift @@ -0,0 +1,261 @@ +import Foundation + + +enum DiffError: Error { + case generateUrlFailure + case missingDiffResponseFailure + case missingUrlResponseFailure + case fetchRevisionConstructTitleFailure + case unrecognizedHardcodedIdsForIntermediateCounts + case failureToPopulateModelsFromDeepLink + case failureToVerifyRevisionIDs + + var localizedDescription: String { + return CommonStrings.genericErrorDescription + } +} + +class DiffController { + + enum RevisionDirection { + case next + case previous + } + + let diffFetcher: DiffFetcher + let pageHistoryFetcher: PageHistoryFetcher? + let globalUserInfoFetcher: GlobalUserInfoFetcher + let siteURL: URL + let type: DiffContainerViewModel.DiffType + private weak var revisionRetrievingDelegate: DiffRevisionRetrieving? + let transformer: DiffTransformer + + init(siteURL: URL, diffFetcher: DiffFetcher = DiffFetcher(), pageHistoryFetcher: PageHistoryFetcher?, revisionRetrievingDelegate: DiffRevisionRetrieving?, type: DiffContainerViewModel.DiffType) { + + self.diffFetcher = diffFetcher + self.pageHistoryFetcher = pageHistoryFetcher + self.globalUserInfoFetcher = GlobalUserInfoFetcher() + self.siteURL = siteURL + self.revisionRetrievingDelegate = revisionRetrievingDelegate + self.type = type + self.transformer = DiffTransformer(type: type, siteURL: siteURL) + } + + func fetchEditCount(guiUser: String, completion: @escaping ((Result) -> Void)) { + + globalUserInfoFetcher.fetchEditCount(guiUser: guiUser, siteURL: siteURL, completion: completion) + } + + func fetchIntermediateCounts(for pageTitle: String, pageURL: URL, from fromRevisionID: Int , to toRevisionID: Int, completion: @escaping (Result) -> Void) { + pageHistoryFetcher?.fetchEditCounts(.edits, .editors, for: pageTitle, pageURL: pageURL, from: fromRevisionID, to: toRevisionID, completion: completion) + } + + func fetchFirstRevisionModel(articleTitle: String, completion: @escaping ((Result) -> Void)) { + + guard let articleTitle = articleTitle.normalizedPageTitle else { + completion(.failure(DiffError.fetchRevisionConstructTitleFailure)) + return + } + + diffFetcher.fetchFirstRevisionModel(siteURL: siteURL, articleTitle: articleTitle, completion: completion) + } + + struct DeepLinkModelsResponse { + let from: WMFPageHistoryRevision? + let to: WMFPageHistoryRevision? + let first: WMFPageHistoryRevision + let articleTitle: String + } + + func populateModelsFromDeepLink(fromRevisionID: Int?, toRevisionID: Int?, articleTitle: String?, completion: @escaping ((Result) -> Void)) { + + if let articleTitle = articleTitle { + populateModelsFromDeepLink(fromRevisionID: fromRevisionID, toRevisionID: toRevisionID, articleTitle: articleTitle, completion: completion) + return + } + + let maybeRevisionID = toRevisionID ?? fromRevisionID + + guard let revisionID = maybeRevisionID else { + completion(.failure(DiffError.failureToVerifyRevisionIDs)) + return + } + + diffFetcher.fetchArticleTitle(siteURL: siteURL, revisionID: revisionID) { (result) in + switch result { + case .success(let title): + + self.populateModelsFromDeepLink(fromRevisionID: fromRevisionID, toRevisionID: toRevisionID, articleTitle: title, completion: completion) + case .failure(let error): + completion(.failure(error)) + } + } + + } + + private func populateModelsFromDeepLink(fromRevisionID: Int?, toRevisionID: Int?, articleTitle: String, completion: @escaping ((Result) -> Void)) { + guard let articleTitle = articleTitle.normalizedPageTitle else { + completion(.failure(DiffError.fetchRevisionConstructTitleFailure)) + return + } + + var fromResponse: WMFPageHistoryRevision? + var toResponse: WMFPageHistoryRevision? + var firstResponse: WMFPageHistoryRevision? + + let group = DispatchGroup() + + if let fromRevisionID = fromRevisionID { + + group.enter() + let fromRequest = DiffFetcher.FetchRevisionModelRequest.populateModel(revisionID: fromRevisionID) + diffFetcher.fetchRevisionModel(siteURL, articleTitle: articleTitle, request: fromRequest) { (result) in + switch result { + case .success(let fetchResponse): + fromResponse = fetchResponse + case .failure: + break + } + group.leave() + } + } + + if let toRevisionID = toRevisionID { + group.enter() + let toRequest = DiffFetcher.FetchRevisionModelRequest.populateModel(revisionID: toRevisionID) + diffFetcher.fetchRevisionModel(siteURL, articleTitle: articleTitle, request: toRequest) { (result) in + switch result { + case .success(let fetchResponse): + toResponse = fetchResponse + case .failure: + break + } + group.leave() + } + } + + group.enter() + diffFetcher.fetchFirstRevisionModel(siteURL: siteURL, articleTitle: articleTitle) { (result) in + switch result { + case .success(let fetchResponse): + firstResponse = fetchResponse + case .failure: + break + } + group.leave() + } + + group.notify(queue: DispatchQueue.global(qos: .userInitiated)) { + guard let firstResponse = firstResponse, + (fromResponse != nil || toResponse != nil) else { + completion(.failure(DiffError.failureToPopulateModelsFromDeepLink)) + return + } + + let response = DeepLinkModelsResponse(from: fromResponse, to: toResponse, first: firstResponse, articleTitle: articleTitle) + completion(.success(response)) + } + } + + func fetchAdjacentRevisionModel(sourceRevision: WMFPageHistoryRevision, direction: RevisionDirection, articleTitle: String, completion: @escaping ((Result) -> Void)) { + + if let revisionRetrievingDelegate = revisionRetrievingDelegate { + + // optimization - first try to grab a revision we might already have in memory from the revisionRetrievingDelegate + switch direction { + case .next: + if let nextRevision = revisionRetrievingDelegate.retrieveNextRevision(with: sourceRevision) { + completion(.success(nextRevision)) + return + } + case .previous: + if let previousRevision = revisionRetrievingDelegate.retrievePreviousRevision(with: sourceRevision) { + completion(.success(previousRevision)) + return + } + } + } + + let direction: DiffFetcher.FetchRevisionModelRequestDirection = direction == .previous ? .older : .newer + + let request = DiffFetcher.FetchRevisionModelRequest.adjacent(sourceRevision: sourceRevision, direction: direction) + + diffFetcher.fetchRevisionModel(siteURL, articleTitle: articleTitle, request: request) { (result) in + switch result { + case .success(let response): + completion(.success(response)) + case .failure(let error): + completion(.failure(error)) + } + } + } + + func fetchFirstRevisionDiff(revisionId: Int, siteURL: URL, theme: Theme, traitCollection: UITraitCollection, completion: @escaping ((Result<[DiffListGroupViewModel], Error>) -> Void)) { + + diffFetcher.fetchWikitext(siteURL: siteURL, revisionId: revisionId) { (result) in + switch result { + case .success(let wikitext): + do { + let viewModels = try self.transformer.firstRevisionViewModels(from: wikitext, theme: theme, traitCollection: traitCollection) + + completion(.success(viewModels)) + } catch let error { + completion(.failure(error)) + } + case .failure(let error): + completion(.failure(error)) + } + } + } + + func fetchDiff(fromRevisionId: Int, toRevisionId: Int, theme: Theme, traitCollection: UITraitCollection, completion: @escaping ((Result<[DiffListGroupViewModel], Error>) -> Void)) { + +// let queue = DispatchQueue.global(qos: .userInitiated) +// +// queue.async { [weak self] in +// +// guard let self = self else { return } +// +// do { +// +// let url = Bundle.main.url(forResource: "DiffResponse", withExtension: "json")! +// let data = try Data(contentsOf: url) +// let diffResponse = try JSONDecoder().decode(DiffResponse.self, from: data) +// +// +// do { +// let viewModels = try self.transformer.viewModels(from: diffResponse, theme: theme, traitCollection: traitCollection) +// +// completion(.success(viewModels)) +// } catch (let error) { +// completion(.failure(error)) +// } +// +// +// } catch (let error) { +// completion(.failure(error)) +// } +// } + + diffFetcher.fetchDiff(fromRevisionId: fromRevisionId, toRevisionId: toRevisionId, siteURL: siteURL) { [weak self] (result) in + + guard let self = self else { return } + + switch result { + case .success(let diffResponse): + + do { + let viewModels = try self.transformer.viewModels(from: diffResponse, theme: theme, traitCollection: traitCollection) + + completion(.success(viewModels)) + } catch let error { + completion(.failure(error)) + } + case .failure(let error): + completion(.failure(error)) + } + } + } + + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffFetcher.swift b/Apps/Wikipedia/Wikipedia/Code/DiffFetcher.swift new file mode 100644 index 0000000..05cfa50 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffFetcher.swift @@ -0,0 +1,279 @@ +import Foundation + +enum DiffFetcherError: Error { + case failureParsingRevisions + case failureParsingWikitext + case failureParsingTitle +} + +class DiffFetcher: Fetcher { + + enum FetchRevisionModelRequestDirection: String { + case older + case newer + } + + func fetchDiff(fromRevisionId: Int, toRevisionId: Int, siteURL: URL, completion: @escaping ((Result) -> Void)) { + + guard let url = compareURL(fromRevisionId: fromRevisionId, toRevisionId: toRevisionId, siteURL: siteURL) else { + completion(.failure(DiffError.generateUrlFailure)) + return + } + + session.jsonDecodableTask(with: url) { (result: DiffResponse?, urlResponse: URLResponse?, error: Error?) in + + guard let result = result else { + completion(.failure(DiffError.missingDiffResponseFailure)) + return + } + + guard urlResponse != nil else { + completion(.failure(DiffError.missingUrlResponseFailure)) + return + } + + if let error = error { + completion(.failure(error)) + return + } + + completion(.success(result)) + } + } + + func fetchWikitext(siteURL: URL, revisionId: Int, completion: @escaping (Result) -> Void) { + + let params: [String: Any] = [ + "action": "query", + "prop": "revisions", + "revids": "\(revisionId)", + "rvprop": "content", + "format": "json" + ] + + performMediaWikiAPIGET(for: siteURL, with: params, cancellationKey: nil) { (result, response, error) in + if let error = error { + completion(.failure(error)) + return + } + + guard let query = result?["query"] as? [String: AnyObject], + let pages = query["pages"] as? [String: AnyObject] else { + completion(.failure(DiffFetcherError.failureParsingWikitext)) + return + } + + var maybeResult: String? + for (_, value) in pages { + guard let valueDict = value as? [String: AnyObject] else { + continue + } + + if let revisionsArray = valueDict["revisions"] as? [[String: AnyObject]], + revisionsArray.count > 0 { + + for revision in revisionsArray { + + if let text = revision["*"] as? String { + maybeResult = text + break + } + + } + } + } + + guard let result = maybeResult else { + completion(.failure(DiffFetcherError.failureParsingWikitext)) + return + } + + completion(.success(result)) + } + } + + private func compareURL(fromRevisionId: Int, toRevisionId: Int, siteURL: URL) -> URL? { + + guard siteURL.host != nil else { + return nil + } + + var pathComponents = ["v1", "revision"] + pathComponents.append("\(fromRevisionId)") + pathComponents.append("compare") + pathComponents.append("\(toRevisionId)") + return configuration.mediaWikiRestAPIURLForURL(siteURL, appending: pathComponents) + } + + enum FetchRevisionModelRequest { + case adjacent(sourceRevision: WMFPageHistoryRevision, direction: FetchRevisionModelRequestDirection) + case populateModel(revisionID: Int) + } + + func fetchRevisionModel(_ siteURL: URL, articleTitle: String, request: FetchRevisionModelRequest, completion: @escaping ((Result) -> Void)) { + + let requestRevisionID: Int + var requestDirection: FetchRevisionModelRequestDirection? = nil + let requestNumberOfRevisions: Int + + switch request { + case .populateModel(let revisionID): + requestRevisionID = revisionID + requestNumberOfRevisions = 1 + case .adjacent(let sourceRevision, let direction): + requestRevisionID = sourceRevision.revisionID + requestDirection = direction + requestNumberOfRevisions = 2 + } + + var parameters: [String: Any] = [ + "action": "query", + "prop": "revisions", + "rvprop": "ids|timestamp|user|size|parsedcomment|flags", + "rvlimit": requestNumberOfRevisions, + "rvstartid": requestRevisionID, + "titles": articleTitle, + "format": "json" + ] + + if let direction = requestDirection { + parameters["rvdir"] = direction.rawValue + } + + performMediaWikiAPIGET(for: siteURL, with: parameters, cancellationKey: nil) { (result, response, error) in + + if let error = error { + completion(.failure(error)) + return + } + + guard + let query = result?["query"] as? [String : Any], + let pages = query["pages"] as? [String : Any] else { + completion(.failure(DiffFetcherError.failureParsingRevisions)) + return + } + + for (_, value) in pages { + + guard let value = value as? [String: Any] else { + completion(.failure(DiffFetcherError.failureParsingRevisions)) + return + } + + let transformer = MTLJSONAdapter.arrayTransformer(withModelClass: WMFPageHistoryRevision.self) + + guard let val = value["revisions"], + let revisions = transformer?.transformedValue(val) as? [WMFPageHistoryRevision] else { + completion(.failure(DiffFetcherError.failureParsingRevisions)) + return + } + + let filteredRevisions: [WMFPageHistoryRevision] + switch request { + case .populateModel: + filteredRevisions = revisions + case .adjacent(let sourceRevision, _): + filteredRevisions = revisions.filter { $0.revisionID != sourceRevision.revisionID } + } + guard let singleRevision = filteredRevisions.first else { + completion(.failure(DiffFetcherError.failureParsingRevisions)) + return + } + + completion(.success(singleRevision)) + return + } + + completion(.failure(DiffFetcherError.failureParsingRevisions)) + } + } + + public func fetchFirstRevisionModel(siteURL: URL, articleTitle: String, completion: @escaping (Result) -> Void) { + let parameters: [String: Any] = [ + "action": "query", + "prop": "revisions", + "rvprop": "ids|timestamp|user|size|parsedcomment|flags", + "rvlimit": 1, + "rvdir": "newer", + "titles": articleTitle, + "format": "json" + ] + + performMediaWikiAPIGET(for: siteURL, with: parameters, cancellationKey: nil) { (result, response, error) in + if let error = error { + completion(.failure(error)) + return + } + + guard + let query = result?["query"] as? [String : Any], + let pages = query["pages"] as? [String : Any] else { + completion(.failure(DiffFetcherError.failureParsingRevisions)) + return + } + + for (_, value) in pages { + + guard let value = value as? [String: Any] else { + completion(.failure(DiffFetcherError.failureParsingRevisions)) + return + } + + let transformer = MTLJSONAdapter.arrayTransformer(withModelClass: WMFPageHistoryRevision.self) + + guard let val = value["revisions"], + let revisions = transformer?.transformedValue(val) as? [WMFPageHistoryRevision], + let singleRevision = revisions.first else { + completion(.failure(DiffFetcherError.failureParsingRevisions)) + return + } + + completion(.success(singleRevision)) + return + } + + completion(.failure(DiffFetcherError.failureParsingRevisions)) + } + } + + public func fetchArticleTitle(siteURL: URL, revisionID: Int, completion: @escaping (Result) -> Void) { + let parameters: [String: Any] = [ + "action": "query", + "revids": revisionID, + "format": "json" + ] + + performMediaWikiAPIGET(for: siteURL, with: parameters, cancellationKey: nil) { (result, response, error) in + if let error = error { + completion(.failure(error)) + return + } + + guard + let query = result?["query"] as? [String : Any], + let pages = query["pages"] as? [String : Any] else { + completion(.failure(DiffFetcherError.failureParsingTitle)) + return + } + + for (_, value) in pages { + + guard let value = value as? [String: Any] else { + completion(.failure(DiffFetcherError.failureParsingTitle)) + return + } + + guard let title = value["title"] as? String else { + completion(.failure(DiffFetcherError.failureParsingTitle)) + return + } + + completion(.success(title)) + return + } + + completion(.failure(DiffFetcherError.failureParsingTitle)) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareItemView.swift b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareItemView.swift new file mode 100644 index 0000000..4ce8517 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareItemView.swift @@ -0,0 +1,169 @@ +import UIKit + +class DiffHeaderCompareItemView: UIView { + + @IBOutlet var userStackView: UIStackView! + @IBOutlet var headingAndTimestampStackView: UIStackView! + @IBOutlet var userAndSummaryStackView: UIStackView! + @IBOutlet var containerStackView: UIStackView! + @IBOutlet var contentView: UIView! + @IBOutlet var headingLabel: UILabel! + @IBOutlet var timestampLabel: UILabel! + @IBOutlet var usernameLabel: UILabel! + @IBOutlet var summaryLabel: UILabel! + @IBOutlet var userIconImageView: UIImageView! + @IBOutlet var stackViewTopPaddingConstraint: NSLayoutConstraint! + let squishedBottomPadding: CGFloat = 4 + let maxStackViewTopPadding: CGFloat = 14 + let minStackViewTopPadding: CGFloat = 6 + let maxContainerStackViewSpacing: CGFloat = 10 + let minContainerStackViewSpacing: CGFloat = 4 + var minHeight: CGFloat { + return timestampLabel.frame.maxY + stackViewTopPaddingConstraint.constant + squishedBottomPadding + } + private var viewModel: DiffHeaderCompareItemViewModel? + + private var userAndSummaryTapGestureRecognizer: UITapGestureRecognizer? + private var headingAndTimestampTapGestureRecognizer: UITapGestureRecognizer? + weak var delegate: DiffHeaderActionDelegate? + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + stackViewTopPaddingConstraint.constant = maxStackViewTopPadding + containerStackView.spacing = maxContainerStackViewSpacing + } + + override func awakeFromNib() { + super.awakeFromNib() + + if let userAndSummaryTapGestureRecognizer = userAndSummaryTapGestureRecognizer { + userAndSummaryStackView.addGestureRecognizer(userAndSummaryTapGestureRecognizer) + } + + if let headingAndTimestampTapGestureRecognizer = headingAndTimestampTapGestureRecognizer { + headingAndTimestampStackView.addGestureRecognizer(headingAndTimestampTapGestureRecognizer) + } + } + + func update(_ viewModel: DiffHeaderCompareItemViewModel) { + + headingLabel.text = viewModel.heading + timestampLabel.text = viewModel.timestampString + userIconImageView.image = UIImage(named: "user-edit") + usernameLabel.text = viewModel.username + + if viewModel.isMinor, + let minorImage = UIImage(named: "minor-edit") { + let imageAttachment = NSTextAttachment() + imageAttachment.image = minorImage + let attributedText = NSMutableAttributedString(attachment: imageAttachment) + attributedText.addAttributes([NSAttributedString.Key.baselineOffset: -1], range: NSRange(location: 0, length: 1)) + + if let summary = viewModel.summary { + attributedText.append(NSAttributedString(string: " \(summary)")) + } + + summaryLabel.attributedText = attributedText + } else { + summaryLabel.text = viewModel.summary + } + + updateFonts(with: traitCollection) + + self.viewModel = viewModel + updateAccessibilityLabels(viewModel: viewModel) + } + + func updateAccessibilityLabels(viewModel: DiffHeaderCompareItemViewModel) { + let isMinorAccessibilityString = viewModel.isMinor ? CommonStrings.minorEditTitle : "" + let authorString = String.localizedStringWithFormat(CommonStrings.authorTitle, viewModel.username ?? CommonStrings.unknownTitle) + headingAndTimestampStackView.accessibilityLabel = UIAccessibility.groupedAccessibilityLabel(for: [headingLabel.text, timestampLabel.text]) + userAndSummaryStackView.accessibilityLabel = UIAccessibility.groupedAccessibilityLabel(for: [authorString, isMinorAccessibilityString, summaryLabel.text]) + } + + func squish(by percentage: CGFloat) { + let topPaddingDelta = maxStackViewTopPadding - minStackViewTopPadding + stackViewTopPaddingConstraint.constant = maxStackViewTopPadding - (topPaddingDelta * percentage) + + let spacingDelta = maxContainerStackViewSpacing - minContainerStackViewSpacing + containerStackView.spacing = maxContainerStackViewSpacing - (spacingDelta * percentage) + + // tonitodo: shrink font size + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts(with: traitCollection) + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + guard !UIAccessibility.isVoiceOverRunning else { + return super.point(inside: point, with: event) + } + + let userStackViewConvertedPoint = self.convert(point, to: userStackView) + if userStackView.point(inside: userStackViewConvertedPoint, with: event) { + return true + } + + let timestampLabelConvertedPoint = self.convert(point, to: timestampLabel) + if timestampLabel.point(inside: timestampLabelConvertedPoint, with: event) { + return true + } + + return false + } + + @objc func tappedElementWithSender(_ sender: UITapGestureRecognizer) { + if let username = viewModel?.username, + sender == userAndSummaryTapGestureRecognizer { + delegate?.tappedUsername(username: username) + } else if let revisionID = viewModel?.revisionID, + sender == headingAndTimestampTapGestureRecognizer { + delegate?.tappedRevision(revisionID: revisionID) + } + } +} + +private extension DiffHeaderCompareItemView { + + func commonInit() { + Bundle.main.loadNibNamed(DiffHeaderCompareItemView.wmf_nibName(), owner: self, options: nil) + addSubview(contentView) + contentView.frame = self.bounds + contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + userAndSummaryTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tappedElementWithSender)) + headingAndTimestampTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tappedElementWithSender)) + headingAndTimestampStackView.isAccessibilityElement = true + userAndSummaryStackView.isAccessibilityElement = true + headingAndTimestampStackView.accessibilityTraits = [.link] + userAndSummaryStackView.accessibilityTraits = [.link] + } + + func updateFonts(with traitCollection: UITraitCollection) { + headingLabel.font = UIFont.wmf_font(DynamicTextStyle.boldFootnote, compatibleWithTraitCollection: traitCollection) + timestampLabel.font = UIFont.wmf_font(DynamicTextStyle.boldFootnote, compatibleWithTraitCollection: traitCollection) + usernameLabel.font = UIFont.wmf_font(DynamicTextStyle.mediumCaption1, compatibleWithTraitCollection: traitCollection) + summaryLabel.font = UIFont.wmf_font(DynamicTextStyle.italicCaption1, compatibleWithTraitCollection: traitCollection) + } +} + +extension DiffHeaderCompareItemView: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + contentView.backgroundColor = theme.colors.paperBackground + headingLabel.textColor = theme.colors.secondaryText + + if let viewModel = viewModel { + timestampLabel.textColor = viewModel.accentColor + usernameLabel.textColor = viewModel.accentColor + userIconImageView.tintColor = viewModel.accentColor + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareItemView.xib b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareItemView.xib new file mode 100644 index 0000000..8bd827e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareItemView.xib @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareView.swift b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareView.swift new file mode 100644 index 0000000..d265361 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareView.swift @@ -0,0 +1,105 @@ +import UIKit + +class DiffHeaderCompareView: UIView { + + @IBOutlet var contentView: UIView! + @IBOutlet var stackViewContainerView: UIView! + @IBOutlet var fromItemView: DiffHeaderCompareItemView! + @IBOutlet var toItemView: DiffHeaderCompareItemView! + @IBOutlet var divView: UIView! + @IBOutlet var innerHeightConstraint: NSLayoutConstraint! + @IBOutlet var stackView: UIStackView! + private var maxHeight: CGFloat = 0 + private var minHeight: CGFloat = 0 + + private var beginSquishYOffset: CGFloat = 0 + private var scrollYOffset: CGFloat = 0 + + var delegate: DiffHeaderActionDelegate? { + get { + return fromItemView.delegate + } + set { + fromItemView.delegate = newValue + toItemView.delegate = newValue + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + override func layoutSubviews() { + super.layoutSubviews() + + maxHeight = stackView.frame.height + minHeight = max(fromItemView.minHeight, toItemView.minHeight) + + configureHeight(beginSquishYOffset: beginSquishYOffset, scrollYOffset: scrollYOffset) + } + + private func commonInit() { + Bundle.main.loadNibNamed(DiffHeaderCompareView.wmf_nibName(), owner: self, options: nil) + addSubview(contentView) + contentView.frame = self.bounds + contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + } + + override func awakeFromNib() { + super.awakeFromNib() + accessibilityElements = [fromItemView, toItemView].compactMap { $0 as Any } + } + + func configureHeight(beginSquishYOffset: CGFloat, scrollYOffset: CGFloat) { + + self.beginSquishYOffset = beginSquishYOffset + self.scrollYOffset = scrollYOffset + + let amountToSquish = scrollYOffset - beginSquishYOffset + let heightDelta = maxHeight - minHeight + + let changePercentage = min(1, max(0,(amountToSquish / heightDelta))) + + innerHeightConstraint.constant = maxHeight - (heightDelta * changePercentage) + fromItemView.squish(by: changePercentage) + toItemView.squish(by: changePercentage) + } + + func update(_ viewModel: DiffHeaderCompareViewModel) { + + fromItemView.update(viewModel.fromModel) + toItemView.update(viewModel.toModel) + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + let fromConvertedPoint = self.convert(point, to: fromItemView) + if fromItemView.point(inside: fromConvertedPoint, with: event) { + return true + } + + let toConvertedPoint = self.convert(point, to: toItemView) + if toItemView.point(inside: toConvertedPoint, with: event) { + return true + } + + return false + } +} + +extension DiffHeaderCompareView: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + contentView.backgroundColor = theme.colors.paperBackground + stackViewContainerView.backgroundColor = theme.colors.paperBackground + stackView.backgroundColor = theme.colors.paperBackground + divView.backgroundColor = theme.colors.chromeShadow + fromItemView.apply(theme: theme) + toItemView.apply(theme: theme) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareView.xib b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareView.xib new file mode 100644 index 0000000..4ecfa6a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderCompareView.xib @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderEditorView.swift b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderEditorView.swift new file mode 100644 index 0000000..d9fa803 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderEditorView.swift @@ -0,0 +1,105 @@ +import UIKit + +class DiffHeaderEditorView: UIView { + + @IBOutlet var contentView: UIView! + @IBOutlet var headingLabel: UILabel! + @IBOutlet var userIconImageView: UIImageView! + @IBOutlet var usernameLabel: UILabel! + @IBOutlet var numberOfEditsLabel: UILabel! + @IBOutlet var userStackView: UIStackView! + + private var tapGestureRecognizer: UITapGestureRecognizer? + weak var delegate: DiffHeaderActionDelegate? + + private var viewModel: DiffHeaderEditorViewModel? + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + override func awakeFromNib() { + super.awakeFromNib() + + if let tapGestureRecognizer = tapGestureRecognizer { + userStackView.addGestureRecognizer(tapGestureRecognizer) + } + userStackView.accessibilityTraits = [.link] + } + + func update(_ viewModel: DiffHeaderEditorViewModel) { + + self.viewModel = viewModel + + headingLabel.text = viewModel.heading + usernameLabel.text = viewModel.username + userIconImageView.image = UIImage(named: "user-edit") + + if let numberOfEditsForDisplay = viewModel.numberOfEditsForDisplay, + !numberOfEditsForDisplay.isEmpty { + numberOfEditsLabel.text = numberOfEditsForDisplay + numberOfEditsLabel.isHidden = false + } else { + numberOfEditsLabel.isHidden = true + } + + updateFonts(with: traitCollection) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts(with: traitCollection) + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + guard !UIAccessibility.isVoiceOverRunning else { + return super.point(inside: point, with: event) + } + let userStackViewConvertedPoint = self.convert(point, to: userStackView) + return userStackView.point(inside: userStackViewConvertedPoint, with: event) + } + + @objc func tappedUserWithSender(_ sender: UITapGestureRecognizer) { + if let username = viewModel?.username { + delegate?.tappedUsername(username: username) + } + } +} + +private extension DiffHeaderEditorView { + + func commonInit() { + Bundle.main.loadNibNamed(DiffHeaderEditorView.wmf_nibName(), owner: self, options: nil) + addSubview(contentView) + contentView.frame = self.bounds + contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tappedUserWithSender(_:))) + + let isRTL = effectiveUserInterfaceLayoutDirection == .rightToLeft + numberOfEditsLabel.textAlignment = isRTL ? .left : .right + } + + func updateFonts(with traitCollection: UITraitCollection) { + + headingLabel.font = UIFont.wmf_font(DynamicTextStyle.boldFootnote, compatibleWithTraitCollection: traitCollection) + usernameLabel.font = UIFont.wmf_font(DynamicTextStyle.subheadline, compatibleWithTraitCollection: traitCollection) + numberOfEditsLabel.font = UIFont.wmf_font(DynamicTextStyle.callout, compatibleWithTraitCollection: traitCollection) + } +} + +extension DiffHeaderEditorView: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + contentView.backgroundColor = theme.colors.paperBackground + headingLabel.textColor = theme.colors.secondaryText + usernameLabel.textColor = theme.colors.link + userIconImageView.tintColor = theme.colors.link + numberOfEditsLabel.textColor = theme.colors.secondaryText + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderEditorView.xib b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderEditorView.xib new file mode 100644 index 0000000..0124748 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderEditorView.xib @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderExtendedView.swift b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderExtendedView.swift new file mode 100644 index 0000000..8e6575d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderExtendedView.swift @@ -0,0 +1,124 @@ +import UIKit + +protocol DiffHeaderActionDelegate: AnyObject { + func tappedUsername(username: String) + func tappedRevision(revisionID: Int) +} + +class DiffHeaderExtendedView: UIView { + + @IBOutlet var contentView: UIView! + @IBOutlet var stackView: UIStackView! + @IBOutlet var summaryView: DiffHeaderSummaryView! + @IBOutlet var editorView: DiffHeaderEditorView! + @IBOutlet var compareView: DiffHeaderCompareView! + @IBOutlet var divViews: [UIView]! + @IBOutlet var summaryDivView: UIView! + @IBOutlet var editorDivView: UIView! + @IBOutlet var compareDivView: UIView! + + private var viewModel: DiffHeaderViewModel? + + weak var delegate: DiffHeaderActionDelegate? { + get { + return editorView.delegate + } + set { + editorView.delegate = newValue + compareView.delegate = newValue + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + private func commonInit() { + Bundle.main.loadNibNamed(DiffHeaderExtendedView.wmf_nibName(), owner: self, options: nil) + addSubview(contentView) + contentView.frame = self.bounds + contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + } + + func configureHeight(beginSquishYOffset: CGFloat, scrollYOffset: CGFloat) { + guard let viewModel = viewModel else { + return + } + + switch viewModel.headerType { + case .compare: + compareView.configureHeight(beginSquishYOffset: beginSquishYOffset, scrollYOffset: scrollYOffset) + default: break + } + } + + func update(_ new: DiffHeaderViewModel) { + + self.viewModel = new + + switch new.headerType { + case .compare(let compareViewModel, _): + summaryView.isHidden = true + summaryDivView.isHidden = true + editorView.isHidden = true + editorDivView.isHidden = true + compareView.isHidden = false + compareDivView.isHidden = false + compareView.update(compareViewModel) + case .single(let editorViewModel, let summaryViewModel): + editorView.isHidden = false + editorDivView.isHidden = false + compareView.isHidden = true + compareDivView.isHidden = true + if let summary = summaryViewModel.summary, summary.wmf_hasNonWhitespaceText { + summaryView.isHidden = false + summaryDivView.isHidden = false + summaryView.update(summaryViewModel) + } else { + summaryView.isHidden = true + summaryDivView.isHidden = true + } + editorView.update(editorViewModel) + } + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + + let summaryConvertedPoint = self.convert(point, to: summaryView) + if summaryView.point(inside: summaryConvertedPoint, with: event) { + return true + } + + let editorConvertedPoint = self.convert(point, to: editorView) + if editorView.point(inside: editorConvertedPoint, with: event) { + return true + } + + let compareConvertedPoint = self.convert(point, to: compareView) + if compareView.point(inside: compareConvertedPoint, with: event) { + return true + } + + return false + } +} + +extension DiffHeaderExtendedView: Themeable { + + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + summaryView.apply(theme: theme) + editorView.apply(theme: theme) + compareView.apply(theme: theme) + + for view in divViews { + view.backgroundColor = theme.colors.chromeShadow + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderExtendedView.xib b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderExtendedView.xib new file mode 100644 index 0000000..ab95168 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderExtendedView.xib @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderSummaryView.swift b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderSummaryView.swift new file mode 100644 index 0000000..69b4cd4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderSummaryView.swift @@ -0,0 +1,76 @@ +import UIKit + +class DiffHeaderSummaryView: UIView, Themeable { + + @IBOutlet var contentView: UIView! + @IBOutlet var headingLabel: UILabel! + @IBOutlet var summaryLabel: UILabel! + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + func update(_ viewModel: DiffHeaderEditSummaryViewModel) { + + headingLabel.text = viewModel.heading + + if viewModel.isMinor, + let minorImage = UIImage(named: "minor-edit") { + let imageAttachment = NSTextAttachment() + imageAttachment.image = minorImage + let attributedText = NSMutableAttributedString(attachment: imageAttachment) + attributedText.addAttributes([NSAttributedString.Key.baselineOffset: -1], range: NSRange(location: 0, length: 1)) + + if let summary = viewModel.summary { + attributedText.append(NSAttributedString(string: " \(summary)")) + } + + summaryLabel.attributedText = attributedText + } else { + summaryLabel.text = viewModel.summary + } + + updateFonts(with: traitCollection) + + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts(with: traitCollection) + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + guard !UIAccessibility.isVoiceOverRunning else { + return super.point(inside: point, with: event) + } + return false + } + + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + contentView.backgroundColor = theme.colors.paperBackground + headingLabel.textColor = theme.colors.secondaryText + summaryLabel.textColor = theme.colors.primaryText + } +} + +private extension DiffHeaderSummaryView { + + func commonInit() { + Bundle.main.loadNibNamed(DiffHeaderSummaryView.wmf_nibName(), owner: self, options: nil) + addSubview(contentView) + contentView.frame = self.bounds + contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + } + + func updateFonts(with traitCollection: UITraitCollection) { + headingLabel.font = UIFont.wmf_font(DynamicTextStyle.boldFootnote, compatibleWithTraitCollection: traitCollection) + summaryLabel.font = UIFont.wmf_font(DynamicTextStyle.subheadline, compatibleWithTraitCollection: traitCollection) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderSummaryView.xib b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderSummaryView.xib new file mode 100644 index 0000000..0eada18 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderSummaryView.xib @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderTitleView.swift b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderTitleView.swift new file mode 100644 index 0000000..ed86588 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderTitleView.swift @@ -0,0 +1,88 @@ +import UIKit + +class DiffHeaderTitleView: UIView { + + @IBOutlet var contentView: UIView! + @IBOutlet var headingLabel: UILabel! + @IBOutlet var titleLabel: UILabel! + @IBOutlet var subtitleLabel: UILabel! + + private(set) var viewModel: DiffHeaderTitleViewModel? + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + func update(_ viewModel: DiffHeaderTitleViewModel) { + + self.viewModel = viewModel + + headingLabel.text = viewModel.heading + titleLabel.text = viewModel.title + + if let subtitle = viewModel.subtitle { + subtitleLabel.text = subtitle + subtitleLabel.isHidden = false + } else { + subtitleLabel.isHidden = true + } + + + updateFonts(with: traitCollection) + + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts(with: traitCollection) + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + guard !UIAccessibility.isVoiceOverRunning else { + return super.point(inside: point, with: event) + } + return false + } +} + +private extension DiffHeaderTitleView { + func commonInit() { + Bundle.main.loadNibNamed(DiffHeaderTitleView.wmf_nibName(), owner: self, options: nil) + addSubview(contentView) + contentView.frame = self.bounds + contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + updateFonts(with: traitCollection) + } + + func updateFonts(with traitCollection: UITraitCollection) { + headingLabel.font = UIFont.wmf_font(DynamicTextStyle.semiboldFootnote, compatibleWithTraitCollection: traitCollection) + titleLabel.font = UIFont.wmf_font(DynamicTextStyle.boldTitle1, compatibleWithTraitCollection: traitCollection) + if let viewModel = viewModel { + subtitleLabel.font = UIFont.wmf_font(viewModel.subtitleTextStyle, compatibleWithTraitCollection: traitCollection) + } else { + subtitleLabel.font = UIFont.wmf_font(DynamicTextStyle.footnote, compatibleWithTraitCollection: traitCollection) + } + } +} + +extension DiffHeaderTitleView: Themeable { + func apply(theme: Theme) { + + backgroundColor = theme.colors.paperBackground + contentView.backgroundColor = theme.colors.paperBackground + headingLabel.textColor = theme.colors.secondaryText + titleLabel.textColor = theme.colors.primaryText + + if let subtitleColor = viewModel?.subtitleColor { + subtitleLabel.textColor = subtitleColor + } else { + subtitleLabel.textColor = theme.colors.secondaryText + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderTitleView.xib b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderTitleView.xib new file mode 100644 index 0000000..f9aa8e3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderTitleView.xib @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffHeaderViewModels.swift b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderViewModels.swift new file mode 100644 index 0000000..906f68e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffHeaderViewModels.swift @@ -0,0 +1,266 @@ +import Foundation + +final class DiffHeaderViewModel: Themeable { + + enum DiffHeaderType { + case single(editorViewModel: DiffHeaderEditorViewModel, summaryViewModel: DiffHeaderEditSummaryViewModel) + case compare(compareViewModel: DiffHeaderCompareViewModel, navBarTitle: String) + } + + var title: DiffHeaderTitleViewModel + let diffType: DiffContainerViewModel.DiffType + let headerType: DiffHeaderType + private let articleTitle: String + private let byteDifference: Int? + static let dateFormatter = DateFormatter() + + var isExtendedViewHidingEnabled: Bool { + switch headerType { + case .compare: + return false + case .single: + return true + } + } + + init?(diffType: DiffContainerViewModel.DiffType, fromModel: WMFPageHistoryRevision?, toModel: WMFPageHistoryRevision, articleTitle: String, byteDifference: Int?, theme: Theme) { + + self.diffType = diffType + self.articleTitle = articleTitle + self.byteDifference = byteDifference + + DiffHeaderViewModel.dateFormatter.timeZone = TimeZone(identifier: "UTC") + let formatString: String + let titleViewModel: DiffHeaderTitleViewModel + + switch diffType { + case .single: + + guard let byteDifference = byteDifference else { + return nil + } + + formatString = "HH:mm zzz, dd MMM yyyy" // tonitodo: "UTC" instead of "GMT" in result? + DiffHeaderViewModel.dateFormatter.dateFormat = formatString + + var heading: String? + var title: String? + if let toDate = toModel.revisionDate as NSDate? { + heading = toDate.wmf_fullyLocalizedRelativeDateStringFromLocalDateToNow() + title = DiffHeaderViewModel.dateFormatter.string(from: toDate as Date) + } + + let subtitle: String + if byteDifference < 0 { + subtitle = String.localizedStringWithFormat(WMFLocalizedString("diff-single-header-subtitle-bytes-removed", value:"{{PLURAL:%1$d|%1$d byte removed|%1$d bytes removed}}", comment:"Subtitle label in header when viewing a revision. %1$d is replaced by the number of bytes that were removed in this revision."), -byteDifference) + } else { + subtitle = String.localizedStringWithFormat(WMFLocalizedString("diff-single-header-subtitle-bytes-added", value:"{{PLURAL:%1$d|%1$d byte added|%1$d bytes added}}", comment:"Subtitle label in header when viewing a revision. %1$d is replaced by the number of bytes that were added in this revision."), byteDifference) + } + + titleViewModel = DiffHeaderTitleViewModel(heading: heading, title: title, subtitle: subtitle, subtitleTextStyle: DynamicTextStyle.boldSubheadline, subtitleColor: nil) + + let summaryViewModel = DiffHeaderEditSummaryViewModel(heading: WMFLocalizedString("diff-single-header-summary-heading", value: "Edit summary", comment: "Heading label in header summary view when viewing a single revision."), isMinor: toModel.isMinor, summary: toModel.parsedComment) + + let editorViewModel = DiffHeaderEditorViewModel(heading: WMFLocalizedString("diff-single-header-editor-title", value: "Editor information", comment: "Title label in header editor view when viewing a single revision."), username: toModel.user) + + self.title = titleViewModel + self.headerType = .single(editorViewModel: editorViewModel, summaryViewModel: summaryViewModel) + + case .compare: + + guard let fromModel = fromModel else { + return nil + } + + titleViewModel = DiffHeaderViewModel.generateTitleViewModelForCompare(articleTitle: articleTitle, editCounts: nil) + + self.title = titleViewModel + + let formatString = "HH:mm, dd MMM yyyy" + DiffHeaderViewModel.dateFormatter.dateFormat = formatString + + let compareModel = DiffHeaderCompareViewModel(fromModel: fromModel, toModel: toModel, dateFormatter: DiffHeaderViewModel.dateFormatter, theme: theme) + let navBarTitle = WMFLocalizedString("diff-compare-title", value: "Compare Revisions", comment: "Title label that shows in the navigation bar when scrolling and comparing revisions.") + self.headerType = .compare(compareViewModel: compareModel, navBarTitle: navBarTitle) + } + + apply(theme: theme) + } + + static func generateTitleViewModelForCompare(articleTitle: String, editCounts: EditCountsGroupedByType?) -> DiffHeaderTitleViewModel { + let heading = CommonStrings.compareRevisionsTitle + let subtitle: String? + + if let editCounts = editCounts { + switch (editCounts[.edits], editCounts[.editors]) { + case (let edits?, let editors?): + + if edits.count == 0 && editors.count == 0 { + subtitle = nil + break + } + + switch (edits.limit, editors.limit) { + case (false, false): + subtitle = String.localizedStringWithFormat(WMFLocalizedString("intermediate-edits-editors-count", value: "{{PLURAL:%1$d|%1$d intermediate revision|%1$d intermediate revisions}} by {{PLURAL:%2$d|%2$d user|%2$d users}} not shown", comment: "Subtitle for the number of revisions that were made between two chosen revisions. It also includes the number of editors who created those revisions. %1$d is replaced with the number of intermediate revisions and %2$d is replaced with the number of editors who created those revisions."), edits.count, editors.count) + case (true, true): + subtitle = String.localizedStringWithFormat(WMFLocalizedString("intermediate-edits-editors-count-limited", value: "%1$d+ intermediate revisions by %2$d+ users not shown", comment: "Subtitle for the number of revisions that were made between two chosen revisions. It also includes the number of editors who created those revisions. %1$d is replaced with the number of intermediate revisions and %2$d is replaced with the number of editors who created those revisions. The numbers are followed by the '+' to indicate that the actual numbers exceed the displayed numbers."), edits.count, editors.count) + case (true, false): + subtitle = String.localizedStringWithFormat(WMFLocalizedString("intermediate-edits-limited-editors-count", value: "%1$d+ intermediate revisions by {{PLURAL:%2$d|%2$d user|%2$d users}} not shown", comment: "Subtitle for the number of revisions that were made between two chosen revisions. It also includes the number of editors who created those revisions. %1$d is replaced with the number of intermediate revisions and %2$d is replaced with the number of editors who created those revisions. The number of intermediate revisions is followed by the '+' to indicate that the actual number of intermediate revisions exceeds the displayed number."), edits.count, editors.count) + case (false, true): + subtitle = String.localizedStringWithFormat(WMFLocalizedString("intermediate-edits-editors-limited-count", value: "{{PLURAL:%1$d|%1$d intermediate revision|%1$d intermediate revisions}} by %2$d+ users not shown", comment: "Subtitle for the number of revisions that were made between two chosen revisions. It also includes the number of editors who created those revisions. %1$d is replaced with the number of intermediate revisions and %2$d is replaced with the number of editors who created those revisions. The number of editors is followed by the '+' to indicate that the actual number of editors exceeds the displayed number."), edits.count, editors.count) + } + case (let edits?, nil): + + if edits.count == 0 { + subtitle = nil + break + } + + if edits.limit { + subtitle = String.localizedStringWithFormat(WMFLocalizedString("intermediate-edits-count-limited", value: "%1$d+ intermediate revisions not shown", comment: "Subtitle for the number of revisions that were made between two chosen revisions. %1$d is replaced with the number of intermediate revisions. The number of intermediate revisions is followed by the '+' to indicate that the actual number of revisions exceeds the displayed number."), edits.count) + } else { + subtitle = String.localizedStringWithFormat(WMFLocalizedString("intermediate-edits-count", value: "{{PLURAL:%1$d|%1$d intermediate revision|%1$d intermediate revisions}} not shown", comment: "Subtitle for the number of revisions that were made between two chosen revisions. %1$d is replaced with the number of intermediate revisions."), edits.count) + } + default: + subtitle = nil + break + } + } else { + subtitle = nil + } + + return DiffHeaderTitleViewModel(heading: heading, title: articleTitle, subtitle: subtitle, subtitleTextStyle: DynamicTextStyle.subheadline, subtitleColor: nil) + } + + func apply(theme: Theme) { + switch diffType { + case .single: + if let byteDifference = byteDifference, + byteDifference < 0 { + title.subtitleColor = theme.colors.destructive + } else { + title.subtitleColor = theme.colors.accent + } + default: + break + } + } +} + +final class DiffHeaderTitleViewModel { + let heading: String? // tonitodo: because WMFPageHistoryRevision revisionDate is nullable and that's displayed as a title in single revision view, can we make it not optional. same with title + let title: String? + let subtitle: String? + let subtitleTextStyle: DynamicTextStyle + var subtitleColor: UIColor? + + init(heading: String?, title: String?, subtitle: String?, subtitleTextStyle: DynamicTextStyle, subtitleColor: UIColor?) { + self.heading = heading?.localizedUppercase + self.title = title + self.subtitle = subtitle + self.subtitleTextStyle = subtitleTextStyle + self.subtitleColor = subtitleColor + } +} + +final class DiffHeaderEditSummaryViewModel { + let heading: String + let isMinor: Bool + let summary: String? // tonitodo - because WMFPageHistoryRevision.parsedComment is nullable, can we make that not optional + + init(heading: String, isMinor: Bool, summary: String?) { + self.heading = heading + self.isMinor = isMinor + self.summary = summary?.removingHTML + } +} + +final class DiffHeaderEditorViewModel { + + let heading: String + let username: String? // tonitodo: because WMFPageHistoryRevision user is nullable, can we make that not nullable + var numberOfEdits: Int? { + didSet { + guard let numberOfEdits = numberOfEdits else { + return + } + + // tonitodo: should we go larger than int? + numberOfEditsForDisplay = String.localizedStringWithFormat(numberOfEditsFormat, numberOfEdits) + } + } + private(set) var numberOfEditsForDisplay: String? + private let numberOfEditsFormat = WMFLocalizedString("diff-single-header-editor-number-edits-format", value:"{{PLURAL:%1$d|%1$d edit|%1$d edits}}", comment:"Label to show the number of total edits made by the editor when viewing a single revision. %1$d is replaced with the editor's number of edits.") + + init(heading: String, username: String?) { + self.heading = heading + self.username = username + } +} + +final class DiffHeaderCompareViewModel: Themeable { + let fromModel: DiffHeaderCompareItemViewModel + let toModel: DiffHeaderCompareItemViewModel + + init(fromModel: WMFPageHistoryRevision, toModel: WMFPageHistoryRevision, dateFormatter: DateFormatter, theme: Theme) { + self.fromModel = DiffHeaderCompareItemViewModel(type: .from, model: fromModel, dateFormatter: dateFormatter, theme: theme, revisionID: fromModel.revisionID) + self.toModel = DiffHeaderCompareItemViewModel(type: .to, model: toModel, dateFormatter: dateFormatter, theme: theme, revisionID: toModel.revisionID) + } + + func apply(theme: Theme) { + fromModel.apply(theme: theme) + toModel.apply(theme: theme) + } +} + +final class DiffHeaderCompareItemViewModel: Themeable { + let type: DiffHeaderCompareType + let heading: String + let username: String? // tonitodo: because WMFPageHistoryRevision.user is nullable, can we make not nullable + let isMinor: Bool + let summary: String? // tonitodo: because WMFPageHistoryRevision.parsedComment is nullable, can we make not nullable + let timestampString: String? // tonitodo: because WMFPageHistoryRevision.revisionDate is nullable, can we make not nullable + var accentColor: UIColor + let revisionID: Int + + init(type: DiffHeaderCompareType, model: WMFPageHistoryRevision, dateFormatter: DateFormatter, theme: Theme, revisionID: Int) { + + self.type = type + switch type { + case .from: + heading = WMFLocalizedString("diff-compare-header-from-info-heading", value: "From:", comment: "Heading label in from revision info box when comparing two revisions.") + case .to: + heading = WMFLocalizedString("diff-compare-header-to-info-heading", value: "To:", comment: "Heading label in to revision info box when comparing two revisions.") + } + + self.username = model.user + self.isMinor = model.isMinor + self.summary = model.parsedComment?.removingHTML + + if let date = model.revisionDate { + self.timestampString = dateFormatter.string(from: date) + } else { + self.timestampString = nil + } + + self.revisionID = revisionID + + accentColor = theme.colors.link // compile error without this, overwrite in apply(theme:) + apply(theme: theme) + } + + func apply(theme: Theme) { + switch type { + case .from: + accentColor = theme.colors.link + case .to: + accentColor = theme.colors.diffCompareAccent + } + } +} + +enum DiffHeaderCompareType { + case from + case to +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListChangeCell.swift b/Apps/Wikipedia/Wikipedia/Code/DiffListChangeCell.swift new file mode 100644 index 0000000..091c8b0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListChangeCell.swift @@ -0,0 +1,224 @@ +import UIKit + +protocol DiffListChangeCellDelegate: AnyObject { + func didTapItem(item: DiffListChangeItemViewModel) +} + +class DiffListChangeCell: UICollectionViewCell { + static let reuseIdentifier = "DiffListChangeCell" + + @IBOutlet var textLeadingConstraint: NSLayoutConstraint! + @IBOutlet var textTrailingConstraint: NSLayoutConstraint! + @IBOutlet var textTopConstraint: NSLayoutConstraint! + @IBOutlet var textBottomConstraint: NSLayoutConstraint! + + @IBOutlet var innerLeadingConstraint: NSLayoutConstraint! + @IBOutlet var innerTrailingConstraint: NSLayoutConstraint! + @IBOutlet var innerBottomConstraint: NSLayoutConstraint! + @IBOutlet var innerTopConstraint: NSLayoutConstraint! + + @IBOutlet var headingLeadingConstraint: NSLayoutConstraint! + @IBOutlet var headingTopConstraint: NSLayoutConstraint! + @IBOutlet var headingBottomConstraint: NSLayoutConstraint! + @IBOutlet var headingTrailingConstraint: NSLayoutConstraint! + + @IBOutlet var headingContainerView: UIView! + @IBOutlet var headingLabel: UILabel! + @IBOutlet var textStackView: UIStackView! + @IBOutlet var innerView: UIView! + + private(set) var viewModel: DiffListChangeViewModel? + private var textLabels: [UILabel] = [] + private var shadedBackgroundViews: [UIView] = [] + private var spacerViews: [UIView] = [] + + weak var delegate: DiffListChangeCellDelegate? + + func update(_ viewModel: DiffListChangeViewModel) { + + textLeadingConstraint.constant = viewModel.stackViewPadding.leading + textTrailingConstraint.constant = viewModel.stackViewPadding.trailing + textTopConstraint.constant = viewModel.stackViewPadding.top + textBottomConstraint.constant = viewModel.stackViewPadding.bottom + + innerLeadingConstraint.constant = viewModel.innerPadding.leading + innerTrailingConstraint.constant = viewModel.innerPadding.trailing + innerTopConstraint.constant = viewModel.innerPadding.top + innerBottomConstraint.constant = viewModel.innerPadding.bottom + + headingLeadingConstraint.constant = viewModel.headingPadding.leading + headingTrailingConstraint.constant = viewModel.headingPadding.trailing + headingBottomConstraint.constant = viewModel.headingPadding.bottom + headingTopConstraint.constant = viewModel.headingPadding.top + + if needsNewTextLabels(newViewModel: viewModel) { + reset() + addTextLabels(to: textStackView, newViewModel: viewModel) + } + + updateTextLabels(in: textStackView, newViewModel: viewModel) + + innerView.borderWidth = 1 + + self.viewModel = viewModel + + apply(theme: viewModel.theme) + } + + func yLocationOfItem(index: Int, convertView: UIView) -> CGFloat? { + + guard let item = textStackView.arrangedSubviews[safeIndex: index] else { + return nil + } + + return textStackView.convert(item.frame, to: convertView).minY + } + + @objc func tappedLabelWithSender(_ sender: UITapGestureRecognizer) { + if let sender = sender.view as? UILabel, + let item = viewModel?.items[safeIndex: sender.tag], + item.diffItemType == .moveSource || item.diffItemType == .moveDestination { + + delegate?.didTapItem(item: item) + } + } +} + +private extension DiffListChangeCell { + func reset() { + for subview in textStackView.arrangedSubviews { + textStackView.removeArrangedSubview(subview) + subview.removeFromSuperview() + } + + textLabels.removeAll() + shadedBackgroundViews.removeAll() + spacerViews.removeAll() + } + + func addTextLabels(to textStackView: UIStackView, newViewModel: DiffListChangeViewModel) { + for (index, item) in newViewModel.items.enumerated() { + let label = UILabel() + textLabels.append(label) + label.numberOfLines = 0 + label.lineBreakMode = .byWordWrapping + label.textAlignment = item.textAlignment + label.isUserInteractionEnabled = true + label.tag = index + label.translatesAutoresizingMaskIntoConstraints = false + + if label.gestureRecognizers == nil { + addTapGestureRecognizer(to: label) + } else if let gestureRecognizers = label.gestureRecognizers, gestureRecognizers.isEmpty { + addTapGestureRecognizer(to: label) + } + + // add surrounding view + let view = UIView(frame: .zero) + view.translatesAutoresizingMaskIntoConstraints = false + + // shaded background view + if item.hasShadedBackgroundView { + let shadedBackgroundView = UIView(frame: .zero) + shadedBackgroundViews.append(shadedBackgroundView) + shadedBackgroundView.translatesAutoresizingMaskIntoConstraints = false + + shadedBackgroundView.addSubview(label) + + view.addSubview(shadedBackgroundView) + + let textTop = label.topAnchor.constraint(equalTo: shadedBackgroundView.topAnchor, constant: item.textPadding.top) + let textBottom = shadedBackgroundView.bottomAnchor.constraint(equalTo: label.bottomAnchor, constant: item.textPadding.bottom) + let textLeading = label.leadingAnchor.constraint(equalTo: shadedBackgroundView.leadingAnchor, constant: item.textPadding.leading) + let textTrailing = shadedBackgroundView.trailingAnchor.constraint(equalTo: label.trailingAnchor, constant: item.textPadding.trailing) + shadedBackgroundView.addConstraints([textTop, textBottom, textLeading, textTrailing]) + + let top = shadedBackgroundView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0) + let leading = shadedBackgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0) + let trailing = view.trailingAnchor.constraint(equalTo: shadedBackgroundView.trailingAnchor, constant: 0) + view.addConstraints([top, leading, trailing]) + + if let inBetweenSpacing = item.inBetweenSpacing { + let spacerView = UIView(frame: .zero) + spacerViews.append(spacerView) + spacerView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(spacerView) + + let spacerTop = spacerView.topAnchor.constraint(equalTo: shadedBackgroundView.bottomAnchor, constant: 0) + let spacerBottom = view.bottomAnchor.constraint(equalTo: spacerView.bottomAnchor, constant: 0) + let spacerLeading = spacerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0) + let spacerTrailing = view.trailingAnchor.constraint(equalTo: spacerView.trailingAnchor, constant: 0) + let spacerHeight = spacerView.heightAnchor.constraint(equalToConstant: inBetweenSpacing) + view.addConstraints([spacerTop, spacerBottom, spacerLeading, spacerTrailing, spacerHeight]) + } else { + let bottom = view.bottomAnchor.constraint(equalTo: shadedBackgroundView.bottomAnchor, constant: 0) + view.addConstraints([bottom]) + } + } else { + view.addSubview(label) + let textTop = label.topAnchor.constraint(equalTo: view.topAnchor, constant: item.textPadding.top) + let textBottom = view.bottomAnchor.constraint(equalTo: label.bottomAnchor, constant: item.textPadding.bottom) + let textLeading = label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: item.textPadding.leading) + let textTrailing = view.trailingAnchor.constraint(equalTo: label.trailingAnchor, constant: item.textPadding.trailing) + view.addConstraints([textTop, textBottom, textLeading, textTrailing]) + } + + + textStackView.addArrangedSubview(view) + } + } + + private func addTapGestureRecognizer(to label: UILabel) { + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tappedLabelWithSender(_:))) + label.addGestureRecognizer(tapGestureRecognizer) + } + + func updateTextLabels(in textStackView: UIStackView, newViewModel: DiffListChangeViewModel) { + + for (index, label) in textLabels.enumerated() { + if let item = newViewModel.items[safeIndex: index] { + label.attributedText = item.textAttributedString + } + } + } + + func needsNewTextLabels(newViewModel: DiffListChangeViewModel) -> Bool { + guard let viewModel = viewModel else { + return true + } + + if viewModel.items != newViewModel.items { + return true + } + + return false + } +} + +extension DiffListChangeCell: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + contentView.backgroundColor = theme.colors.paperBackground + + if let viewModel = viewModel { + innerView.borderColor = viewModel.borderColor + innerView.layer.cornerRadius = viewModel.innerViewClipsToBounds ? 7 : 0 + innerView.clipsToBounds = viewModel.innerViewClipsToBounds + + headingContainerView.backgroundColor = viewModel.borderColor + headingLabel.attributedText = viewModel.headingAttributedString + } + + for shadedBackgroundView in shadedBackgroundViews { + shadedBackgroundView.backgroundColor = theme.colors.diffMoveParagraphBackground + } + + for spacerView in spacerViews { + spacerView.backgroundColor = theme.colors.paperBackground + } + + for subview in textStackView.arrangedSubviews { + subview.backgroundColor = theme.colors.paperBackground + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListChangeCell.xib b/Apps/Wikipedia/Wikipedia/Code/DiffListChangeCell.xib new file mode 100644 index 0000000..a80051c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListChangeCell.xib @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListChangeItemViewModel.swift b/Apps/Wikipedia/Wikipedia/Code/DiffListChangeItemViewModel.swift new file mode 100644 index 0000000..719e212 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListChangeItemViewModel.swift @@ -0,0 +1,327 @@ +import Foundation + +final class DiffListChangeItemViewModel { + let text: String + let highlightedRanges: [DiffHighlightRange] + let type: DiffListChangeType + let diffItemType: DiffItemType + let textAlignment: NSTextAlignment + let moveInfo: TransformMoveInfo? + private(set) var textPadding: NSDirectionalEdgeInsets + private let semanticContentAttribute: UISemanticContentAttribute + + private(set) var hasShadedBackgroundView: Bool + private(set) var inBetweenSpacing: CGFloat? + + var theme: Theme { + didSet { + textAttributedString = DiffListChangeItemViewModel.calculateAttributedString(with: text, highlightedRanges: highlightedRanges, traitCollection: traitCollection, theme: theme, type: type, diffItemType: diffItemType, moveInfo: moveInfo, semanticContentAttribute: semanticContentAttribute) + } + } + + var traitCollection: UITraitCollection { + didSet { + textAttributedString = DiffListChangeItemViewModel.calculateAttributedString(with: text, highlightedRanges: highlightedRanges, traitCollection: traitCollection, theme: theme, type: type, diffItemType: diffItemType, moveInfo: moveInfo, semanticContentAttribute: semanticContentAttribute) + } + } + + private(set) var textAttributedString: NSAttributedString? + + init(firstRevisionText: String, traitCollection: UITraitCollection, theme: Theme, semanticContentAttribute: UISemanticContentAttribute) { + let text = firstRevisionText + let theme = theme + self.text = text + self.traitCollection = traitCollection + self.theme = theme + let type = DiffListChangeType.singleRevison + let diffItemType = DiffItemType.addLine + self.type = type + self.diffItemType = diffItemType + self.moveInfo = nil + let semanticContentAttribute = semanticContentAttribute + self.semanticContentAttribute = semanticContentAttribute + + let highlightedRanges = [DiffHighlightRange(start: 0, length: text.count, type: .add)] + self.highlightedRanges = highlightedRanges + self.textAlignment = .natural + let textPaddingAndInBetweenSpacing = DiffListChangeItemViewModel.calculateTextPaddingAndInBetweenSpacing(type: type, diffItemType: diffItemType, nextMiddleItem: nil) + self.textPadding = textPaddingAndInBetweenSpacing.0 + self.inBetweenSpacing = nil + self.hasShadedBackgroundView = false + self.textAttributedString = DiffListChangeItemViewModel.calculateAttributedString(with: text, highlightedRanges: highlightedRanges, traitCollection: traitCollection, theme: theme, type: type, diffItemType: diffItemType, moveInfo: nil, semanticContentAttribute: semanticContentAttribute) + } + + init(item: TransformDiffItem, traitCollection: UITraitCollection, theme: Theme, type: DiffListChangeType, diffItemType: DiffItemType, nextMiddleItem: TransformDiffItem?, semanticContentAttribute: UISemanticContentAttribute) { + self.text = item.text + + self.traitCollection = traitCollection + self.theme = theme + self.type = type + self.diffItemType = diffItemType + + self.moveInfo = item.moveInfo + self.semanticContentAttribute = semanticContentAttribute + + // account for utf8 offsets in highlighted ranges + var convertedHighlightedRanges: [DiffHighlightRange] = [] + if let diffHighlightedRanges = item.highlightRanges { + for diffHighlightedRange in diffHighlightedRanges { + let start = diffHighlightedRange.start + let length = diffHighlightedRange.length + let type = diffHighlightedRange.type + + let fromIdx = text.utf8.index(text.utf8.startIndex, offsetBy: start) + let toIdx = text.utf8.index(fromIdx, offsetBy: length) + let nsRange = NSRange(fromIdx.. (textPadding: NSDirectionalEdgeInsets, inBetweenSpacing: CGFloat?) { + + var top: CGFloat = 0 + var bottom: CGFloat = 0 + var inBetweenSpacing: CGFloat? + if diffItemType == .moveSource || diffItemType == .moveDestination { + top = 10 + if let middleItem = nextMiddleItem, + middleItem.type == .moveSource || middleItem.type == .moveDestination { + bottom = 10 + inBetweenSpacing = 10 + } else { + bottom = 15 + } + } else { + if let middleItem = nextMiddleItem, + middleItem.type == .moveSource || middleItem.type == .moveDestination { + bottom = 10 + } + } + + switch type { + case .singleRevison: + let leading: CGFloat = (diffItemType == .moveSource || diffItemType == .moveDestination) ? 10 : 0 + let trailing: CGFloat = (diffItemType == .moveSource || diffItemType == .moveDestination) ? 10 : 0 + return (NSDirectionalEdgeInsets(top: top, leading: leading, bottom: bottom, trailing: trailing), inBetweenSpacing) + case .compareRevision: + return (NSDirectionalEdgeInsets(top: top, leading: 10, bottom: bottom, trailing: 10), inBetweenSpacing) + } + } + + private static func moveAttributedString(with text: String, diffItemType: DiffItemType, moveInfo: TransformMoveInfo, highlightedRanges: inout [DiffHighlightRange], traitCollection: UITraitCollection, theme: Theme) -> (moveAttributedString: NSAttributedString?, lengthOfPrefix: Int)? { + + guard !text.isEmpty, + (diffItemType == .moveSource || diffItemType == .moveDestination) else { + return nil + } + + var modifiedText = text + + // additional move changes that are specific to the client + if let moveIndex = moveInfo.groupedIndex { + + let moveIndexString = " \(moveIndex + 1) " + let imageAttachment = NSTextAttachment() + let imageName = moveInfo.linkDirection == .down ? "moveArrowDown" : "moveArrowUp" + imageAttachment.image = UIImage(named: imageName) + let imageString = NSAttributedString(attachment: imageAttachment) + + switch diffItemType { + case .moveSource: + + if let moveDistance = moveInfo.moveDistance { + var moveDistanceString: String + switch moveDistance { + case .section(let amount): + let moveDistanceSectionsFormat = WMFLocalizedString("diff-paragraph-moved-distance-section", value:"{{PLURAL:%1$d|%1$d section|%1$d sections}}", comment:"Diff view distance moved in sections when a paragraph has moved across sections - %1$@ is replaced with the number of sections a paragraph has moved across.") + moveDistanceString = String.localizedStringWithFormat(moveDistanceSectionsFormat, amount) + case .line(let amount): + let moveDistanceLinesFormat = WMFLocalizedString("diff-paragraph-moved-distance-line", value:"{{PLURAL:%1$d|%1$d line|%1$d lines}}", comment:"Diff view distance moved in line numbers when a paragraph has moved lines but remained in the same section - %1$@ is replaced with the number of lines a paragraph has moved across.") + moveDistanceString = String.localizedStringWithFormat(moveDistanceLinesFormat, amount) + } + + let paragraphMovedFormat = WMFLocalizedString("diff-paragraph-moved-format", value: "Paragraph moved %1$@ %2$@", comment: "Label in moved paragraph source location on diff view for indicating how far and what direction a pargraph has moved. %1$@ is replaced by the move direction (e.g. 'up' or 'down') and %2$@ is replaced by the move type and move distance (e.g. '2 lines', '4 sections')") + let upOrDownString: String + switch moveInfo.linkDirection { + case .down: upOrDownString = WMFLocalizedString("diff-paragraph-moved-direction-down", value: "down", comment: "Label in moved pararaph source location on diff view for indicating that a paragraph was moved down in the document.") + case .up: upOrDownString = WMFLocalizedString("diff-paragraph-moved-direction-up", value: "up", comment: "Label in moved pararaph source location on diff view for indicating that a paragraph was moved up in the document.") + } + + modifiedText = String.localizedStringWithFormat(paragraphMovedFormat, upOrDownString, moveDistanceString) + } else { + modifiedText = WMFLocalizedString("diff-paragraph-moved", value:"Paragraph moved", comment:"Label in diff to indicate that a paragraph has been moved. This label is in the location of where the paragraph was moved from.") + } + case .moveDestination: + + let originalHighlightedRanges = highlightedRanges + highlightedRanges.removeAll(keepingCapacity: true) + for range in originalHighlightedRanges { + let amountToOffset = imageString.string.count + moveIndexString.count + let newRange = DiffHighlightRange(start: range.start + amountToOffset, length: range.length, type: range.type) + highlightedRanges.append(newRange) + } + default: + assertionFailure("Cannot handle non-move diff item type here") + return nil + } + + let mutableAttributedString = NSMutableAttributedString(string: modifiedText) + + // insert move index number + let indexAttributedString = NSAttributedString(string: moveIndexString) + mutableAttributedString.insert(indexAttributedString, at: 0) + + // insert move arrow + mutableAttributedString.insert(imageString, at:0) + mutableAttributedString.addAttributes([NSAttributedString.Key.baselineOffset: -2], range: NSRange(location: 0, length: 1)) + + return (moveAttributedString: mutableAttributedString.copy() as? NSAttributedString, lengthOfPrefix: indexAttributedString.length + imageString.length) + } + + return nil + } + + private static func updateParamsForAddDeleteLine(text: inout String, diffItemType: DiffItemType, highlightedRanges: inout [DiffHighlightRange]) { + + guard diffItemType == .addLine || diffItemType == .deleteLine else { + return + } + + if text.isEmpty { + text = " " + } + + var highlightRange: DiffHighlightRangeType? = nil + switch diffItemType { + case .addLine: + highlightRange = .add + case .deleteLine: + highlightRange = .delete + default: + break + } + + if let highlightRange = highlightRange { + let newAddRange = DiffHighlightRange(start: 0, length: text.count, type: highlightRange) + highlightedRanges.append(newAddRange) + } + } + + private static func calculateAttributedString(with text: String, highlightedRanges: [DiffHighlightRange], traitCollection: UITraitCollection, theme: Theme, type: DiffListChangeType, diffItemType: DiffItemType, moveInfo: TransformMoveInfo?, semanticContentAttribute: UISemanticContentAttribute) -> NSAttributedString? { + + var modifiedText = text + var modifiedHighlightedRanges = highlightedRanges + + let regularFontStyle = DynamicTextStyle.subheadline + let boldFontStyle = DynamicTextStyle.boldSubheadline + + let font = diffItemType == .moveSource || diffItemType == .moveDestination ? UIFont.wmf_font(boldFontStyle, compatibleWithTraitCollection: traitCollection) : UIFont.wmf_font(regularFontStyle, compatibleWithTraitCollection: traitCollection) + + var moveItemAttributedString: NSAttributedString? + var lengthOfPrefix: Int? + switch diffItemType { + case .moveSource, .moveDestination: + if let moveInfo = moveInfo { + let moveResult = moveAttributedString(with: text, diffItemType: diffItemType, moveInfo: moveInfo, highlightedRanges: &modifiedHighlightedRanges, traitCollection: traitCollection, theme: theme) + moveItemAttributedString = moveResult?.0 + lengthOfPrefix = moveResult?.1 + } + case .addLine, .deleteLine: + updateParamsForAddDeleteLine(text: &modifiedText, diffItemType: diffItemType, highlightedRanges: &modifiedHighlightedRanges) + default: + break + } + + let paragraphStyle = NSMutableParagraphStyle() + let lineSpacing: CGFloat = 4 + paragraphStyle.lineSpacing = lineSpacing + paragraphStyle.lineHeightMultiple = font.lineHeightMultipleToMatch(lineSpacing: lineSpacing) + + if diffItemType == .moveSource { + paragraphStyle.alignment = .center + } else { + switch semanticContentAttribute { + case .forceRightToLeft: + paragraphStyle.alignment = .right + default: + paragraphStyle.alignment = .left + } + } + + let attributes = [NSAttributedString.Key.font: font, + NSAttributedString.Key.foregroundColor: theme.colors.primaryText, + NSAttributedString.Key.paragraphStyle: paragraphStyle.copy()] + + let finalAttributedStringToHighlight: NSMutableAttributedString + + if let moveItemAttributedString = moveItemAttributedString, + let lengthOfPrefix = lengthOfPrefix { + let moveIndexAttributes = [NSAttributedString.Key.foregroundColor: theme.colors.diffCompareAccent] + finalAttributedStringToHighlight = NSMutableAttributedString(attributedString: moveItemAttributedString) + finalAttributedStringToHighlight.addAttributes(attributes, range: NSRange(location: 0, length: moveItemAttributedString.length)) + finalAttributedStringToHighlight.addAttributes(moveIndexAttributes, range: NSRange(location: 0, length: lengthOfPrefix)) + } else { + finalAttributedStringToHighlight = NSMutableAttributedString(string: modifiedText, attributes: attributes) + } + + for range in modifiedHighlightedRanges { + + let nsRange = NSRange(location: range.start, length: range.length) + var highlightColor: UIColor? + let textColor: UIColor? + + let isNotLightAndEmpty = theme != Theme.light && (diffItemType == .addLine || diffItemType == .deleteLine) && + text.isEmpty + + switch range.type { + case .add: + highlightColor = isNotLightAndEmpty ? theme.colors.diffTextAdd : theme.colors.diffHighlightAdd + textColor = theme.colors.diffTextAdd + + case .delete: + highlightColor = isNotLightAndEmpty ? theme.colors.diffTextDelete : theme.colors.diffHighlightDelete + textColor = theme.colors.diffTextDelete + let deletedAttributes: [NSAttributedString.Key: Any] = [ + NSAttributedString.Key.strikethroughStyle:NSUnderlineStyle.single.rawValue, + NSAttributedString.Key.strikethroughColor:theme.colors.diffStrikethroughColor as Any + ] + finalAttributedStringToHighlight.addAttributes(deletedAttributes, range: nsRange) + } + + let font = UIFont.wmf_font(boldFontStyle, compatibleWithTraitCollection: traitCollection) + finalAttributedStringToHighlight.addAttribute(NSAttributedString.Key.font, value: font, range: nsRange) + + if let highlightColor = highlightColor { + finalAttributedStringToHighlight.addAttribute(NSAttributedString.Key.backgroundColor, value: highlightColor, range: nsRange) + } + + if let textColor = textColor { + finalAttributedStringToHighlight.addAttribute(NSAttributedString.Key.foregroundColor, value: textColor, range: nsRange) + } + } + + return finalAttributedStringToHighlight.copy() as? NSAttributedString + } +} + +extension DiffListChangeItemViewModel: Equatable { + static func == (lhs: DiffListChangeItemViewModel, rhs: DiffListChangeItemViewModel) -> Bool { + return lhs.highlightedRanges == rhs.highlightedRanges && + lhs.text == rhs.text + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListChangeViewModel.swift b/Apps/Wikipedia/Wikipedia/Code/DiffListChangeViewModel.swift new file mode 100644 index 0000000..0efb04c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListChangeViewModel.swift @@ -0,0 +1,264 @@ +import Foundation + +enum DiffListChangeType { + case singleRevison + case compareRevision +} + +final class DiffListChangeViewModel: DiffListGroupViewModel { + + let type: DiffListChangeType + let heading: String? + private(set) var headingAttributedString: NSAttributedString? + let items: [DiffListChangeItemViewModel] + var theme: Theme { + didSet { + + borderColor = DiffListChangeViewModel.calculateBorderColor(type: type, theme: theme) + let headingColor = DiffListChangeViewModel.calculateHeadingColor(type: type, theme: theme) + headingAttributedString = DiffListChangeViewModel.calculateHeadingAttributedString(headingColor: headingColor, text: heading, traitCollection: traitCollection) + + for item in items { + item.theme = theme + } + } + } + var traitCollection: UITraitCollection { + didSet { + for item in items { + item.traitCollection = traitCollection + } + + innerPadding = DiffListChangeViewModel.calculateInnerPadding(traitCollection: traitCollection) + let headingColor = DiffListChangeViewModel.calculateHeadingColor(type: type, theme: theme) + headingAttributedString = DiffListChangeViewModel.calculateHeadingAttributedString(headingColor: headingColor, text: heading, traitCollection: traitCollection) + } + } + + private var availableWidth: CGFloat { + return width - innerPadding.leading - innerPadding.trailing - stackViewPadding.leading - stackViewPadding.trailing + } + + private var _width: CGFloat + var width: CGFloat { + get { + return _width + } + set { + _width = newValue + height = DiffListChangeViewModel.calculateHeight(items: items, availableWidth: availableWidth, innerPadding: innerPadding, headingAttributedString: headingAttributedString, headingPadding: headingPadding, stackViewPadding: stackViewPadding) + } + } + + private(set) var borderColor: UIColor + private(set) var height: CGFloat = 0 + private(set) var innerPadding: NSDirectionalEdgeInsets + private(set) var headingPadding: NSDirectionalEdgeInsets + private(set) var stackViewPadding: NSDirectionalEdgeInsets + + let innerViewClipsToBounds: Bool + + init(type: DiffListChangeType, items: [DiffListChangeItemViewModel], theme: Theme, width: CGFloat, traitCollection: UITraitCollection, semanticContentAttribute: UISemanticContentAttribute) { + self.type = type + self.theme = theme + self._width = width + self.traitCollection = traitCollection + self.innerViewClipsToBounds = type == .compareRevision + self.heading = String.localizedStringWithFormat(CommonStrings.diffSingleLineFormat, 1) + self.items = items + + borderColor = DiffListChangeViewModel.calculateBorderColor(type: type, theme: theme) + innerPadding = DiffListChangeViewModel.calculateInnerPadding(traitCollection: traitCollection) + headingPadding = DiffListChangeViewModel.calculateHeadingPadding(type: type) + + let headingColor = DiffListChangeViewModel.calculateHeadingColor(type: type, theme: theme) + headingAttributedString = DiffListChangeViewModel.calculateHeadingAttributedString(headingColor: headingColor, text: heading, traitCollection: traitCollection) + stackViewPadding = DiffListChangeViewModel.calculateStackViewPadding(type: type, items: items) + + height = DiffListChangeViewModel.calculateHeight(items: items, availableWidth: availableWidth, innerPadding: innerPadding, headingAttributedString: headingAttributedString, headingPadding: headingPadding, stackViewPadding: stackViewPadding) + } + + init(type: DiffListChangeType, diffItems: [TransformDiffItem], theme: Theme, width: CGFloat, traitCollection: UITraitCollection, semanticContentAttribute: UISemanticContentAttribute) { + + self.type = type + self.theme = theme + self._width = width + self.traitCollection = traitCollection + self.innerViewClipsToBounds = type == .compareRevision + + switch type { + case .compareRevision: + self.heading = DiffListChangeViewModel.calculateHeadingsFromLineNumbers(diffItems: diffItems) + case .singleRevison: + self.heading = DiffListChangeViewModel.calculateHeadingsFromSectionTitles(diffItems: diffItems) + } + + var itemViewModels: [DiffListChangeItemViewModel] = [] + for (index, item) in diffItems.enumerated() { + + // passing in next middle item to avoid double-space calculations for moved items + let nextItem: TransformDiffItem? = diffItems[safeIndex: index + 1] + let changeItemViewModel = DiffListChangeItemViewModel(item: item, traitCollection: traitCollection, theme: theme, type: type, diffItemType: item.type, nextMiddleItem: nextItem, semanticContentAttribute: semanticContentAttribute) + + itemViewModels.append(changeItemViewModel) + } + + self.items = itemViewModels + + borderColor = DiffListChangeViewModel.calculateBorderColor(type: type, theme: theme) + innerPadding = DiffListChangeViewModel.calculateInnerPadding(traitCollection: traitCollection) + headingPadding = DiffListChangeViewModel.calculateHeadingPadding(type: type) + + let headingColor = DiffListChangeViewModel.calculateHeadingColor(type: type, theme: theme) + headingAttributedString = DiffListChangeViewModel.calculateHeadingAttributedString(headingColor: headingColor, text: heading, traitCollection: traitCollection) + stackViewPadding = DiffListChangeViewModel.calculateStackViewPadding(type: type, items: items) + + height = DiffListChangeViewModel.calculateHeight(items: items, availableWidth: availableWidth, innerPadding: innerPadding, headingAttributedString: headingAttributedString, headingPadding: headingPadding, stackViewPadding: stackViewPadding) + } + + private static func calculateHeadingsFromSectionTitles(diffItems: [TransformDiffItem]) -> String? { + + for item in diffItems { + if let sectionTitle = item.sectionTitle { + return sectionTitle + } + } + + return calculateHeadingsFromLineNumbers(diffItems: diffItems) + } + + private static func calculateHeadingsFromLineNumbers(diffItems: [TransformDiffItem]) -> String? { + + let lineNumbers = diffItems.compactMap { ($0.type == .deleteLine && $0.text.isEmpty) ? nil : $0.lineNumber } + + if let firstItemLineNumber = lineNumbers.first { + + let lastItemLineNumber = lineNumbers.last + + if diffItems.count == 1 || lastItemLineNumber == nil { + return String.localizedStringWithFormat(CommonStrings.diffSingleLineFormat, firstItemLineNumber) + } else if let lastItemLineNumber = lastItemLineNumber { + return String.localizedStringWithFormat(CommonStrings.diffMultiLineFormat, firstItemLineNumber, lastItemLineNumber) + } else { + return nil + } + + } else { + return nil + } + } + + private static func calculateBorderColor(type: DiffListChangeType, theme: Theme) -> UIColor { + switch type { + case .compareRevision: + return theme.colors.diffCompareAccent + case .singleRevison: + return theme.colors.paperBackground + } + } + + private static func calculateHeadingColor(type: DiffListChangeType, theme: Theme) -> UIColor { + switch type { + case .compareRevision: + return theme.colors.diffCompareChangeHeading + case .singleRevison: + return theme.colors.secondaryText + } + } + + private static func calculateStackViewPadding(type: DiffListChangeType, items: [DiffListChangeItemViewModel]) -> NSDirectionalEdgeInsets { + switch type { + case .compareRevision: + + var top: CGFloat = 10 + var bottom: CGFloat = 10 + if let firstItemType = items.first?.diffItemType, + let lastItemType = items.last?.diffItemType { + + if firstItemType == .moveDestination || firstItemType == .moveSource { + top = 0 + } + + if lastItemType == .moveDestination || lastItemType == .moveSource { + bottom = 0 + } + } + + return NSDirectionalEdgeInsets(top: top, leading: 0, bottom: bottom, trailing: 0) + case .singleRevison: + return NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0) + } + } + + private static func calculateHeadingPadding(type: DiffListChangeType) -> NSDirectionalEdgeInsets { + switch type { + case .compareRevision: + return NSDirectionalEdgeInsets(top: 10, leading: 7, bottom: 10, trailing: 7) + case .singleRevison: + return NSDirectionalEdgeInsets(top: 5, leading: 0, bottom: 10, trailing: 0) + } + } + + private static func calculateHeadingAttributedString(headingColor: UIColor, text: String?, traitCollection: UITraitCollection) -> NSAttributedString? { + + guard let text = text else { + return nil + } + + let font = UIFont.wmf_font(DynamicTextStyle.boldFootnote, compatibleWithTraitCollection: traitCollection) + let attributes: [NSAttributedString.Key: Any] = [ + NSAttributedString.Key.font: font, + NSAttributedString.Key.foregroundColor: headingColor + ] + return NSAttributedString(string: text, attributes: attributes) + } + + private static func calculateHeight(items: [DiffListChangeItemViewModel], availableWidth: CGFloat, innerPadding: NSDirectionalEdgeInsets, headingAttributedString: NSAttributedString?, headingPadding: NSDirectionalEdgeInsets, stackViewPadding: NSDirectionalEdgeInsets) -> CGFloat { + + var height: CGFloat = 0 + + for item in items { + if let textAttributedString = item.textAttributedString { + let newHeight = ceil(textAttributedString.boundingRect(with: CGSize(width: availableWidth - item.textPadding.leading - item.textPadding.trailing, height: CGFloat.infinity), options: [.usesLineFragmentOrigin], context: nil).height) + height += newHeight + } + + height += item.textPadding.top + height += item.textPadding.bottom + + if let inBetweenSpacing = item.inBetweenSpacing { + height += inBetweenSpacing + } + } + + // add heading height + height += headingPadding.top + height += headingPadding.bottom + if let headingAttributedString = headingAttributedString { + height += ceil(headingAttributedString.boundingRect(with: CGSize(width: availableWidth, height: CGFloat.infinity), options: [.usesLineFragmentOrigin], context: nil).height) + } + + height += innerPadding.top + height += innerPadding.bottom + height += stackViewPadding.top + height += stackViewPadding.bottom + + return height + } + + private static func calculateInnerPadding(traitCollection: UITraitCollection) -> NSDirectionalEdgeInsets { + switch (traitCollection.horizontalSizeClass, traitCollection.verticalSizeClass) { + case (.regular, .regular): + return NSDirectionalEdgeInsets(top: 0, leading: 50, bottom: 0, trailing: 50) + default: + return NSDirectionalEdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15) + } + } + + func updateSize(width: CGFloat, traitCollection: UITraitCollection) { + _width = width + self.traitCollection = traitCollection + + height = DiffListChangeViewModel.calculateHeight(items: items, availableWidth: availableWidth, innerPadding: innerPadding, headingAttributedString: headingAttributedString, headingPadding: headingPadding, stackViewPadding: stackViewPadding) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListContextCell.swift b/Apps/Wikipedia/Wikipedia/Code/DiffListContextCell.swift new file mode 100644 index 0000000..4a6bde4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListContextCell.swift @@ -0,0 +1,158 @@ +import UIKit + +protocol DiffListContextCellDelegate: AnyObject { + func didTapContextExpand(indexPath: IndexPath) +} + +class DiffListContextCell: UICollectionViewCell { + static let reuseIdentifier = "DiffListContextCell" + + @IBOutlet var innerLeadingConstraint: NSLayoutConstraint! + @IBOutlet var innerTrailingConstraint: NSLayoutConstraint! + @IBOutlet var innerBottomConstraint: NSLayoutConstraint! + @IBOutlet var innerTopConstraint: NSLayoutConstraint! + @IBOutlet var containerStackView: UIStackView! + @IBOutlet var contextItemStackView: UIStackView! + + @IBOutlet var headingLabel: UILabel! + @IBOutlet var expandButton: UIButton! + + private var viewModel: DiffListContextViewModel? + private var indexPath: IndexPath? + + weak var delegate: DiffListContextCellDelegate? + + func update(_ viewModel: DiffListContextViewModel, indexPath: IndexPath?) { + + if let indexPath = indexPath { + self.indexPath = indexPath + } + + innerLeadingConstraint.constant = viewModel.innerPadding.leading + innerTrailingConstraint.constant = viewModel.innerPadding.trailing + innerTopConstraint.constant = viewModel.innerPadding.top + innerBottomConstraint.constant = viewModel.innerPadding.bottom + + containerStackView.spacing = DiffListContextViewModel.containerStackSpacing + contextItemStackView.spacing = DiffListContextViewModel.contextItemStackSpacing + + headingLabel.font = viewModel.headingFont + headingLabel.text = viewModel.heading + + if self.viewModel == nil { + expandButton.setTitle(viewModel.expandButtonTitle, for: .normal) + } else { + expandButton.alpha = 0 + expandButton.setTitle(viewModel.expandButtonTitle, for: .normal) + UIView.animate(withDuration: 0.2) { + self.expandButton.alpha = 1 + } + } + + expandButton.titleLabel?.font = viewModel.contextFont + + apply(theme: viewModel.theme) + + if needsNewContextViews(newViewModel: viewModel) { + removeContextViews(from: contextItemStackView) + addContextViews(to: contextItemStackView, newViewModel: viewModel) + } + + updateContextViews(in: contextItemStackView, newViewModel: viewModel, theme: viewModel.theme) + + self.viewModel = viewModel + } + + @IBAction func tappedExpandButton(_ sender: UIButton) { + if let indexPath = indexPath { + delegate?.didTapContextExpand(indexPath: indexPath) + } + } +} + +private extension DiffListContextCell { + + func removeContextViews(from stackView: UIStackView) { + for subview in stackView.arrangedSubviews { + stackView.removeArrangedSubview(subview) + subview.removeFromSuperview() + } + } + + func addContextViews(to stackView: UIStackView, newViewModel: DiffListContextViewModel) { + for item in newViewModel.items { + + if item != nil { + + // needs label + let label = UILabel() + label.numberOfLines = 0 + label.lineBreakMode = .byWordWrapping + label.translatesAutoresizingMaskIntoConstraints = false + + let view = UIView(frame: .zero) + view.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(label) + + let top = label.topAnchor.constraint(equalTo: view.topAnchor, constant: DiffListContextViewModel.contextItemTextPadding.top) + let bottom = view.bottomAnchor.constraint(equalTo: label.bottomAnchor, constant: DiffListContextViewModel.contextItemTextPadding.bottom) + let leading = label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: DiffListContextViewModel.contextItemTextPadding.leading) + let trailing = view.trailingAnchor.constraint(equalTo: label.trailingAnchor, constant: DiffListContextViewModel.contextItemTextPadding.trailing) + + view.addConstraints([top, bottom, leading, trailing]) + + stackView.addArrangedSubview(view) + } else { + let view = UIView(frame: .zero) + + view.translatesAutoresizingMaskIntoConstraints = false + let heightConstraint = view.heightAnchor.constraint(equalToConstant: newViewModel.emptyContextLineHeight) + view.addConstraint(heightConstraint) + + stackView.addArrangedSubview(view) + } + + + } + } + + func updateContextViews(in stackView: UIStackView, newViewModel: DiffListContextViewModel, theme: Theme) { + for (index, subview) in stackView.arrangedSubviews.enumerated() { + + subview.backgroundColor = theme.colors.diffContextItemBackground + subview.borderColor = theme.colors.diffContextItemBorder + subview.borderWidth = 1 + subview.layer.cornerRadius = 5 + + if let item = newViewModel.items[safeIndex: index] as? DiffListContextItemViewModel, + let label = subview.subviews.first as? UILabel { + label.attributedText = item.textAttributedString + } + } + } + + func needsNewContextViews(newViewModel: DiffListContextViewModel) -> Bool { + guard let viewModel = viewModel else { + return true + } + + if viewModel.items != newViewModel.items { + return true + } + + return false + } +} + +extension DiffListContextCell: Themeable { + func apply(theme: Theme) { + headingLabel.textColor = theme.colors.secondaryText + expandButton.tintColor = theme.colors.link + backgroundColor = theme.colors.paperBackground + contentView.backgroundColor = theme.colors.paperBackground + + if let viewModel = viewModel { + updateContextViews(in: contextItemStackView, newViewModel: viewModel, theme: theme) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListContextCell.xib b/Apps/Wikipedia/Wikipedia/Code/DiffListContextCell.xib new file mode 100644 index 0000000..747fef2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListContextCell.xib @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListContextViewModel.swift b/Apps/Wikipedia/Wikipedia/Code/DiffListContextViewModel.swift new file mode 100644 index 0000000..4fdb0a4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListContextViewModel.swift @@ -0,0 +1,212 @@ +import Foundation + +final class DiffListContextItemViewModel { + private let text: String + private let semanticContentAttribute: UISemanticContentAttribute + var theme: Theme { + didSet { + self.textAttributedString = DiffListContextItemViewModel.calculateAttributedString(with: text, semanticContentAttribute: semanticContentAttribute, theme: theme, contextFont: contextFont) + } + } + var contextFont: UIFont { + didSet { + self.textAttributedString = DiffListContextItemViewModel.calculateAttributedString(with: text, semanticContentAttribute: semanticContentAttribute, theme: theme, contextFont: contextFont) + } + } + + private(set) var textAttributedString: NSAttributedString + + init(text: String, semanticContentAttribute: UISemanticContentAttribute, theme: Theme, contextFont: UIFont) { + self.text = text + self.semanticContentAttribute = semanticContentAttribute + self.theme = theme + self.contextFont = contextFont + + self.textAttributedString = DiffListContextItemViewModel.calculateAttributedString(with: text, semanticContentAttribute: semanticContentAttribute, theme: theme, contextFont: contextFont) + } + + private static func calculateAttributedString(with text: String, semanticContentAttribute: UISemanticContentAttribute, theme: Theme, contextFont: UIFont) -> NSAttributedString { + + let paragraphStyle = NSMutableParagraphStyle() + let lineSpacing: CGFloat = 4 + paragraphStyle.lineSpacing = lineSpacing + paragraphStyle.lineHeightMultiple = contextFont.lineHeightMultipleToMatch(lineSpacing: lineSpacing) + switch semanticContentAttribute { + case .forceRightToLeft: + paragraphStyle.alignment = .right + default: + paragraphStyle.alignment = .left + } + let attributes = [NSAttributedString.Key.font: contextFont, + NSAttributedString.Key.paragraphStyle: paragraphStyle, + NSAttributedString.Key.foregroundColor: theme.colors.primaryText] + + return NSAttributedString(string: text, attributes: attributes) + } +} + +extension DiffListContextItemViewModel: Equatable { + static func == (lhs: DiffListContextItemViewModel, rhs: DiffListContextItemViewModel) -> Bool { + return lhs.text == rhs.text + } +} + +final class DiffListContextViewModel: DiffListGroupViewModel { + let heading: String + var isExpanded: Bool + let items: [DiffListContextItemViewModel?] + var theme: Theme { + didSet { + for item in items { + item?.theme = theme + } + } + } + var expandButtonTitle: String { + return isExpanded ? WMFLocalizedString("diff-context-lines-expanded-button-title", value:"Hide", comment:"Expand button title in diff compare context section when section is in expanded state.") : WMFLocalizedString("diff-context-lines-collapsed-button-title", value:"Show", comment:"Expand button title in diff compare context section when section is in collapsed state.") + } + + private(set) var contextFont: UIFont { + didSet { + for item in items { + item?.contextFont = contextFont + } + } + } + private(set) var headingFont: UIFont + + private var _width: CGFloat + var width: CGFloat { + get { + return _width + } + set { + _width = newValue + expandedHeight = DiffListContextViewModel.calculateExpandedHeight(items: items, heading: heading, availableWidth: availableWidth, innerPadding: innerPadding, contextItemPadding: DiffListContextViewModel.contextItemTextPadding, contextFont: contextFont, headingFont: headingFont, emptyContextLineHeight: emptyContextLineHeight) + height = DiffListContextViewModel.calculateCollapsedHeight(items: items, heading: heading, availableWidth: availableWidth, innerPadding: innerPadding, contextItemPadding: DiffListContextViewModel.contextItemTextPadding, contextFont: contextFont, headingFont: headingFont) + } + } + var traitCollection: UITraitCollection { + didSet { + innerPadding = DiffListContextViewModel.calculateInnerPadding(traitCollection: traitCollection) + contextFont = UIFont.wmf_font(contextDynamicTextStyle, compatibleWithTraitCollection: traitCollection) + headingFont = UIFont.wmf_font(.boldFootnote, compatibleWithTraitCollection: traitCollection) + } + } + + private let contextDynamicTextStyle = DynamicTextStyle.subheadline + private(set) var height: CGFloat = 0 + private(set) var expandedHeight: CGFloat = 0 + private(set) var innerPadding: NSDirectionalEdgeInsets + + static let contextItemTextPadding = NSDirectionalEdgeInsets(top: 3, leading: 8, bottom: 8, trailing: 8) + static let contextItemStackSpacing: CGFloat = 5 + static let containerStackSpacing: CGFloat = 15 + + private var availableWidth: CGFloat { + return width - innerPadding.leading - innerPadding.trailing - DiffListContextViewModel.contextItemTextPadding.leading - DiffListContextViewModel.contextItemTextPadding.trailing + } + var emptyContextLineHeight: CGFloat { + return contextFont.pointSize * 1.8 + } + + init(diffItems: [TransformDiffItem], isExpanded: Bool, theme: Theme, width: CGFloat, traitCollection: UITraitCollection, semanticContentAttribute: UISemanticContentAttribute) { + + self.isExpanded = isExpanded + self.theme = theme + self._width = width + self.traitCollection = traitCollection + + if let firstItemLineNumber = diffItems.first?.lineNumber, + let lastItemLineNumber = diffItems.last?.lineNumber { + + if diffItems.count == 1 { + self.heading = String.localizedStringWithFormat(CommonStrings.diffSingleLineFormat, firstItemLineNumber) + } else { + self.heading = String.localizedStringWithFormat(CommonStrings.diffMultiLineFormat, firstItemLineNumber, lastItemLineNumber) + } + + } else { + self.heading = "" // tonitodo: optional would be better + } + + let contextFont = UIFont.wmf_font(contextDynamicTextStyle, compatibleWithTraitCollection: traitCollection) + + self.items = diffItems.map({ (item) -> DiffListContextItemViewModel? in + if item.text.isEmpty { + return nil + } + + return DiffListContextItemViewModel(text: item.text, semanticContentAttribute: semanticContentAttribute, theme: theme, contextFont: contextFont) + }) + + self.contextFont = contextFont + self.headingFont = UIFont.wmf_font(.semiboldFootnote, compatibleWithTraitCollection: traitCollection) + + innerPadding = DiffListContextViewModel.calculateInnerPadding(traitCollection: traitCollection) + + expandedHeight = DiffListContextViewModel.calculateExpandedHeight(items: items, heading: heading, availableWidth: availableWidth, innerPadding: innerPadding, contextItemPadding: DiffListContextViewModel.contextItemTextPadding, contextFont: contextFont, headingFont: headingFont, emptyContextLineHeight: emptyContextLineHeight) + height = DiffListContextViewModel.calculateCollapsedHeight(items: items, heading: heading, availableWidth: availableWidth, innerPadding: innerPadding, contextItemPadding: DiffListContextViewModel.contextItemTextPadding, contextFont: contextFont, headingFont: headingFont) + } + + private static func calculateInnerPadding(traitCollection: UITraitCollection) -> NSDirectionalEdgeInsets { + switch (traitCollection.horizontalSizeClass, traitCollection.verticalSizeClass) { + case (.regular, .regular): + return NSDirectionalEdgeInsets(top: 0, leading: 50, bottom: 0, trailing: 50) + default: + return NSDirectionalEdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15) + } + } + + private static func calculateExpandedHeight(items: [DiffListContextItemViewModel?], heading: String, availableWidth: CGFloat, innerPadding: NSDirectionalEdgeInsets, contextItemPadding: NSDirectionalEdgeInsets, contextFont: UIFont, headingFont: UIFont, emptyContextLineHeight: CGFloat) -> CGFloat { + + var height: CGFloat = 0 + height = calculateCollapsedHeight(items: items, heading: heading, availableWidth: availableWidth, innerPadding: innerPadding, contextItemPadding: contextItemPadding, contextFont: contextFont, headingFont: headingFont) + + for (index, item) in items.enumerated() { + + height += contextItemPadding.top + + let itemTextHeight: CGFloat + if let item = item { + itemTextHeight = ceil(item.textAttributedString.boundingRect(with: CGSize(width: availableWidth, height: CGFloat.infinity), options: [.usesLineFragmentOrigin], context: nil).height) + } else { + itemTextHeight = emptyContextLineHeight + } + + height += itemTextHeight + height += contextItemPadding.bottom + + if index < (items.count - 1) { + height += DiffListContextViewModel.contextItemStackSpacing + } + } + + height += DiffListContextViewModel.containerStackSpacing + + return height + } + + private static func calculateCollapsedHeight(items: [DiffListContextItemViewModel?], heading: String, availableWidth: CGFloat, innerPadding: NSDirectionalEdgeInsets, contextItemPadding: NSDirectionalEdgeInsets, contextFont: UIFont, headingFont: UIFont) -> CGFloat { + + var height: CGFloat = 0 + + // add heading height + + height += innerPadding.top + let attributedString = NSAttributedString(string: heading, attributes: [NSAttributedString.Key.font: headingFont]) + height += ceil(attributedString.boundingRect(with: CGSize(width: availableWidth, height: CGFloat.infinity), options: [.usesLineFragmentOrigin], context: nil).height) + + height += innerPadding.bottom + + return height + } + + func updateSize(width: CGFloat, traitCollection: UITraitCollection) { + _width = width + self.traitCollection = traitCollection + + expandedHeight = DiffListContextViewModel.calculateExpandedHeight(items: items, heading: heading, availableWidth: availableWidth, innerPadding: innerPadding, contextItemPadding: DiffListContextViewModel.contextItemTextPadding, contextFont: contextFont, headingFont: headingFont, emptyContextLineHeight: emptyContextLineHeight) + height = DiffListContextViewModel.calculateCollapsedHeight(items: items, heading: heading, availableWidth: availableWidth, innerPadding: innerPadding, contextItemPadding: DiffListContextViewModel.contextItemTextPadding, contextFont: contextFont, headingFont: headingFont) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListGroupViewModel.swift b/Apps/Wikipedia/Wikipedia/Code/DiffListGroupViewModel.swift new file mode 100644 index 0000000..23d64a5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListGroupViewModel.swift @@ -0,0 +1,10 @@ +import Foundation + +// tonitodo: rename since unedited lines isn't really a group +protocol DiffListGroupViewModel { + var theme: Theme { get set } + var width: CGFloat { get set } + var height: CGFloat { get } + var traitCollection: UITraitCollection { get } + func updateSize(width: CGFloat, traitCollection: UITraitCollection) +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListUneditedCell.swift b/Apps/Wikipedia/Wikipedia/Code/DiffListUneditedCell.swift new file mode 100644 index 0000000..43bfefa --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListUneditedCell.swift @@ -0,0 +1,36 @@ +import UIKit + +class DiffListUneditedCell: UICollectionViewCell { + + static let reuseIdentifier = "DiffListUneditedCell" + + @IBOutlet var innerLeadingConstraint: NSLayoutConstraint! + @IBOutlet var innerTrailingConstraint: NSLayoutConstraint! + @IBOutlet var innerTopConstraint: NSLayoutConstraint! + @IBOutlet var innerBottomConstraint: NSLayoutConstraint! + @IBOutlet var textLabel: UILabel! + @IBOutlet var divView: UIView! + @IBOutlet var textBackgroundView: UIView! + + func update(_ viewModel: DiffListUneditedViewModel) { + innerLeadingConstraint.constant = viewModel.innerPadding.leading + innerTrailingConstraint.constant = viewModel.innerPadding.trailing + innerTopConstraint.constant = viewModel.innerPadding.top + innerBottomConstraint.constant = viewModel.innerPadding.bottom + + textLabel.font = viewModel.font + textLabel.text = viewModel.text + + apply(theme: viewModel.theme) + } +} + +extension DiffListUneditedCell: Themeable { + func apply(theme: Theme) { + textLabel.textColor = theme.colors.secondaryText + + backgroundColor = theme.colors.paperBackground + textBackgroundView.backgroundColor = theme.colors.paperBackground + divView.backgroundColor = theme.colors.border + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListUneditedCell.xib b/Apps/Wikipedia/Wikipedia/Code/DiffListUneditedCell.xib new file mode 100644 index 0000000..ee2f0c7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListUneditedCell.xib @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListUneditedViewModel.swift b/Apps/Wikipedia/Wikipedia/Code/DiffListUneditedViewModel.swift new file mode 100644 index 0000000..4afa1d9 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListUneditedViewModel.swift @@ -0,0 +1,76 @@ +import Foundation + +final class DiffListUneditedViewModel: DiffListGroupViewModel { + private(set) var height: CGFloat = 0 + + private var _width: CGFloat + var width: CGFloat { + get { + return _width + } + set { + _width = newValue + height = DiffListUneditedViewModel.calculateHeight(text: text, availableWidth: availableWidth, innerPadding: innerPadding, font: font) + } + } + + var traitCollection: UITraitCollection { + didSet { + font = DiffListUneditedViewModel.calculateTextLabelFont(traitCollection: traitCollection) + } + } + + let text: String + var theme: Theme + private(set) var font: UIFont + private(set) var innerPadding: NSDirectionalEdgeInsets + + private var availableWidth: CGFloat { + return width - innerPadding.leading - innerPadding.trailing + } + + init(numberOfUneditedLines: Int, theme: Theme, width: CGFloat, traitCollection: UITraitCollection) { + self.theme = theme + self._width = width + self.traitCollection = traitCollection + self.theme = theme + self.innerPadding = DiffListUneditedViewModel.calculateInnerPadding(traitCollection: traitCollection) + self.font = DiffListUneditedViewModel.calculateTextLabelFont(traitCollection: traitCollection) + + self.text = String.localizedStringWithFormat(WMFLocalizedString("diff-unedited-lines-format", value:"{{PLURAL:%1$d|%1$d line unedited|%1$d lines unedited}}", comment:"Label in diff to indicate how many lines are not displayed because they were not changed. %1$d is replaced by the number of unedited lines."), numberOfUneditedLines) + self.height = DiffListUneditedViewModel.calculateHeight(text: text, availableWidth: availableWidth, innerPadding: innerPadding, font: font) + } + + private static func calculateInnerPadding(traitCollection: UITraitCollection) -> NSDirectionalEdgeInsets { + switch (traitCollection.horizontalSizeClass, traitCollection.verticalSizeClass) { + case (.regular, .regular): + return NSDirectionalEdgeInsets(top: 0, leading: 50, bottom: 0, trailing: 50) + default: + return NSDirectionalEdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15) + } + } + + private static func calculateTextLabelFont(traitCollection: UITraitCollection) -> UIFont { + return UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + } + + private static func calculateHeight(text: String, availableWidth: CGFloat, innerPadding: NSDirectionalEdgeInsets, font: UIFont) -> CGFloat { + + var height: CGFloat = 0 + + height += innerPadding.top + let attributedString = NSAttributedString(string: text, attributes: [NSAttributedString.Key.font: font]) + height += ceil(attributedString.boundingRect(with: CGSize(width: availableWidth, height: CGFloat.infinity), options: [.usesLineFragmentOrigin], context: nil).height) + + height += innerPadding.bottom + + return height + } + + func updateSize(width: CGFloat, traitCollection: UITraitCollection) { + _width = width + self.traitCollection = traitCollection + + height = DiffListUneditedViewModel.calculateHeight(text: text, availableWidth: availableWidth, innerPadding: innerPadding, font: font) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffListViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DiffListViewController.swift new file mode 100644 index 0000000..c8b198b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffListViewController.swift @@ -0,0 +1,367 @@ +import UIKit + +protocol DiffListDelegate: AnyObject { + func diffListScrollViewDidScroll(_ scrollView: UIScrollView) +} + +class DiffListViewController: ViewController { + + enum ListUpdateType { + case itemExpandUpdate(indexPath: IndexPath) // tapped context cell to expand + case layoutUpdate(collectionViewWidth: CGFloat, traitCollection: UITraitCollection) // willTransitionToSize - simple rotation that keeps size class + case initialLoad(width: CGFloat) + case theme(theme: Theme) + } + + lazy private(set) var collectionView: UICollectionView = { + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layoutCopy) + collectionView.dataSource = self + collectionView.delegate = self + collectionView.contentInsetAdjustmentBehavior = .never + collectionView.alwaysBounceVertical = true + scrollView = collectionView + return collectionView + }() + + var layoutCopy: UICollectionViewFlowLayout { + let layout = UICollectionViewFlowLayout() + layout.minimumInteritemSpacing = 0 + layout.minimumLineSpacing = 20 + switch type { + case .single: + layout.sectionInset = UIEdgeInsets(top: 15, left: 0, bottom: 10, right: 0) + case .compare: + layout.sectionInset = UIEdgeInsets(top: 25, left: 0, bottom: 10, right: 0) + } + + return layout + } + + private var dataSource: [DiffListGroupViewModel] = [] + private weak var delegate: DiffListDelegate? + private let type: DiffContainerViewModel.DiffType + + private var updateWidthsOnLayoutSubviews = false + private var isRotating = false + + private var centerIndexPath: IndexPath? { + let center = view.convert(collectionView.center, to: collectionView) + return collectionView.indexPathForItem(at: center) + } + private var indexPathBeforeRotating: IndexPath? + private let chunkedHeightCalculationsConcurrentQueue = DispatchQueue(label: "org.wikipedia.diff.chunkedHeightCalculations", qos: .userInteractive, attributes: .concurrent) + private let layoutSubviewsHeightCalculationsSerialQueue = DispatchQueue(label: "org.wikipedia.diff.layoutHeightCalculations", qos: .userInteractive) + + private var scrollDidFinishInfo: (indexPathToScrollTo: IndexPath, changeItemToScrollTo: Int)? + + init(theme: Theme, delegate: DiffListDelegate?, type: DiffContainerViewModel.DiffType) { + self.type = type + super.init(nibName: nil, bundle: nil) + self.theme = theme + self.delegate = delegate + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + view.addSubview(collectionView) + collectionView.translatesAutoresizingMaskIntoConstraints = false + view.addConstraints([ + view.safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: collectionView.leadingAnchor), + view.safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: collectionView.trailingAnchor), + view.topAnchor.constraint(equalTo: collectionView.topAnchor), + view.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor) + ]) + collectionView.register(DiffListChangeCell.wmf_classNib(), forCellWithReuseIdentifier: DiffListChangeCell.reuseIdentifier) + collectionView.register(DiffListContextCell.wmf_classNib(), forCellWithReuseIdentifier: DiffListContextCell.reuseIdentifier) + collectionView.register(DiffListUneditedCell.wmf_classNib(), forCellWithReuseIdentifier: DiffListUneditedCell.reuseIdentifier) + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + if updateWidthsOnLayoutSubviews { + + // More improvements could be size caching & putting layoutSubviewsHeightCalculationsSerialQueue instead into an NSOperation to be cancelled if another viewDidLayoutSubviews is called. + // tonitodo: clean up - move this and updateListViewModel methods into separate class, DiffListSizeCalculator or something + let updateType = ListUpdateType.layoutUpdate(collectionViewWidth: self.collectionView.frame.width, traitCollection: self.traitCollection) + + // actually not sure if this serial queue is needed or simply calling on the main thread (also serial) is the same. this also seems faster than without though. + layoutSubviewsHeightCalculationsSerialQueue.async { + + self.backgroundUpdateListViewModels(listViewModel: self.dataSource, updateType: updateType) { + + DispatchQueue.main.async { + self.applyListViewModelChanges(updateType: updateType) + + if let indexPathBeforeRotating = self.indexPathBeforeRotating { + self.collectionView.scrollToItem(at: indexPathBeforeRotating, at: .centeredVertically, animated: false) + self.indexPathBeforeRotating = nil + } + + self.updateScrollViewInsets() + } + + } + } + } + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + + updateWidthsOnLayoutSubviews = true + isRotating = true + + self.indexPathBeforeRotating = centerIndexPath + coordinator.animate(alongsideTransition: { (context) in + + // nothing + + }) { (context) in + self.updateWidthsOnLayoutSubviews = false + self.isRotating = false + } + + } + + override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) { + super.willTransition(to: newCollection, with: coordinator) + + updateWidthsOnLayoutSubviews = true + coordinator.animate(alongsideTransition: { (context) in + }) { (context) in + self.updateWidthsOnLayoutSubviews = false + } + + } + + override func scrollViewDidScroll(_ scrollView: UIScrollView) { + super.scrollViewDidScroll(scrollView) + delegate?.diffListScrollViewDidScroll(scrollView) + } + + override func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { + super.scrollViewDidEndScrollingAnimation(scrollView) + + if let scrollDidFinishInfo = scrollDidFinishInfo { + scrollToChangeItem(cellIndexPath: scrollDidFinishInfo.indexPathToScrollTo, itemIndex: scrollDidFinishInfo.changeItemToScrollTo) + self.scrollDidFinishInfo = nil + } + } + + func updateListViewModels(listViewModel: [DiffListGroupViewModel], updateType: DiffListViewController.ListUpdateType) { + + switch updateType { + case .itemExpandUpdate(let indexPath): + + if let item = listViewModel[safeIndex: indexPath.item] as? DiffListContextViewModel { + item.isExpanded.toggle() + } + + case .layoutUpdate(let width, let traitCollection): + for item in listViewModel { + if item.width != width || item.traitCollection != traitCollection { + item.updateSize(width: width, traitCollection: traitCollection) + } + } + + case .initialLoad(let width): + for var item in listViewModel { + if item.width != width { + item.width = width + } + } + self.dataSource = listViewModel + case .theme(let theme): + for var item in listViewModel { + if item.theme != theme { + item.theme = theme + } + } + } + } + + override func apply(theme: Theme) { + + super.apply(theme: theme) + + guard isViewLoaded else { + return + } + + updateListViewModels(listViewModel: dataSource, updateType: .theme(theme: theme)) + applyListViewModelChanges(updateType: .theme(theme: theme)) + + collectionView.backgroundColor = theme.colors.paperBackground + } + + func applyListViewModelChanges(updateType: DiffListViewController.ListUpdateType) { + switch updateType { + case .itemExpandUpdate: + + collectionView.setCollectionViewLayout(layoutCopy, animated: true) + + default: + collectionView.reloadData() + } + } +} + +private extension DiffListViewController { + + func backgroundUpdateListViewModels(listViewModel: [DiffListGroupViewModel], updateType: DiffListViewController.ListUpdateType, completion: @escaping () -> Void) { + + let group = DispatchGroup() + + let chunked = listViewModel.chunked(into: 10) + + for chunk in chunked { + chunkedHeightCalculationsConcurrentQueue.async(group: group) { + + self.updateListViewModels(listViewModel: chunk, updateType: updateType) + } + } + + group.notify(queue: layoutSubviewsHeightCalculationsSerialQueue) { + completion() + } + } + + func scrollToChangeItem(cellIndexPath: IndexPath, itemIndex: Int) { + if let cell = collectionView.cellForItem(at: cellIndexPath) as? DiffListChangeCell, + let offsetToView = cell.yLocationOfItem(index: itemIndex, convertView: view) { + + let midPointTarget = collectionView.frame.height / 2 + let delta = midPointTarget - offsetToView + collectionView.setContentOffset(CGPoint(x: collectionView.contentOffset.x, y: collectionView.contentOffset.y - delta), animated: true) + } + } +} + +extension DiffListViewController: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return dataSource.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + + guard let viewModel = dataSource[safeIndex: indexPath.item] else { + return UICollectionViewCell() + } + + if let viewModel = viewModel as? DiffListChangeViewModel, + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DiffListChangeCell.reuseIdentifier, for: indexPath) as? DiffListChangeCell { + cell.update(viewModel) + cell.delegate = self + return cell + } else if let viewModel = viewModel as? DiffListContextViewModel, + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DiffListContextCell.reuseIdentifier, for: indexPath) as? DiffListContextCell { + cell.update(viewModel, indexPath: indexPath) + cell.delegate = self + return cell + } else if let viewModel = viewModel as? DiffListUneditedViewModel, + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DiffListUneditedCell.reuseIdentifier, for: indexPath) as? DiffListUneditedCell { + cell.update(viewModel) + return cell + } + + return UICollectionViewCell() + } +} + +extension DiffListViewController: UICollectionViewDelegateFlowLayout { + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + + guard let viewModel = dataSource[safeIndex: indexPath.item] else { + return .zero + } + + if let contextViewModel = viewModel as? DiffListContextViewModel { + let height = contextViewModel.isExpanded ? contextViewModel.expandedHeight : contextViewModel.height + return CGSize(width: min(collectionView.frame.width, contextViewModel.width), height: height) + } + + return CGSize(width: min(collectionView.frame.width, viewModel.width), height: viewModel.height) + + } + + func collectionView(_ collectionView: UICollectionView, targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint { + + if !isRotating { + // prevents jumping when expanding/collapsing context cell + return collectionView.contentOffset + } else { + return proposedContentOffset + } + + } +} + +extension DiffListViewController: DiffListContextCellDelegate { + func didTapContextExpand(indexPath: IndexPath) { + + updateListViewModels(listViewModel: dataSource, updateType: .itemExpandUpdate(indexPath: indexPath)) + applyListViewModelChanges(updateType: .itemExpandUpdate(indexPath: indexPath)) + + if let contextViewModel = dataSource[safeIndex: indexPath.item] as? DiffListContextViewModel, + let cell = collectionView.cellForItem(at: indexPath) as? DiffListContextCell { + cell.update(contextViewModel, indexPath: indexPath) + } + } +} + +extension DiffListViewController: DiffListChangeCellDelegate { + func didTapItem(item: DiffListChangeItemViewModel) { + + guard let tappedMoveInfo = item.moveInfo else { + return + } + + let tappedLinkId = tappedMoveInfo.linkId + let moveDirection = tappedMoveInfo.linkDirection + + var indexOfOtherMoveCell: Int? + var changeItemToScrollTo: Int? + for (index, viewModel) in dataSource.enumerated() { + if let changeViewModel = viewModel as? DiffListChangeViewModel { + for (subindex, item) in changeViewModel.items.enumerated() { + if let moveInfo = item.moveInfo, + moveInfo.id == tappedLinkId { + indexOfOtherMoveCell = index + changeItemToScrollTo = subindex + } + } + } + } + + if let indexOfOtherMoveCell = indexOfOtherMoveCell, + let changeItemToScrollTo = changeItemToScrollTo { + + let indexPathOfOtherMoveCell = IndexPath(item: indexOfOtherMoveCell, section: 0) + let visibleIndexPaths = collectionView.indexPathsForVisibleItems + + if visibleIndexPaths.contains(indexPathOfOtherMoveCell) { // cell already configured, skip straight to detecting offset needed to get top of *item* on screen. + + scrollToChangeItem(cellIndexPath: indexPathOfOtherMoveCell, itemIndex: changeItemToScrollTo) + } else { + + // avoids weird bouncing when scrolling up if we choose the index path below + let indexAfterIndexOfOtherMoveCell = indexOfOtherMoveCell + 1 + let indexToScrollTo = moveDirection == .down ? indexOfOtherMoveCell : ((dataSource.count) > indexAfterIndexOfOtherMoveCell) ? indexAfterIndexOfOtherMoveCell : indexOfOtherMoveCell + let indexPathToScrollTo = IndexPath(item: indexToScrollTo, section: 0) + + // first scroll to cell, scrollViewDidEndAnimation will then scroll to item + scrollDidFinishInfo = (indexPathOfOtherMoveCell, changeItemToScrollTo) + collectionView.scrollToItem(at: indexPathToScrollTo, at: UICollectionView.ScrollPosition.top, animated: true) + } + } + } + + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffNetworkModels.swift b/Apps/Wikipedia/Wikipedia/Code/DiffNetworkModels.swift new file mode 100644 index 0000000..fd60463 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffNetworkModels.swift @@ -0,0 +1,85 @@ +import Foundation + +struct DiffSection: Codable { + let level: Int + let heading: String + let offset: Int +} + +struct DiffItemOffset: Codable { + let from: Int? + let to: Int? +} + +struct DiffSideMetaData: Codable { + let sections: [DiffSection] +} + +struct DiffResponse: Codable { + let diff: [DiffItem] + let from: DiffSideMetaData + let to: DiffSideMetaData +} + +enum DiffItemType: Int, Codable { + case context + case addLine + case deleteLine + case change + case moveSource + case moveDestination +} + +enum DiffHighlightRangeType: Int, Codable { + case add + case delete +} + +enum DiffLinkDirection: Int, Codable { + case down + case up +} + +struct DiffHighlightRange: Codable { + let start: Int + let length: Int + let type: DiffHighlightRangeType +} + +struct DiffItem: Codable { + let type: DiffItemType + let text: String + let highlightRanges: [DiffHighlightRange]? + let moveInfo: DiffMoveInfo? + let offset: DiffItemOffset + let lineNumber: Int? +} + +struct DiffMoveInfo: Codable { + let id: String + let linkId: String + let linkDirection: DiffLinkDirection +} + +extension DiffItem: Equatable { + static func == (lhs: DiffItem, rhs: DiffItem) -> Bool { + return lhs.lineNumber == rhs.lineNumber && + lhs.type == rhs.type && + lhs.text == rhs.text && + lhs.highlightRanges == rhs.highlightRanges && + lhs.moveInfo == rhs.moveInfo && + lhs.offset == rhs.offset + } +} + +extension DiffMoveInfo: Equatable { + +} + +extension DiffItemOffset: Equatable { + +} + +extension DiffHighlightRange: Equatable { + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffResponse.json b/Apps/Wikipedia/Wikipedia/Code/DiffResponse.json new file mode 100644 index 0000000..fe7eb44 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffResponse.json @@ -0,0 +1,343 @@ +{ + "diff": [{ + "type": 3, + "lineNumber": 1, + "text": "Intro text here (changed)", + "offset": { + "from": 0, + "to": 0 + }, + "highlightRanges": [{ + "start": 15, + "length": 10, + "type": 0 + }] + }, { + "type": 0, + "lineNumber": 2, + "text": "", + "offset": { + "from": 16, + "to": 26 + } + }, { + "type": 0, + "lineNumber": 3, + "text": "== Section 1 ==", + "offset": { + "from": 17, + "to": 27 + } + }, { + "type": 0, + "lineNumber": 9, + "text": "", + "offset": { + "from": 56, + "to": 66 + } + }, { + "type": 0, + "lineNumber": 10, + "text": "== Section 2 ==", + "offset": { + "from": 57, + "to": 67 + } + }, { + "type": 1, + "lineNumber": 11, + "text": "", + "offset": { + "from": null, + "to": 83 + } + }, { + "type": 5, + "lineNumber": 12, + "moveInfo": { + "id": "movedpara_2_1_rhs", + "linkId": "movedpara_5_1_lhs", + "linkDirection": 0 + }, + "text": "Line F", + "offset": { + "from": null, + "to": 84 + }, + "highlightRanges": [] + }, { + "type": 0, + "lineNumber": 13, + "text": "", + "offset": { + "from": 73, + "to": 91 + } + }, { + "type": 0, + "lineNumber": 14, + "text": "Line D", + "offset": { + "from": 74, + "to": 92 + } + }, { + "type": 0, + "lineNumber": 15, + "text": "", + "offset": { + "from": 81, + "to": 99 + } + }, { + "type": 3, + "lineNumber": 16, + "text": "Line E 日", + "offset": { + "from": 82, + "to": 100 + }, + "highlightRanges": [{ + "start": 6, + "length": 4, + "type": 0 + }] + }, { + "type": 2, + "text": "", + "offset": { + "from": 89, + "to": null + } + }, { + "type": 4, + "moveInfo": { + "id": "movedpara_5_1_lhs", + "linkId": "movedpara_2_1_rhs", + "linkDirection": 1 + }, + "text": "Line F", + "offset": { + "from": 90, + "to": null + } + }, { + "type": 0, + "lineNumber": 17, + "text": "", + "offset": { + "from": 97, + "to": 111 + } + }, { + "type": 0, + "lineNumber": 18, + "text": "=== Subsection 3===", + "offset": { + "from": 98, + "to": 112 + } + }, { + "type": 0, + "lineNumber": 20, + "text": "Line G", + "offset": { + "from": 119, + "to": 133 + } + }, { + "type": 0, + "lineNumber": 21, + "text": "", + "offset": { + "from": 126, + "to": 140 + } + }, { + "type": 3, + "lineNumber": 22, + "text": "Line H 一", + "offset": { + "from": 127, + "to": 141 + }, + "highlightRanges": [{ + "start": 6, + "length": 4, + "type": 0 + }] + }, { + "type": 2, + "text": "", + "offset": { + "from": 134, + "to": null + } + }, { + "type": 4, + "moveInfo": { + "id": "movedpara_8_1_lhs", + "linkId": "movedpara_10_1_rhs", + "linkDirection": 0 + }, + "text": "Line I", + "offset": { + "from": 135, + "to": null + } + }, { + "type": 0, + "lineNumber": 23, + "text": "", + "offset": { + "from": 142, + "to": 152 + } + }, { + "type": 0, + "lineNumber": 24, + "text": "Line J", + "offset": { + "from": 143, + "to": 153 + } + }, { + "type": 0, + "lineNumber": 35, + "text": "", + "offset": { + "from": 206, + "to": 216 + } + }, { + "type": 0, + "lineNumber": 36, + "text": "Line N", + "offset": { + "from": 207, + "to": 217 + } + }, { + "type": 1, + "lineNumber": 37, + "text": "", + "offset": { + "from": null, + "to": 224 + } + }, { + "type": 5, + "lineNumber": 38, + "moveInfo": { + "id": "movedpara_10_1_rhs", + "linkId": "movedpara_8_1_lhs", + "linkDirection": 1 + }, + "text": "Line I", + "offset": { + "from": null, + "to": 225 + }, + "highlightRanges": [] + }, { + "type": 0, + "lineNumber": 39, + "text": "", + "offset": { + "from": 214, + "to": 232 + } + }, { + "type": 0, + "lineNumber": 40, + "text": "Line O", + "offset": { + "from": 215, + "to": 233 + } + }, { + "type": 0, + "lineNumber": 41, + "text": "", + "offset": { + "from": 222, + "to": 240 + } + }, { + "type": 3, + "lineNumber": 42, + "text": "Line P 月", + "offset": { + "from": 223, + "to": 241 + }, + "highlightRanges": [{ + "start": 6, + "length": 4, + "type": 0 + }] + }, { + "type": 0, + "lineNumber": 43, + "text": "", + "offset": { + "from": 230, + "to": 252 + } + }, { + "type": 0, + "lineNumber": 44, + "text": "Line Q", + "offset": { + "from": 231, + "to": 253 + } + }], + "from": { + "sections": [{ + "heading": "== Section 1 ==", + "level": 2, + "offset": 17 + }, { + "heading": "== Section 2 ==", + "level": 2, + "offset": 57 + }, { + "heading": "=== Subsection 3===", + "level": 3, + "offset": 98 + }, { + "heading": "==Section 4==", + "level": 2, + "offset": 151 + }, { + "heading": "==Section 5==", + "level": 2, + "offset": 182 + }] + }, + "to": { + "sections": [{ + "heading": "== Section 1 ==", + "level": 2, + "offset": 27 + }, { + "heading": "== Section 2 ==", + "level": 2, + "offset": 67 + }, { + "heading": "=== Subsection 3===", + "level": 3, + "offset": 112 + }, { + "heading": "==Section 4==", + "level": 2, + "offset": 161 + }, { + "heading": "==Section 5==", + "level": 2, + "offset": 192 + }] + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffRevisionTransition.swift b/Apps/Wikipedia/Wikipedia/Code/DiffRevisionTransition.swift new file mode 100644 index 0000000..0d1cd95 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffRevisionTransition.swift @@ -0,0 +1,71 @@ +import Foundation + +protocol DiffRevisionAnimating: AnyObject { + var embeddedViewController: UIViewController? { get } + var animateDirection: DiffRevisionTransition.Direction? { get set } +} + +class DiffRevisionTransition : NSObject, UIViewControllerAnimatedTransitioning { + + static let duration = TimeInterval(0.3) + + enum Direction { + case up + case down + } + + let direction: Direction + + init(direction: Direction) { + self.direction = direction + } + + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + + let container = transitionContext.containerView + + guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from), + let toView = transitionContext.view(forKey: UITransitionContextViewKey.to), + let toVC = (transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as? DiffRevisionAnimating) else { + + transitionContext.completeTransition(false) + return + } + + let fromEmbedVC = (transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as? DiffRevisionAnimating)?.embeddedViewController + toVC.animateDirection = direction + + toView.alpha = 0 + container.addSubview(toView) + toView.frame = fromView.frame + + var oldFromEmbedVCFrame: CGRect? + var newFromEmbedVCFrame: CGRect? + + if let fromFrame = fromEmbedVC?.view.frame { + + let newY = direction == .down ? fromFrame.minY - fromFrame.height : fromFrame.maxY + newFromEmbedVCFrame = CGRect(x: fromFrame.minX, y: newY, width: fromFrame.width, height: fromFrame.height) + + oldFromEmbedVCFrame = fromFrame + } + + UIView.animate(withDuration: DiffRevisionTransition.duration, delay: 0.0, options: .curveEaseInOut, animations: { + toView.alpha = 1 + + if let newFromEmbedVCFrame = newFromEmbedVCFrame { + fromEmbedVC?.view.frame = newFromEmbedVCFrame + } + }) { (finished) in + if let oldFromEmbedVCFrame = oldFromEmbedVCFrame { + fromEmbedVC?.view.frame = oldFromEmbedVCFrame + } + + transitionContext.completeTransition(!transitionContext.transitionWasCancelled) + } + } + + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + return DiffRevisionTransition.duration + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffThanker.swift b/Apps/Wikipedia/Wikipedia/Code/DiffThanker.swift new file mode 100644 index 0000000..0869f29 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffThanker.swift @@ -0,0 +1,52 @@ +enum DiffThankerError: LocalizedError { + case thanksStatusNotSuccess + case thanksError(String) + + var errorDescription: String? { + switch self { + case .thanksStatusNotSuccess: + return "Thanks did not succeed" + case .thanksError(let message): + return message + } + } +} + +struct DiffThankerResult { + var recipient: String + init(recipient:String) { + self.recipient = recipient + } +} + +class DiffThanker: Fetcher { + func thank(siteURL: URL, rev: Int, completion: @escaping ((Result) -> Void)) { + let parameters = [ + "action": "thank", + "rev": String(rev), + "source": "diff", + "errorsuselocal": "1", + "format": "json" + ] + performTokenizedMediaWikiAPIPOST(to: siteURL, with: parameters) { (result, response, error) in + if let error = error { + completion(.failure(error)) + return + } + if let error = result?["error"] as? [String: Any], let info = error["info"] as? String { + completion(.failure(DiffThankerError.thanksError(info))) + return + } + guard + let resultDict = result?["result"] as? [String: Any], + let successInt = resultDict["success"] as? Int, + successInt == 1, + let recipient = resultDict["recipient"] as? String + else { + completion(.failure(DiffThankerError.thanksStatusNotSuccess)) + return + } + completion(.success(DiffThankerResult.init(recipient: recipient))) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffToolbarView.swift b/Apps/Wikipedia/Wikipedia/Code/DiffToolbarView.swift new file mode 100644 index 0000000..1bf9fe5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffToolbarView.swift @@ -0,0 +1,185 @@ +import UIKit + +protocol DiffToolbarViewDelegate: AnyObject { + func tappedPrevious() + func tappedNext() + func tappedShare(_ sender: UIBarButtonItem) + func tappedThankButton() + var isLoggedIn: Bool { get } +} + +class DiffToolbarView: UIView { + + var parentViewState: DiffContainerViewModel.State? { + didSet { + apply(theme: theme) + } + } + + private var theme: Theme = .standard + + @IBOutlet private var toolbar: UIToolbar! + @IBOutlet var contentView: UIView! + lazy var previousButton: IconBarButtonItem = { + let item = IconBarButtonItem(iconName: "chevron-down", target: self, action: #selector(tappedPrevious(_:)), for: .touchUpInside) + item.accessibilityLabel = WMFLocalizedString("action-previous-revision-accessibility", value: "Previous Revision", comment: "Accessibility title for the 'Previous Revision' action button when viewing a single revision diff.") + item.customView?.widthAnchor.constraint(equalToConstant: 38).isActive = true + return item + }() + + lazy var nextButton: IconBarButtonItem = { + let item = IconBarButtonItem(iconName: "chevron-up", target: self, action: #selector(tappedNext(_:)), for: .touchUpInside) + item.accessibilityLabel = WMFLocalizedString("action-next-revision-accessibility", value: "Next Revision", comment: "Accessibility title for the 'Next Revision' action button when viewing a single revision diff.") + item.customView?.widthAnchor.constraint(equalToConstant: 38).isActive = true + return item + }() + + lazy var shareButton: IconBarButtonItem = { + let item = IconBarButtonItem(iconName: "share", target: self, action: #selector(tappedShare(_:)), for: .touchUpInside) + item.accessibilityLabel = CommonStrings.accessibilityShareTitle + + return item + }() + + lazy var thankButton: IconBarButtonItem = { + let item = IconBarButtonItem(iconName: "diff-smile", target: self, action: #selector(tappedThank(_:)), for: .touchUpInside , iconInsets: UIEdgeInsets(top: 5.0, left: 0, bottom: -5.0, right: 0)) + item.accessibilityLabel = WMFLocalizedString("action-thank-user-accessibility", value: "Thank User", comment: "Accessibility title for the 'Thank User' action button when viewing a single revision diff.") + + return item + }() + + weak var delegate: DiffToolbarViewDelegate? + var isThankSelected = false { + didSet { + + let imageName = isThankSelected ? "diff-smile-filled" : "diff-smile" + if let button = thankButton.customView as? UIButton { + button.setImage(UIImage(named: imageName), for: .normal) + } + } + } + + var toolbarHeight: CGFloat { + return toolbar.frame.height + } + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + func commonInit() { + Bundle.main.loadNibNamed(DiffToolbarView.wmf_nibName(), owner: self, options: nil) + addSubview(contentView) + contentView.frame = self.bounds + contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + + setItems() + } + + @objc func tappedPrevious(_ sender: UIBarButtonItem) { + delegate?.tappedPrevious() + } + + @objc func tappedNext(_ sender: UIBarButtonItem) { + delegate?.tappedNext() + } + + @objc func tappedShare(_ sender: UIBarButtonItem) { + delegate?.tappedShare(shareButton) + } + + @objc func tappedThank(_ sender: UIBarButtonItem) { + delegate?.tappedThankButton() + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + setItems() + } + + private func setItems() { + let trailingMarginSpacing = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) + switch (traitCollection.horizontalSizeClass, traitCollection.verticalSizeClass) { + case (.regular, .regular): + trailingMarginSpacing.width = 58 + default: + trailingMarginSpacing.width = 24 + } + + let leadingMarginSpacing = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) + switch (traitCollection.horizontalSizeClass, traitCollection.verticalSizeClass) { + case (.regular, .regular): + leadingMarginSpacing.width = 42 + default: + leadingMarginSpacing.width = 0 + } + + let largeFixedSize = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) + largeFixedSize.width = 30 + + let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + + toolbar.items = [leadingMarginSpacing, nextButton, previousButton, spacer, thankButton, largeFixedSize, shareButton, trailingMarginSpacing] + } + + func setPreviousButtonState(isEnabled: Bool) { + previousButton.isEnabled = isEnabled + } + + func setNextButtonState(isEnabled: Bool) { + nextButton.isEnabled = isEnabled + } + + func setThankButtonState(isEnabled: Bool) { + thankButton.isEnabled = isEnabled + } + + func setShareButtonState(isEnabled: Bool) { + shareButton.isEnabled = isEnabled + } +} + +extension DiffToolbarView: Themeable { + func apply(theme: Theme) { + + self.theme = theme + + toolbar.isTranslucent = false + + toolbar.backgroundColor = theme.colors.chromeBackground + toolbar.barTintColor = theme.colors.chromeBackground + contentView.backgroundColor = theme.colors.chromeBackground + + // avoid toolbar disappearing when empty/error states are shown + if theme == Theme.black { + switch parentViewState { + case .error, .empty: + toolbar.backgroundColor = theme.colors.paperBackground + toolbar.barTintColor = theme.colors.paperBackground + contentView.backgroundColor = theme.colors.paperBackground + default: + break + } + } + + previousButton.apply(theme: theme) + nextButton.apply(theme: theme) + shareButton.apply(theme: theme) + thankButton.apply(theme: theme) + + if let delegate = delegate, + !delegate.isLoggedIn { + if let button = thankButton.customView as? UIButton { + button.tintColor = theme.colors.disabledLink + } + } + shareButton.tintColor = theme.colors.link + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffToolbarView.xib b/Apps/Wikipedia/Wikipedia/Code/DiffToolbarView.xib new file mode 100644 index 0000000..484a339 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffToolbarView.xib @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/DiffTransformer.swift b/Apps/Wikipedia/Wikipedia/Code/DiffTransformer.swift new file mode 100644 index 0000000..1c9aa57 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DiffTransformer.swift @@ -0,0 +1,423 @@ +import Foundation + +struct TransformDiffItem { + let type: DiffItemType + let text: String + let highlightRanges: [DiffHighlightRange]? + let offset: DiffItemOffset + var sectionTitle: String? + let lineNumber: Int? + var moveInfo: TransformMoveInfo? +} + +struct TransformMoveInfo { + let id: String + let linkId: String + let linkDirection: DiffLinkDirection + var groupedIndex: Int? + var moveDistance: TransformMoveDistance? +} + +enum TransformMoveDistance { + case line(amount: Int) + case section(amount: Int) +} + +struct TransformSectionInfo { + + struct Side { + let title: String + let order: Int + } + + let from: Side? + let to: Side? + let fromIsIntro: Bool + let toIsIntro: Bool +} + +enum DiffTransformerError: Error { + case failureTransformingNetworkModels + case failureParsingFirstRevisionWikitext +} + +// takes a DiffResponse and turns it into [DiffListGroupViewModel] +class DiffTransformer { + + let type: DiffContainerViewModel.DiffType + let siteURL: URL + lazy var semanticContentAttribute: UISemanticContentAttribute = { + let contentLanguageCode = siteURL.wmf_contentLanguageCode + return MWKLanguageLinkController.semanticContentAttribute(forContentLanguageCode: contentLanguageCode) + }() + + init(type: DiffContainerViewModel.DiffType, siteURL: URL) { + self.type = type + self.siteURL = siteURL + } + + func firstRevisionViewModels(from wikitext: String, theme: Theme, traitCollection: UITraitCollection) throws -> [DiffListGroupViewModel] { + + let lines = wikitext.split { $0.isNewline } + + var items: [DiffListChangeItemViewModel] = [] + for text in lines { + let item = DiffListChangeItemViewModel(firstRevisionText: String(text), traitCollection: traitCollection, theme: theme, semanticContentAttribute: semanticContentAttribute) + items.append(item) + } + + if !wikitext.isEmpty && items.isEmpty { + throw DiffTransformerError.failureParsingFirstRevisionWikitext + } + + return [DiffListChangeViewModel(type: .singleRevison, items: items, theme: theme, width: 0, traitCollection: traitCollection, semanticContentAttribute: semanticContentAttribute)] + } + + func viewModels(from response: DiffResponse, theme: Theme, traitCollection: UITraitCollection) throws -> [DiffListGroupViewModel] { + + let groupedMoveIndexes = self.groupedIndexesOfMoveItems(from: response) + let transformSectionInfo = self.transformSectionInfosOfItems(from: response) + let transformDiffItems = self.transformDiffItemsWithPopulatedLineNumbers(from: response) + + guard let populatedTransformDiffItems = self.populateAdditionalSectionAndMoveInfo(transformSectionInfo: transformSectionInfo, transformDiffItems: transformDiffItems, groupedMoveIndexes: groupedMoveIndexes) else { + + throw DiffTransformerError.failureTransformingNetworkModels + } + + switch self.type { + case .single: + let viewModels: [DiffListGroupViewModel] = self.viewModelsForSingle(from: populatedTransformDiffItems, theme: theme, traitCollection: traitCollection) + + return viewModels + case .compare: + let viewModels: [DiffListGroupViewModel] = self.viewModelsForCompare(from: populatedTransformDiffItems, theme: theme, traitCollection: traitCollection) + return viewModels + } + } + + private func populateAdditionalSectionAndMoveInfo(transformSectionInfo: [TransformSectionInfo], transformDiffItems: [TransformDiffItem], groupedMoveIndexes: [String: Int]) -> [TransformDiffItem]? { + + guard transformDiffItems.count == transformSectionInfo.count, + transformDiffItems.count == transformDiffItems.count else { + assertionFailure("Expecting section info count to equal number of diff items") + return nil + } + + var newItems: [TransformDiffItem] = [] + let zipped = zip(transformDiffItems, transformSectionInfo) + + var correspondingMoveItems: [String: (linkItem: TransformDiffItem, linkSectionInfo: TransformSectionInfo)] = [:] + for zippedItem in zipped { + guard zippedItem.0.type == .moveDestination || + zippedItem.0.type == .moveSource else { + continue + } + + if let linkId = zippedItem.0.moveInfo?.linkId { + correspondingMoveItems[linkId] = (zippedItem.0, zippedItem.1) + } + } + + for var zippedItem in zipped { + + var isToIntro = zippedItem.1.toIsIntro + var isFromIntro = zippedItem.1.fromIsIntro + + zippedItem.0.sectionTitle = zippedItem.1.to?.title ?? zippedItem.1.from?.title + + if let moveInfo = zippedItem.0.moveInfo { + + let groupedIndex = groupedMoveIndexes[moveInfo.id] + + var moveDistance: TransformMoveDistance? = nil + + if let correspondingMoveItem = correspondingMoveItems[moveInfo.id] { + + let fromSectionTitle = zippedItem.0.type == .moveSource ? zippedItem.1.from?.title : correspondingMoveItem.linkSectionInfo.from?.title + let toSectionTitle = zippedItem.0.type == .moveSource ? correspondingMoveItem.linkSectionInfo.to?.title : zippedItem.1.to?.title + let fromSectionOrder = zippedItem.0.type == .moveSource ? zippedItem.1.from?.order : correspondingMoveItem.linkSectionInfo.from?.order + let toSectionOrder = zippedItem.0.type == .moveSource ? correspondingMoveItem.linkSectionInfo.to?.order : zippedItem.1.to?.order + isToIntro = zippedItem.0.type == .moveSource ? correspondingMoveItem.linkSectionInfo.toIsIntro : zippedItem.1.toIsIntro + isFromIntro = zippedItem.0.type == .moveSource ? zippedItem.1.fromIsIntro : correspondingMoveItem.linkSectionInfo.fromIsIntro + + if let fromSectionTitle = fromSectionTitle, + let toSectionTitle = toSectionTitle, + let fromSectionOrder = fromSectionOrder, + let toSectionOrder = toSectionOrder { + + switch (fromSectionTitle == toSectionTitle, fromSectionOrder == toSectionOrder) { + case (false, false): + moveDistance = .section(amount: abs(fromSectionOrder - toSectionOrder)) + default: + break + } + } + + if moveDistance == nil { + // fallback to line numbers + if let firstLineNumber = zippedItem.0.lineNumber, + let nextLineNumber = correspondingMoveItem.linkItem.lineNumber { + moveDistance = .line(amount: abs(firstLineNumber - nextLineNumber)) + } + } + } + + let transformMoveInfo = TransformMoveInfo(id: moveInfo.id, linkId: moveInfo.linkId, linkDirection: moveInfo.linkDirection, groupedIndex: groupedIndex, moveDistance: moveDistance) + zippedItem.0.moveInfo = transformMoveInfo + } + + if zippedItem.0.sectionTitle == nil { + if isToIntro && isFromIntro { + zippedItem.0.sectionTitle = WMFLocalizedString("diff-single-intro-title", value:"Intro", comment:"Section heading on revision changes diff screen that indicates the following highlighted changes occurred in the intro section.") + } + } + + newItems.append(zippedItem.0) + } + + return newItems + } + + private func transformSectionInfosOfItems(from response: DiffResponse) -> [TransformSectionInfo] { + + var result: [TransformSectionInfo] = [] + + var fromSections = response.from.sections + var toSections = response.to.sections + + let firstFrom = fromSections.first + let firstTo = toSections.first + + var lastFrom: DiffSection? = nil + var lastTo: DiffSection? = nil + + var lastFromIndex = -1 + var lastToIndex = -1 + + var currentFrom = fromSections.first + var currentTo = toSections.first + + var fromIsIntro = false + var toIsIntro = false + for item in response.diff { + + // from side + var fromSide: TransformSectionInfo.Side? + + if let itemFromOffset = item.offset.from { + while currentFrom != nil && + currentFrom!.offset <= itemFromOffset { + + lastFrom = fromSections.removeFirst() + lastFromIndex = lastFromIndex + 1 + currentFrom = fromSections.first + } + + if let lastFrom = lastFrom { + fromSide = TransformSectionInfo.Side(title: lastFrom.heading, order: lastFromIndex) + } + + if let firstFromOffset = firstFrom?.offset, + fromSide == nil { + fromIsIntro = itemFromOffset < firstFromOffset + } + } + + + // to side + var toSide: TransformSectionInfo.Side? + + if let itemToOffset = item.offset.to { + while currentTo != nil && + currentTo!.offset <= itemToOffset { + + lastTo = toSections.removeFirst() + lastToIndex = lastToIndex + 1 + currentTo = toSections.first + } + + if let lastTo = lastTo { + toSide = TransformSectionInfo.Side(title: lastTo.heading, order: lastToIndex) + } + + if let firstToOffset = firstTo?.offset, + toSide == nil { + toIsIntro = itemToOffset < firstToOffset + } + } + + result.append(TransformSectionInfo(from: fromSide, to: toSide, fromIsIntro: fromIsIntro, toIsIntro: toIsIntro)) + } + + return result + } + + private func groupedIndexesOfMoveItems(from response: DiffResponse) -> [String: Int] { + let movedItems = response.diff.filter { $0.type == .moveSource || $0.type == .moveDestination } + + var indexCounter = 0 + var result: [String: Int] = [:] + + for item in movedItems { + + if let id = item.moveInfo?.id, + let linkId = item.moveInfo?.linkId { + + if result[id] == nil { + if let existingIndex = result[linkId] { + result[id] = existingIndex + } else { + result[id] = indexCounter + indexCounter += 1 + } + } + } + } + + return result + } + + private func transformDiffItemsWithPopulatedLineNumbers(from response: DiffResponse) -> [TransformDiffItem] { + + var items: [TransformDiffItem] = [] + + var lastLineNumber: Int? + for item in response.diff { + + var transformMoveInfo: TransformMoveInfo? + if let moveInfo = item.moveInfo { + transformMoveInfo = TransformMoveInfo(id: moveInfo.id, linkId: moveInfo.linkId, linkDirection: moveInfo.linkDirection, groupedIndex: nil, moveDistance: nil) + } + + let transformDiffItem: TransformDiffItem + + if let lineNumber = item.lineNumber { + lastLineNumber = lineNumber + transformDiffItem = TransformDiffItem(type: item.type, text: item.text, highlightRanges: item.highlightRanges, offset: item.offset, sectionTitle: nil, lineNumber: lineNumber, moveInfo: transformMoveInfo) + } else { + transformDiffItem = TransformDiffItem(type: item.type, text: item.text, highlightRanges: item.highlightRanges, offset: item.offset, sectionTitle: nil, lineNumber: lastLineNumber, moveInfo: transformMoveInfo) + } + + items.append(transformDiffItem) + } + + return items + } + + private func viewModelsForSingle(from transformDiffItems: [TransformDiffItem], theme: Theme, traitCollection: UITraitCollection) -> [DiffListGroupViewModel] { + + var result: [DiffListGroupViewModel] = [] + + var sectionItems: [TransformDiffItem] = [] + var lastItem: TransformDiffItem? + + let packageUpSectionItemsIfNeeded = { + + if sectionItems.count > 0 { + // package contexts up into change view model, append to result + + let changeViewModel = DiffListChangeViewModel(type: .singleRevison, diffItems: sectionItems, theme: theme, width: 0, traitCollection: traitCollection, semanticContentAttribute: self.semanticContentAttribute) + + result.append(changeViewModel) + sectionItems.removeAll() + } + + } + + for item in transformDiffItems { + + + if item.type == .context { + + continue + + } else { + + if item.sectionTitle != lastItem?.sectionTitle { + packageUpSectionItemsIfNeeded() + } + + sectionItems.append(item) + } + + lastItem = item + + continue + } + + packageUpSectionItemsIfNeeded() + + return result + } + + private func viewModelsForCompare(from transformDiffItems: [TransformDiffItem], theme: Theme, traitCollection: UITraitCollection) -> [DiffListGroupViewModel] { + + var result: [DiffListGroupViewModel] = [] + + var contextItems: [TransformDiffItem] = [] + var changeItems: [TransformDiffItem] = [] + var lastItem: TransformDiffItem? + + let packageUpContextItemsIfNeeded = { + + if contextItems.count > 0 { + // package contexts up into context view model, append to result + let contextViewModel = DiffListContextViewModel(diffItems: contextItems, isExpanded: false, theme: theme, width: 0, traitCollection: traitCollection, semanticContentAttribute: self.semanticContentAttribute) + result.append(contextViewModel) + contextItems.removeAll() + } + } + + let packageUpChangeItemsIfNeeded = { + + if changeItems.count > 0 { + // package contexts up into change view model, append to result + + let changeViewModel = DiffListChangeViewModel(type: .compareRevision, diffItems: changeItems, theme: theme, width: 0, traitCollection: traitCollection, semanticContentAttribute: self.semanticContentAttribute) + + result.append(changeViewModel) + changeItems.removeAll() + } + + } + + for item in transformDiffItems { + + if let lastItemLineNumber = lastItem?.lineNumber, + let currentItemLineNumber = item.lineNumber { + let delta = currentItemLineNumber - lastItemLineNumber + if delta > 1 { + + packageUpContextItemsIfNeeded() + packageUpChangeItemsIfNeeded() + + // insert unedited lines view model + let uneditedViewModel = DiffListUneditedViewModel(numberOfUneditedLines: delta, theme: theme, width: 0, traitCollection: traitCollection) + result.append(uneditedViewModel) + } + } + + if item.type == .context { + + packageUpChangeItemsIfNeeded() + + contextItems.append(item) + + } else { + + packageUpContextItemsIfNeeded() + + changeItems.append(item) + } + + lastItem = item + + continue + } + + packageUpContextItemsIfNeeded() + packageUpChangeItemsIfNeeded() + + return result + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DisambiguationPagesViewController.swift b/Apps/Wikipedia/Wikipedia/Code/DisambiguationPagesViewController.swift new file mode 100644 index 0000000..e88a5f3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DisambiguationPagesViewController.swift @@ -0,0 +1,70 @@ +import UIKit +import WMF + +@objc(WMFDisambiguationPagesViewController) +class DisambiguationPagesViewController: ArticleFetchedResultsViewController { + + let siteURL: URL + let articleURLs: [URL] + @objc var resultLimit: Int = 10 + + @objc(initWithURLs:siteURL:dataStore:theme:) + required init(with URLs: [URL], siteURL: URL, dataStore: MWKDataStore, theme: Theme) { + self.siteURL = siteURL + self.articleURLs = URLs + super.init() + self.dataStore = dataStore + self.theme = theme + self.title = WMFLocalizedString("page-similar-titles", value: "Similar pages", comment: "Label for button that shows a list of similar titles (disambiguation) for the current page") + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func setupFetchedResultsController(with dataStore: MWKDataStore) { + let request = WMFArticle.fetchRequest() + request.predicate = NSPredicate(format: "key IN %@", articleURLs.compactMap { $0.wmf_databaseKey }) + request.sortDescriptors = [NSSortDescriptor(key: "key", ascending: true)] + fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: dataStore.viewContext, sectionNameKeyPath: nil, cacheName: nil) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + fetch() + } + + lazy var fakeProgressController: FakeProgressController = { + return FakeProgressController(progress: navigationBar, delegate: navigationBar) + }() + + func fetch() { + fakeProgressController.start() + let articleKeys = articleURLs.compactMap { $0.wmf_inMemoryKey } + self.dataStore.articleSummaryController.updateOrCreateArticleSummariesForArticles(withKeys: articleKeys) { (_, error) in + self.fakeProgressController.finish() + if let error = error { + self.wmf_showAlertWithError(error as NSError) + return + } + } + } + + override func configure(cell: ArticleRightAlignedImageCollectionViewCell, forItemAt indexPath: IndexPath, layoutOnly: Bool) { + super.configure(cell: cell, forItemAt: indexPath, layoutOnly: layoutOnly) + cell.topSeparator.isHidden = indexPath.item != 0 + cell.bottomSeparator.isHidden = false + } + + override func canDelete(at indexPath: IndexPath) -> Bool { + return false + } + + override var eventLoggingLabel: EventLoggingLabel? { + return .similarPage + } + + override var eventLoggingCategory: EventLoggingCategory { + return .article + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DisappearingCallbackNavigationController.swift b/Apps/Wikipedia/Wikipedia/Code/DisappearingCallbackNavigationController.swift new file mode 100644 index 0000000..107d330 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DisappearingCallbackNavigationController.swift @@ -0,0 +1,12 @@ +import UIKit + +class DisappearingCallbackNavigationController: WMFThemeableNavigationController { + + var willDisappearCallback: (() -> Void)? + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + willDisappearCallback?() + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/DisclosureButton.swift b/Apps/Wikipedia/Wikipedia/Code/DisclosureButton.swift new file mode 100644 index 0000000..10c284d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/DisclosureButton.swift @@ -0,0 +1,31 @@ +import Foundation +import SwiftUI + +struct DisclosureButton: View { + + let item: Element + let action: (Element) -> Void + + @EnvironmentObject var observableTheme: ObservableTheme + + var body: some View { + Button(action: { action(item) }) { + VStack(spacing: 0) { + HStack { + Text(item.description) + .foregroundColor(Color(observableTheme.theme.colors.primaryText)) + .font(.callout) + .fontWeight(.semibold) + Spacer(minLength: 12) + Image(systemName: "chevron.right").font(Font.system(.footnote).weight(.semibold)) + .foregroundColor(Color(observableTheme.theme.colors.secondaryText)) + } + .padding(EdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16)) + Divider() + .frame(height: 1) + .background(Color(observableTheme.theme.colors.midBackground)) + } + } + .buttonStyle(BackgroundHighlightingButtonStyle()) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EchoSubscriptionFetcher.swift b/Apps/Wikipedia/Wikipedia/Code/EchoSubscriptionFetcher.swift new file mode 100644 index 0000000..0e72f4f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EchoSubscriptionFetcher.swift @@ -0,0 +1,99 @@ +import Foundation + +@objc(WMFEchoSubscriptionFetcher) +class EchoSubscriptionFetcher: Fetcher { + + @objc func subscribe(siteURL: URL?, deviceToken: NSData?, completion: @escaping (Error?) -> Void) { + guard let bundleID = Bundle.main.bundleIdentifier, + let siteURL = siteURL, + let deviceToken = deviceToken else { + completion(RequestError.invalidParameters) + return + } + + let deviceTokenString = deviceTokenStringFromDeviceToken(deviceToken) + let bodyParameters: [String: String] = [ + "action": "echopushsubscriptions", + "format": "json", + "command": "create", + "provider": "apns", + "providertoken": deviceTokenString, + "topic": bundleID + ] + + self.performTokenizedMediaWikiAPIPOST(to: siteURL, with: bodyParameters) { result, response, error in + guard error == nil else { + completion(error) + return + } + + guard let response = response, + response.statusCode == 200 else { + completion(RequestError.unexpectedResponse) + return + } + + if let responseError = RequestError.from(result) { + switch responseError { + case .api("echo-push-token-exists"): + // MediaWiki has already registered the requested device token, which is what we ultimately want, so we can consider this request a success + break + default: + completion(responseError) + return + } + } + + completion(nil) + } + } + + @objc func unsubscribe(siteURL: URL?, deviceToken: NSData?, completion: ((Error?) -> Void)?) { + + guard let siteURL = siteURL, + let deviceToken = deviceToken else { + completion?(RequestError.invalidParameters) + return + } + + let deviceTokenString = deviceTokenStringFromDeviceToken(deviceToken) + let bodyParameters: [String: String] = [ + "action": "echopushsubscriptions", + "format": "json", + "command": "delete", + "providertoken": deviceTokenString + ] + + self.performTokenizedMediaWikiAPIPOST(to: siteURL, with: bodyParameters) { result, response, error in + guard error == nil else { + completion?(error) + return + } + + guard let response = response, + response.statusCode == 200 else { + completion?(RequestError.unexpectedResponse) + return + } + + if let responseError = RequestError.from(result) { + switch responseError { + case .api("echo-push-token-not-found"): + // MediaWiki can't find this registered token, which is what we ultimately want, so we can consider this request a success + break + default: + completion?(responseError) + return + } + } + + completion?(nil) + } + } + + private func deviceTokenStringFromDeviceToken(_ deviceToken: NSData) -> String { + let tokenComponents = deviceToken.map { data in String(format: "%02.2hhx", data) } + let deviceTokenString = tokenComponents.joined() + return deviceTokenString + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditFunnel.swift b/Apps/Wikipedia/Wikipedia/Code/EditFunnel.swift new file mode 100644 index 0000000..65c404a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditFunnel.swift @@ -0,0 +1,227 @@ +@objc public enum EditFunnelSource: Int { + case titleDescription + case pencil + case highlight + case unknown + + var stringValue: String? { + switch self { + case .titleDescription: + return "title_description" + case .pencil: + return "pencil" + case .highlight: + return "highlight" + case .unknown: + return nil + } + } +} + +// https://meta.wikimedia.org/wiki/Schema:MobileWikiAppEdit +@objc final class EditFunnel: EventLoggingFunnel, EventLoggingStandardEventProviding { + @objc public static let shared = EditFunnel() + + var sessionToken: String? + + private override init() { + super.init(schema: "MobileWikiAppEdit", version: 19082700) + } + + enum Action: String { + case start + case preview + case saved + case captchaShown + case captchaFailure + case abuseFilterWarning + case abuseFilterError + case editSummaryTap + case editSummaryShown + case abuseFilterWarningIgnore + case abuseFilterWarningBack + case saveAttempt + case error + case ready + case onboarding + } + + private enum WikidataDescriptionEdit: String { + case new + case existing + + init(isAddingNewTitleDescription: Bool) { + self = isAddingNewTitleDescription ? .new : .existing + } + } + + private func event(action: Action, source: EditFunnelSource? = nil, sessionToken: String? = nil, wikidataDescriptionEdit: WikidataDescriptionEdit? = nil, editSummaryType: EditSummaryViewCannedButtonType? = nil, abuseFilterName: String? = nil, errorText: String? = nil, revision: UInt64? = nil) -> [String: Any] { + var event: [String : Any] = ["action": action.rawValue] + + if let source = source, let stringValue = source.stringValue { + event["source"] = stringValue + } + if let revision = revision { + event["revID"] = revision + } + if let editSummaryType = editSummaryType { + event["editSummaryTapped"] = editSummaryType.eventLoggingKey + } + if let abuseFilterName = abuseFilterName { + event["abuseFilterName"] = abuseFilterName + } + if let sessionToken = sessionToken { + event["session_token"] = sessionToken + } + if let wikidataDescriptionEdit = wikidataDescriptionEdit { + event["wikidataDescriptionEdit"] = wikidataDescriptionEdit.rawValue + } + if let errorText = errorText { + event["errorText"] = errorText + } + + return event + } + + override func preprocessData(_ eventData: [AnyHashable : Any]) -> [AnyHashable : Any] { + guard + let sessionID = sessionID, + let appInstallID = appInstallID + else { + assertionFailure("Missing required properties (sessionID or appInstallID); event won't be logged") + return eventData + } + guard let event = eventData as? [String: Any] else { + assertionFailure("Expected dictionary with keys of type String") + return eventData + } + + let requiredData: [String: Any] = ["session_token": sessionID, "anon": isAnon, "app_install_id": appInstallID, "client_dt": timestamp] + + return requiredData.merging(event, uniquingKeysWith: { (first, _) in first }) + } + + // MARK: Start + + @objc(logSectionEditingStartFromSource:language:) + public func logSectionEditingStart(from source: EditFunnelSource, language: String) { + logStart(source: source, language: language) + } + + @objc(logTitleDescriptionEditingStartFromSource:language:) + public func logTitleDescriptionEditingStart(from source: EditFunnelSource, language: String) { + let wikidataDescriptionEdit: WikidataDescriptionEdit + if source == .titleDescription { + wikidataDescriptionEdit = .new + } else { + wikidataDescriptionEdit = .existing + } + logStart(source: source, wikidataDescriptionEdit: wikidataDescriptionEdit, language: language) + } + + private func logStart(source: EditFunnelSource, wikidataDescriptionEdit: WikidataDescriptionEdit? = nil, language: String) { + // session token should be regenerated at every 'start' event + sessionToken = singleUseUUID() + log(event(action: .start, source: source, sessionToken: sessionToken, wikidataDescriptionEdit: wikidataDescriptionEdit), language: language) + } + + // MARK: Onboarding + + @objc(logOnboardingPresentationInitiatedBySource:language:) + public func logOnboardingPresentation(initiatedBy source: EditFunnelSource, language: String) { + logOnboardingPresentation(source: source, language: language) + } + + private func logOnboardingPresentation(source: EditFunnelSource, language: String) { + log(event(action: .onboarding, source: source), language: language) + } + + // MARK: Ready + + @objc(logTitleDescriptionReadyToEditFromSource:isAddingNewTitleDescription:language:) + public func logTitleDescriptionReadyToEditFrom(from source: EditFunnelSource, isAddingNewTitleDescription: Bool, language: String) { + log(event(action: .ready, source: source, wikidataDescriptionEdit: WikidataDescriptionEdit(isAddingNewTitleDescription: isAddingNewTitleDescription)), language: language) + } + + public func logSectionReadyToEdit(from source: EditFunnelSource, language: String?) { + log(event(action: .ready, source: source), language: language) + } + + // MARK: Preview + + public func logEditPreviewForArticle(from source: EditFunnelSource, language: String?) { + log(event(action: .preview, source: source), language: language) + } + + // MARK: Save attempt + + public func logTitleDescriptionSaveAttempt(source: EditFunnelSource, isAddingNewTitleDescription: Bool, language: String?) { + log(event(action: .saveAttempt, source: source, wikidataDescriptionEdit: WikidataDescriptionEdit(isAddingNewTitleDescription: isAddingNewTitleDescription)), language: language) + } + + public func logSectionSaveAttempt(source: EditFunnelSource, language: String?) { + log(event(action: .saveAttempt, source: source), language: language) + } + + // MARK: Saved + + public func logTitleDescriptionSaved(source: EditFunnelSource, isAddingNewTitleDescription: Bool, language: String?) { + log(event(action: .saved, source: source, wikidataDescriptionEdit: WikidataDescriptionEdit(isAddingNewTitleDescription: isAddingNewTitleDescription)), language: language) + } + + public func logSectionSaved(source: EditFunnelSource, revision: UInt64?, language: String?) { + log(event(action: .saved, source: source, revision: revision), language: language) + } + + // MARK: Error + + public func logTitleDescriptionSaveError(source: EditFunnelSource, isAddingNewTitleDescription: Bool, language: String?, errorText: String) { + log(event(action: .error, source: source, wikidataDescriptionEdit: WikidataDescriptionEdit(isAddingNewTitleDescription: isAddingNewTitleDescription), errorText: errorText), language: language) + } + + public func logSectionSaveError(source: EditFunnelSource, language: String?, errorText: String) { + log(event(action: .error, source: source, errorText: errorText), language: language) + } + + public func logSectionHighlightToEditError(language: String?) { + log(event(action: .error, source: .highlight, errorText: "non-editable"), language: language) + } + + // MARK: Section edit summary + + public func logSectionEditSummaryTap(source: EditFunnelSource, editSummaryType: EditSummaryViewCannedButtonType, language: String?) { + log(event(action: .editSummaryTap, source: source, editSummaryType: editSummaryType), language: language) + } + + public func logSectionEditSummaryShown(source: EditFunnelSource, language: String?) { + log(event(action: .editSummaryShown, source: source), language: language) + } + + // MARK: Captcha + + public func logCaptchaShownForSectionEdit(source: EditFunnelSource, language: String?) { + log(event(action: .captchaShown, source: source), language: language) + } + + public func logCaptchaFailedForSectionEdit(source: EditFunnelSource, language: String?) { + log(event(action: .captchaFailure, source: source), language: language) + } + + // MARK: Abuse filter + + public func logAbuseFilterWarningForSectionEdit(abuseFilterName: String, source: EditFunnelSource, language: String?) { + log(event(action: .abuseFilterWarning, source: source, abuseFilterName: abuseFilterName), language: language) + } + + public func logAbuseFilterWarningBackForSectionEdit(abuseFilterName: String, source: EditFunnelSource, language: String?) { + log(event(action: .abuseFilterWarningBack, source: source, abuseFilterName: abuseFilterName), language: language) + } + + public func logAbuseFilterWarningIgnoreForSectionEdit(abuseFilterName: String, source: EditFunnelSource, language: String?) { + log(event(action: .abuseFilterWarningIgnore, source: source, abuseFilterName: abuseFilterName), language: language) + } + + public func logAbuseFilterErrorForSectionEdit(abuseFilterName: String, source: EditFunnelSource, language: String?) { + log(event(action: .abuseFilterError, source: source, abuseFilterName: abuseFilterName), language: language) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditHintController.swift b/Apps/Wikipedia/Wikipedia/Code/EditHintController.swift new file mode 100644 index 0000000..9c8e1f4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditHintController.swift @@ -0,0 +1,12 @@ +@objc(WMFEditHintController) +class EditHintController: HintController { + @objc init() { + let editHintViewController = EditHintViewController() + super.init(hintViewController: editHintViewController) + } + + override func toggle(presenter: HintPresentingViewController, context: HintController.Context?, theme: Theme) { + super.toggle(presenter: presenter, context: context, theme: theme) + setHintHidden(false) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditHintViewController.swift b/Apps/Wikipedia/Wikipedia/Code/EditHintViewController.swift new file mode 100644 index 0000000..dc86619 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditHintViewController.swift @@ -0,0 +1,7 @@ +@objc(WMFEditHintViewController) +class EditHintViewController: HintViewController { + override func configureSubviews() { + defaultImageView.image = UIImage(named: "published-pencil") + defaultLabel.text = "Your edit was successfully published" + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditHistoryCompareFunnel.swift b/Apps/Wikipedia/Wikipedia/Code/EditHistoryCompareFunnel.swift new file mode 100644 index 0000000..ca3768f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditHistoryCompareFunnel.swift @@ -0,0 +1,81 @@ +import Foundation + +final class EditHistoryCompareFunnel: EventLoggingFunnel, EventLoggingStandardEventProviding { + private enum Action: String, Codable { + case showHistory = "show_history" + case revisionView = "revision_view" + case compare1 + case compare2 + case thankTry = "thank_try" + case thankSuccess = "thank_success" + case thankFail = "thank_fail" + } + + + private struct Event: EventInterface { + static let schema: EventPlatformClient.Schema = .editHistoryCompare + let action: Action + let primary_language: String + let is_anon: Bool + } + + public static let shared = EditHistoryCompareFunnel() + + private override init() { + super.init(schema: "MobileWikiAppiOSEditHistoryCompare", version: 19795952) + } + + private func event(action: Action) -> [String: Any] { + let event: [String: Any] = ["action": action.rawValue, "primary_language": primaryLanguage(), "is_anon": isAnon] + return event + } + + override func preprocessData(_ eventData: [AnyHashable: Any]) -> [AnyHashable: Any] { + return wholeEvent(with: eventData) + } + + private func newLog(action: Action, domain: String?) { + let event = Event(action: action, primary_language: primaryLanguage(), is_anon: isAnon.boolValue) + EventPlatformClient.shared.submit(stream: .editHistoryCompare, event: event, domain: domain) + } + + public func logShowHistory(articleURL: URL) { + log(event(action: .showHistory), language: articleURL.wmf_languageCode) + newLog(action: .showHistory, domain: articleURL.wmf_site?.host) + } + + /** + * Log a revision view event. + * - Parameter url: either a `siteURL` (when logging from `PageHistoryViewController`) + * or a `pageURL` (when logging from `DiffContainerViewController`) + */ + public func logRevisionView(url: URL) { + log(event(action: .revisionView), language: url.wmf_languageCode) + newLog(action: .revisionView, domain: url.wmf_site?.host) + } + + public func logCompare1(articleURL: URL) { + log(event(action: .compare1), language: articleURL.wmf_languageCode) + newLog(action: .compare1, domain: articleURL.wmf_site?.host) + } + + public func logCompare2(articleURL: URL) { + log(event(action: .compare2), language: articleURL.wmf_languageCode) + newLog(action: .compare2, domain: articleURL.wmf_site?.host) + } + + public func logThankTry(siteURL: URL) { + log(event(action: .thankTry), language: siteURL.wmf_languageCode) + newLog(action: .thankTry, domain: siteURL.host) + } + + public func logThankSuccess(siteURL: URL) { + log(event(action: .thankSuccess), language: siteURL.wmf_languageCode) + newLog(action: .thankSuccess, domain: siteURL.host) + } + + public func logThankFail(siteURL: URL) { + log(event(action: .thankFail), language: siteURL.wmf_languageCode) + newLog(action: .thankFail, domain: siteURL.host) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditLinkViewController.swift b/Apps/Wikipedia/Wikipedia/Code/EditLinkViewController.swift new file mode 100644 index 0000000..cb6206d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditLinkViewController.swift @@ -0,0 +1,212 @@ +import UIKit + +protocol EditLinkViewControllerDelegate: AnyObject { + func editLinkViewController(_ editLinkViewController: EditLinkViewController, didTapCloseButton button: UIBarButtonItem) + func editLinkViewController(_ editLinkViewController: EditLinkViewController, didFinishEditingLink displayText: String?, linkTarget: String) + func editLinkViewController(_ editLinkViewController: EditLinkViewController, didFailToExtractArticleTitleFromArticleURL articleURL: URL) + func editLinkViewControllerDidRemoveLink(_ editLinkViewController: EditLinkViewController) +} + +class EditLinkViewController: ViewController { + weak var delegate: EditLinkViewControllerDelegate? + + private let link: Link + private let siteURL: URL + private var articleURL: URL + + private let articleCell = ArticleRightAlignedImageCollectionViewCell() + private let dataStore: MWKDataStore + + private var navigationBarVisibleHeightObservation: NSKeyValueObservation? + + @IBOutlet private weak var contentView: UIView! + @IBOutlet private weak var scrollViewTopConstraint: NSLayoutConstraint! + @IBOutlet private weak var displayTextLabel: UILabel! + @IBOutlet private weak var displayTextView: UITextView! + @IBOutlet private weak var displayTextViewHeightConstraint: NSLayoutConstraint! + @IBOutlet private weak var linkTargetLabel: UILabel! + @IBOutlet private weak var linkTargetContainerView: UIView! + @IBOutlet private weak var linkTargetContainerViewHeightConstraint: NSLayoutConstraint! + @IBOutlet private weak var activityIndicatorView: UIActivityIndicatorView! + @IBOutlet private weak var removeLinkButton: AutoLayoutSafeMultiLineButton! + @IBOutlet private var separatorViews: [UIView] = [] + + private lazy var closeButton: UIBarButtonItem = { + let closeButton = UIBarButtonItem.wmf_buttonType(.X, target: self, action: #selector(close(_:))) + closeButton.accessibilityLabel = CommonStrings.closeButtonAccessibilityLabel + return closeButton + }() + + private lazy var doneButton = UIBarButtonItem(title: CommonStrings.doneTitle, style: .done, target: self, action: #selector(finishEditing(_:))) + + init?(link: Link, siteURL: URL?, dataStore: MWKDataStore) { + guard + let siteURL = siteURL ?? MWKDataStore.shared().primarySiteURL ?? NSURL.wmf_URLWithDefaultSiteAndCurrentLocale(), + let articleURL = link.articleURL(for: siteURL) + else { + return nil + } + self.link = link + self.siteURL = siteURL + self.articleURL = articleURL + self.dataStore = dataStore + super.init(nibName: "EditLinkViewController", bundle: nil) + } + + deinit { + navigationBarVisibleHeightObservation?.invalidate() + navigationBarVisibleHeightObservation = nil + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + navigationBar.displayType = .modal + title = CommonStrings.editLinkTitle + navigationItem.leftBarButtonItem = closeButton + navigationItem.rightBarButtonItem = doneButton + navigationItem.backBarButtonItem = UIBarButtonItem(title: CommonStrings.accessibilityBackTitle, style: .plain, target: nil, action: nil) + var textContainerInset = displayTextView.textContainerInset + textContainerInset.top = 15 + displayTextLabel.text = WMFLocalizedString("edit-link-display-text-title", value: "Display text", comment: "Title for the display text label") + displayTextView.textContainerInset = textContainerInset + displayTextView.textContainer.lineFragmentPadding = 0 + displayTextView.text = link.label ?? link.page + linkTargetLabel.text = WMFLocalizedString("edit-link-link-target-title", value: "Link target", comment: "Title for the link target label") + removeLinkButton.setTitle(WMFLocalizedString("edit-link-remove-link-title", value: "Remove link", comment: "Title for the remove link button"), for: .normal) + articleCell.isHidden = true + linkTargetContainerView.addSubview(articleCell) + navigationBarVisibleHeightObservation = navigationBar.observe(\.visibleHeight, options: [.new, .initial], changeHandler: { [weak self] (observation, change) in + guard let self = self else { + return + } + self.scrollViewTopConstraint.constant = self.navigationBar.visibleHeight + }) + updateFonts() + apply(theme: theme) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + fetchArticle() + } + + private func fetchArticle() { + guard let article = dataStore.fetchArticle(with: articleURL) else { + guard let key = articleURL.wmf_inMemoryKey else { + return + } + dataStore.articleSummaryController.updateOrCreateArticleSummaryForArticle(withKey: key) { (article, _) in + guard let article = article else { + return + } + self.updateView(with: article) + } + return + } + updateView(with: article) + } + + private func updateView(with article: WMFArticle) { + articleCell.configure(article: article, displayType: .compactList, index: 0, theme: theme, layoutOnly: false) + articleCell.topSeparator.isHidden = true + articleCell.bottomSeparator.isHidden = true + articleCell.extractLabel?.numberOfLines = 5 + updateLinkTargetContainer() + articleCell.isHidden = false + + activityIndicatorView.stopAnimating() + view.setNeedsLayout() + } + + private func updateLinkTargetContainer() { + articleCell.frame = CGRect(origin: linkTargetContainerView.bounds.origin, size: articleCell.sizeThatFits(CGSize(width: linkTargetContainerView.bounds.width, height: UIView.noIntrinsicMetric), apply: true)) + linkTargetContainerViewHeightConstraint.constant = articleCell.frame.height + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + displayTextLabel.font = UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + linkTargetLabel.font = UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + displayTextView.font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection) + removeLinkButton.titleLabel?.font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection) + } + + @objc private func close(_ sender: UIBarButtonItem) { + delegate?.editLinkViewController(self, didTapCloseButton: sender) + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + displayTextViewHeightConstraint.constant = displayTextView.sizeThatFits(CGSize(width: displayTextView.bounds.width, height: UIView.noIntrinsicMetric)).height + updateLinkTargetContainer() + } + + @objc private func finishEditing(_ sender: UIBarButtonItem) { + let displayText = displayTextView.text + guard let linkTarget = articleURL.wmf_title else { + assertionFailure("Failed to extract article title from url: \(articleURL)") + delegate?.editLinkViewController(self, didFailToExtractArticleTitleFromArticleURL: articleURL) + return + } + delegate?.editLinkViewController(self, didFinishEditingLink: displayText, linkTarget: linkTarget) + } + + @IBAction private func removeLink(_ sender: UIButton) { + delegate?.editLinkViewControllerDidRemoveLink(self) + } + + @IBAction private func searchArticles(_ sender: UITapGestureRecognizer) { + let searchViewController = SearchViewController() + searchViewController.shouldSetTitleViewWhenRecentSearchesAreDisabled = false + searchViewController.shouldAdjustNavigationBarInsetHidingOnAppearance = false + searchViewController.siteURL = siteURL + searchViewController.shouldSetSearchVisible = false + searchViewController.shouldBecomeFirstResponder = true + searchViewController.displayType = .backVisible + searchViewController.areRecentSearchesEnabled = true + searchViewController.dataStore = MWKDataStore.shared() + searchViewController.shouldShowCancelButton = false + searchViewController.delegate = self + searchViewController.delegatesSelection = true + searchViewController.showLanguageBar = false + searchViewController.navigationItem.title = title + searchViewController.searchTerm = articleURL.wmf_title + searchViewController.search() + searchViewController.apply(theme: theme) + navigationController?.pushViewController(searchViewController, animated: true) + } + + override func apply(theme: Theme) { + super.apply(theme: theme) + self.theme = theme + guard viewIfLoaded != nil else { + return + } + contentView.backgroundColor = theme.colors.paperBackground + view.backgroundColor = theme.colors.baseBackground + separatorViews.forEach { $0.backgroundColor = theme.colors.border } + displayTextLabel.textColor = theme.colors.secondaryText + linkTargetLabel.textColor = theme.colors.secondaryText + removeLinkButton.tintColor = theme.colors.destructive + removeLinkButton.backgroundColor = theme.colors.paperBackground + closeButton.tintColor = theme.colors.primaryText + doneButton.tintColor = theme.colors.link + displayTextView.textColor = theme.colors.primaryText + activityIndicatorView.color = theme.isDark ? .white : .gray + } +} + +extension EditLinkViewController: ArticleCollectionViewControllerDelegate { + func articleCollectionViewController(_ articleCollectionViewController: ArticleCollectionViewController, didSelectArticleWith articleURL: URL, at indexPath: IndexPath) { + self.articleURL = articleURL + navigationController?.popViewController(animated: true) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditLinkViewController.xib b/Apps/Wikipedia/Wikipedia/Code/EditLinkViewController.xib new file mode 100644 index 0000000..52b3a8f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditLinkViewController.xib @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/EditNoticesFetcher.swift b/Apps/Wikipedia/Wikipedia/Code/EditNoticesFetcher.swift new file mode 100644 index 0000000..7a05284 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditNoticesFetcher.swift @@ -0,0 +1,56 @@ +class EditNoticesFetcher: Fetcher { + + // MARK: - Nested Types + + struct Notice: Codable { + let name: String + let description: String + } + + private struct Response: Codable { + struct VisualEditor: Codable { + let notices: [String: String]? + } + + enum CodingKeys: String, CodingKey { + case visualEditor = "visualeditor" + } + + let visualEditor: VisualEditor? + } + + // MARK: - Public + + func fetchNotices(for articleURL: URL, completion: @escaping (Result<[Notice], Error>) -> Void) { + guard let title = articleURL.wmf_title else { + completion(.failure(RequestError.invalidParameters)) + return + } + + let parameters: [String: Any] = [ + "action": "visualeditor", + "paction": "metadata", + "page": title, + "errorsuselocal": "1", + "formatversion" : "2", + "format": "json" + ] + + performDecodableMediaWikiAPIGET(for: articleURL, with: parameters) { (result: Result) in + switch result { + case .failure(let error): + completion(.failure(error)) + case .success(let response): + var notices: [Notice] = [] + if let rawNotices = response.visualEditor?.notices?.filter({ $0.key.contains("editnotice")}) { + for rawNotice in rawNotices { + notices.append(Notice(name: rawNotice.key, description: rawNotice.value)) + } + } + + completion(.success(notices)) + } + } + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditNoticesView.swift b/Apps/Wikipedia/Wikipedia/Code/EditNoticesView.swift new file mode 100644 index 0000000..3bbfc85 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditNoticesView.swift @@ -0,0 +1,264 @@ +import UIKit + +final class EditNoticesView: SetupView { + + // MARK: - UI Elements + + lazy var scrollView: UIScrollView = { + let scrollView = UIScrollView() + scrollView.translatesAutoresizingMaskIntoConstraints = false + scrollView.showsVerticalScrollIndicator = false + return scrollView + }() + + lazy var stackView: UIStackView = { + let stackView = UIStackView() + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.axis = .vertical + stackView.spacing = 0 + stackView.isUserInteractionEnabled = true + return stackView + }() + + lazy var editNoticesImageContainer: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(editNoticesImageView) + NSLayoutConstraint.activate([ + editNoticesImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 10), + editNoticesImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10), + editNoticesImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + editNoticesImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor) + ]) + return view + }() + + lazy var editNoticesImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(systemName: "exclamationmark.circle.fill")) + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.contentMode = .scaleAspectFill + + let imageWidthConstraint = imageView.widthAnchor.constraint(equalToConstant: 50) + imageWidthConstraint.priority = .required + let imageHeightConstraint = imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor) + imageHeightConstraint.priority = .required + + NSLayoutConstraint.activate([ + imageWidthConstraint, + imageHeightConstraint + ]) + + return imageView + }() + + lazy var editNoticesTitle: UILabel = { + let label = UILabel() + label.text = CommonStrings.editNotices + label.textAlignment = .center + label.numberOfLines = 0 + label.font = UIFont.wmf_scaledSystemFont(forTextStyle: .title1, weight: .semibold, size: 20) + label.adjustsFontForContentSizeCategory = true + return label + }() + + lazy var editNoticesSubtitle: UILabel = { + let label = UILabel() + label.text = WMFLocalizedString("edit-notices-please-read", value: "Please read before editing", comment: "Subtitle displayed in edit notices view.") + label.textAlignment = .center + label.numberOfLines = 0 + label.font = UIFont.wmf_scaledSystemFont(forTextStyle: .title2, weight: .semibold, size: 15) + label.adjustsFontForContentSizeCategory = true + return label + }() + + lazy var contentContainer: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + lazy var doneContainer: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + lazy var doneButton: UIButton = { + let button = UIButton(type: .system) + button.titleLabel?.font = UIFont.wmf_scaledSystemFont(forTextStyle: .body, weight: .medium, size: 17) + button.titleLabel?.adjustsFontForContentSizeCategory = true + button.setTitle(CommonStrings.doneTitle, for: .normal) + button.translatesAutoresizingMaskIntoConstraints = false + return button + }() + + lazy var footerContainer: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + lazy var footerStack: UIStackView = { + let stackView = UIStackView() + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.axis = .horizontal + stackView.distribution = .fill + stackView.spacing = 20 + return stackView + }() + + lazy var footerSwitchLabel: UILabel = { + let label = UILabel() + label.text = WMFLocalizedString("edit-notices-always-display", value: "Always display edit notices", comment: "Title for toggle switch label in edit notices view.") + label.numberOfLines = 0 + label.font = UIFont.wmf_scaledSystemFont(forTextStyle: .body, weight: .regular, size: 15) + label.adjustsFontForContentSizeCategory = true + label.translatesAutoresizingMaskIntoConstraints = false + + label.setContentCompressionResistancePriority(.required, for: .vertical) + label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + + return label + }() + + lazy var switchContainer: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + + view.addSubview(toggleSwitch) + NSLayoutConstraint.activate([ + toggleSwitch.centerYAnchor.constraint(equalTo: view.centerYAnchor), + toggleSwitch.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor), + toggleSwitch.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor), + toggleSwitch.leadingAnchor.constraint(equalTo: view.leadingAnchor), + toggleSwitch.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ]) + + return view + }() + + lazy var toggleSwitch: UISwitch = { + let toggle = UISwitch() + toggle.translatesAutoresizingMaskIntoConstraints = false + toggle.setContentHuggingPriority(.required, for: .vertical) + toggle.setContentCompressionResistancePriority(.required, for: .vertical) + return toggle + }() + + lazy var textView: UITextView = { + let textView = UITextView() + textView.translatesAutoresizingMaskIntoConstraints = false + textView.isScrollEnabled = false + textView.isEditable = false + textView.adjustsFontForContentSizeCategory = true + textView.textContainerInset = .zero + textView.textContainer.lineFragmentPadding = 0 + return textView + }() + + // MARK: - Private Properties + + private var doneButtonTrailingConstraint: NSLayoutConstraint! + + private var doneButtonTrailingMargin: CGFloat { + return traitCollection.verticalSizeClass == .compact ? -20 : -8 + } + + // MARK: - Override + + override func setup() { + // Top "navigation" bar + + addSubview(doneContainer) + doneContainer.addSubview(doneButton) + doneButtonTrailingConstraint = doneButton.trailingAnchor.constraint(equalTo: doneContainer.readableContentGuide.trailingAnchor, constant: doneButtonTrailingMargin) + + // Primary content container, scrollable + + addSubview(contentContainer) + contentContainer.addSubview(scrollView) + scrollView.addSubview(stackView) + + stackView.addArrangedSubview(editNoticesImageContainer) + stackView.addArrangedSubview(VerticalSpacerView.spacerWith(space: 10)) + stackView.addArrangedSubview(editNoticesTitle) + stackView.addArrangedSubview(VerticalSpacerView.spacerWith(space: 6)) + stackView.addArrangedSubview(editNoticesSubtitle) + stackView.addArrangedSubview(VerticalSpacerView.spacerWith(space: 32)) + stackView.addArrangedSubview(textView) + + // Footer label/switch + + addSubview(footerContainer) + footerContainer.addSubview(footerStack) + + footerStack.addArrangedSubview(footerSwitchLabel) + footerStack.addArrangedSubview(switchContainer) + + NSLayoutConstraint.activate([ + doneContainer.topAnchor.constraint(equalTo: topAnchor), + doneContainer.leadingAnchor.constraint(equalTo: leadingAnchor), + doneContainer.trailingAnchor.constraint(equalTo: trailingAnchor), + doneContainer.bottomAnchor.constraint(equalTo: contentContainer.topAnchor), + + doneButtonTrailingConstraint, + doneButton.topAnchor.constraint(equalTo: doneContainer.topAnchor, constant: 16), + doneButton.bottomAnchor.constraint(equalTo: doneContainer.bottomAnchor, constant: -5), + + contentContainer.leadingAnchor.constraint(equalTo: leadingAnchor), + contentContainer.trailingAnchor.constraint(equalTo: trailingAnchor), + + scrollView.topAnchor.constraint(equalTo: contentContainer.topAnchor), + scrollView.bottomAnchor.constraint(equalTo: contentContainer.bottomAnchor), + scrollView.leadingAnchor.constraint(equalTo: contentContainer.readableContentGuide.leadingAnchor, constant: 24), + scrollView.trailingAnchor.constraint(equalTo: contentContainer.readableContentGuide.trailingAnchor, constant: -24), + + stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor), + stackView.topAnchor.constraint(equalTo: scrollView.topAnchor), + stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), + stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), + stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), + + footerContainer.topAnchor.constraint(equalTo: contentContainer.bottomAnchor), + footerContainer.leadingAnchor.constraint(equalTo: leadingAnchor), + footerContainer.trailingAnchor.constraint(equalTo: trailingAnchor), + footerContainer.bottomAnchor.constraint(equalTo: bottomAnchor), + + footerStack.leadingAnchor.constraint(equalTo: footerContainer.readableContentGuide.leadingAnchor, constant: 20), + footerStack.trailingAnchor.constraint(equalTo: footerContainer.readableContentGuide.trailingAnchor, constant: -20), + footerStack.topAnchor.constraint(equalTo: footerContainer.topAnchor, constant: 16), + footerStack.bottomAnchor.constraint(equalTo: footerContainer.safeAreaLayoutGuide.bottomAnchor, constant: -16) + ]) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + doneButtonTrailingConstraint.constant = doneButtonTrailingMargin + doneContainer.setNeedsLayout() + } + + // MARK: - Public + + func configure(viewModel: EditNoticesViewModel, theme: Theme) { + let attributedNoticeString = NSMutableAttributedString() + for notice in viewModel.notices { + let noticeString = notice.description.byAttributingHTML(with: .subheadline, matching: traitCollection, color: theme.colors.primaryText, handlingLinks: true) + attributedNoticeString.append(noticeString) + } + + textView.attributedText = attributedNoticeString.removingInitialNewlineCharacters().removingRepetitiveNewlineCharacters() + textView.textAlignment = viewModel.semanticContentAttribute == .forceRightToLeft ? .right : .left + + // Update colors + backgroundColor = theme.colors.paperBackground + doneButton.setTitleColor(theme.colors.link, for: .normal) + editNoticesImageView.tintColor = theme.colors.primaryText + editNoticesTitle.textColor = theme.colors.primaryText + editNoticesSubtitle.textColor = theme.colors.primaryText + textView.backgroundColor = theme.colors.paperBackground + textView.linkTextAttributes = [.foregroundColor: theme.colors.link] + footerSwitchLabel.textColor = theme.colors.primaryText + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditNoticesViewController.swift b/Apps/Wikipedia/Wikipedia/Code/EditNoticesViewController.swift new file mode 100644 index 0000000..a7cff5a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditNoticesViewController.swift @@ -0,0 +1,75 @@ +import UIKit + +protocol EditNoticesViewControllerDelegate: AnyObject { + func editNoticesControllerUserTapped(url: URL) +} + +class EditNoticesViewController: ThemeableViewController, RMessageSuppressing { + + // MARK: - Properties + + let viewModel: EditNoticesViewModel + + weak var delegate: EditNoticesViewControllerDelegate? + + var editNoticesView: EditNoticesView { + return view as! EditNoticesView + } + + // MARK: - Lifecycle + + init(theme: Theme, viewModel: EditNoticesViewModel) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + self.theme = theme + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + let editNoticesView = EditNoticesView(frame: UIScreen.main.bounds) + view = editNoticesView + editNoticesView.configure(viewModel: viewModel, theme: theme) + + editNoticesView.doneButton.addTarget(self, action: #selector(dismissView), for: .primaryActionTriggered) + editNoticesView.toggleSwitch.addTarget(self, action: #selector(didToggleSwitch(_:)), for: .valueChanged) + editNoticesView.toggleSwitch.isOn = UserDefaults.standard.wmf_alwaysDisplayEditNotices + editNoticesView.textView.delegate = self + } + + // MARK: - Actions + + @objc private func dismissView() { + dismiss(animated: true) + } + + @objc private func didToggleSwitch(_ sender: UISwitch) { + UserDefaults.standard.wmf_alwaysDisplayEditNotices = sender.isOn + } + + // MARK: - Themeable + + override func apply(theme: Theme) { + super.apply(theme: theme) + editNoticesView.configure(viewModel: viewModel, theme: theme) + } + +} + +extension EditNoticesViewController: UITextViewDelegate { + + func textView(_ textView: UITextView, shouldInteractWith url: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { + guard let url = URL(string: url.absoluteString, relativeTo: viewModel.siteURL) else { + return false + } + + dismiss(animated: true) { + self.delegate?.editNoticesControllerUserTapped(url: url) + } + + return false + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditNoticesViewModel.swift b/Apps/Wikipedia/Wikipedia/Code/EditNoticesViewModel.swift new file mode 100644 index 0000000..1904278 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditNoticesViewModel.swift @@ -0,0 +1,21 @@ +import Foundation + +final class EditNoticesViewModel { + + // MARK: - Properties + + var siteURL: URL + var notices: [EditNoticesFetcher.Notice] + + // MARK: - Public + + init(siteURL: URL, notices: [EditNoticesFetcher.Notice]) { + self.siteURL = siteURL + self.notices = notices + } + + var semanticContentAttribute: UISemanticContentAttribute { + return MWKLanguageLinkController.semanticContentAttribute(forContentLanguageCode: siteURL.wmf_contentLanguageCode) + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditPreviewInternalLinkViewController.swift b/Apps/Wikipedia/Wikipedia/Code/EditPreviewInternalLinkViewController.swift new file mode 100644 index 0000000..5202e15 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditPreviewInternalLinkViewController.swift @@ -0,0 +1,73 @@ +import UIKit + +class EditPreviewInternalLinkViewController: UIViewController { + @IBOutlet private weak var containerView: UIView! + private var containerViewHeightConstraint: NSLayoutConstraint? + @IBOutlet private weak var button: UIButton! + @IBOutlet private weak var tapView: UIView! + @IBOutlet private weak var tapGestureRecignizer: UITapGestureRecognizer! + + private let articleURL: URL + private let dataStore: MWKDataStore + private var theme = Theme.standard + + init(articleURL: URL, dataStore: MWKDataStore) { + self.articleURL = articleURL + self.dataStore = dataStore + super.init(nibName: "EditPreviewInternalLinkViewController", bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + button.titleLabel?.font = UIFont.wmf_font(.title3, compatibleWithTraitCollection: traitCollection) + } + + override func viewDidLoad() { + super.viewDidLoad() + button.layer.cornerRadius = 8 + button.setTitle(CommonStrings.okTitle, for: .normal) + wmf_addPeekableChildViewController(for: articleURL, dataStore: dataStore, theme: theme, containerView: containerView) + tapGestureRecignizer.delegate = self + tapGestureRecignizer.addTarget(self, action: #selector(dismissAnimated(_:))) + updateFonts() + apply(theme: theme) + } + + override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) { + if let containerViewHeightConstraint = containerViewHeightConstraint { + containerViewHeightConstraint.constant = container.preferredContentSize.height + } else { + containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: container.preferredContentSize.height) + containerViewHeightConstraint?.isActive = true + } + } + + @IBAction private func dismissAnimated(_ sender: UIButton) { + dismiss(animated: true) + } +} + +extension EditPreviewInternalLinkViewController: UIGestureRecognizerDelegate { + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + return touch.view == tapView + } +} + +extension EditPreviewInternalLinkViewController: Themeable { + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + button.backgroundColor = theme.colors.midBackground + button.tintColor = theme.colors.link + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditPreviewInternalLinkViewController.xib b/Apps/Wikipedia/Wikipedia/Code/EditPreviewInternalLinkViewController.xib new file mode 100644 index 0000000..13399fb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditPreviewInternalLinkViewController.xib @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/EditPreviewViewController.swift b/Apps/Wikipedia/Wikipedia/Code/EditPreviewViewController.swift new file mode 100644 index 0000000..0b44e1b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditPreviewViewController.swift @@ -0,0 +1,303 @@ +import UIKit +import WMF + +protocol EditPreviewViewControllerDelegate: NSObjectProtocol { + func editPreviewViewControllerDidTapNext(_ editPreviewViewController: EditPreviewViewController) +} + +class EditPreviewViewController: ViewController, WMFPreviewAnchorTapAlertDelegate, InternalLinkPreviewing { + var sectionID: Int? + var articleURL: URL + var languageCode: String? + var wikitext = "" + var editFunnel: EditFunnel? + var loggedEditActions: NSMutableSet? + var editFunnelSource: EditFunnelSource = .unknown + var savedPagesFunnel: SavedPagesFunnel? + + weak var delegate: EditPreviewViewControllerDelegate? + + lazy var messagingController: ArticleWebMessagingController = { + let controller = ArticleWebMessagingController() + controller.delegate = self + return controller + }() + + lazy var fetcher = ArticleFetcher() + + private let previewWebViewContainer: PreviewWebViewContainer + + var scrollToAnchorCompletions: [ScrollToAnchorCompletion] = [] + var scrollViewAnimationCompletions: [() -> Void] = [] + + lazy var referenceWebViewBackgroundTapGestureRecognizer: UITapGestureRecognizer = { + let tapGR = UITapGestureRecognizer(target: self, action: #selector(tappedWebViewBackground)) + tapGR.delegate = self + webView.scrollView.addGestureRecognizer(tapGR) + tapGR.isEnabled = false + return tapGR + }() + + init(articleURL: URL) { + self.articleURL = articleURL + self.previewWebViewContainer = PreviewWebViewContainer() + super.init() + + webView.scrollView.delegate = self + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func previewWebViewContainer(_ previewWebViewContainer: PreviewWebViewContainer, didTapLink url: URL) { + let isExternal = url.host != articleURL.host + if isExternal { + showExternalLinkInAlert(link: url.absoluteString) + } else { + showInternalLink(url: url) + } + } + + func showExternalLinkInAlert(link: String) { + let title = WMFLocalizedString("wikitext-preview-link-external-preview-title", value: "External link", comment: "Title for external link preview popup") + let message = String(format: WMFLocalizedString("wikitext-preview-link-external-preview-description", value: "This link leads to an external website: %1$@", comment: "Description for external link preview popup. $1$@ is the external url."), link) + let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: CommonStrings.okTitle, style: .default, handler: nil)) + present(alertController, animated: true) + } + + func showInternalLinkInAlert(link: String) { + let title = WMFLocalizedString("wikitext-preview-link-preview-title", value: "Link preview", comment: "Title for link preview popup") + let message = String(format: WMFLocalizedString("wikitext-preview-link-preview-description", value: "This link leads to '%1$@'", comment: "Description of the link URL. %1$@ is the URL."), link) + let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: CommonStrings.okTitle, style: .default, handler: nil)) + present(alertController, animated: true) + } + + @objc func goBack() { + navigationController?.popViewController(animated: true) + } + + @objc func goForward() { + delegate?.editPreviewViewControllerDidTapNext(self) + } + + override func viewDidLoad() { + super.viewDidLoad() + + view.addSubview(previewWebViewContainer) + view.wmf_addConstraintsToEdgesOfView(previewWebViewContainer) + previewWebViewContainer.previewAnchorTapAlertDelegate = self + + navigationItem.title = WMFLocalizedString("navbar-title-mode-edit-wikitext-preview", value: "Preview", comment: "Header text shown when wikitext changes are being previewed. {{Identical|Preview}}") + + navigationItem.leftBarButtonItem = UIBarButtonItem.wmf_buttonType(.caretLeft, target: self, action: #selector(self.goBack)) + + navigationItem.rightBarButtonItem = UIBarButtonItem(title: CommonStrings.nextTitle, style: .done, target: self, action: #selector(self.goForward)) + navigationItem.rightBarButtonItem?.tintColor = theme.colors.link + + if let loggedEditActions = loggedEditActions, + !loggedEditActions.contains(EditFunnel.Action.preview) { + editFunnel?.logEditPreviewForArticle(from: editFunnelSource, language: languageCode) + loggedEditActions.add(EditFunnel.Action.preview) + } + apply(theme: theme) + previewWebViewContainer.webView.uiDelegate = self + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + loadPreviewIfNecessary() + } + + override func viewWillDisappear(_ animated: Bool) { + WMFAlertManager.sharedInstance.dismissAlert() + super.viewWillDisappear(animated) + } + + deinit { + messagingController.removeScriptMessageHandler() + } + + private var hasPreviewed = false + + private func loadPreviewIfNecessary() { + guard !hasPreviewed else { + return + } + hasPreviewed = true + messagingController.setup(with: previewWebViewContainer.webView, languageCode: languageCode ?? "en", theme: theme, layoutMargins: articleMargins, areTablesInitiallyExpanded: true) + WMFAlertManager.sharedInstance.showAlert(WMFLocalizedString("wikitext-preview-changes", value: "Retrieving preview of your changes...", comment: "Alert text shown when getting preview of user changes to wikitext"), sticky: false, dismissPreviousAlerts: true, tapCallBack: nil) + + let pcsLocalAndStagingEnvironmentsCompletion: () throws -> Void = { [weak self] in + + guard let self = self else { + return + } + + // If on local or staging PCS, we need to split this call. On the RESTBase server, wikitext-to-mobilehtml just puts together two other + // calls - wikitext-to-html, and html-to-mobilehtml. Since we have html-to-mobilehtml in local/staging PCS but not the first call, if + // we're making PCS edits to mobilehtml we need this code in order to view them. We split the call (similar to what the server dioes) + // routing the wikitext-to-html call to production, and html-to-mobilehtml to local or staging PCS. + let completion: ((String?, URL?) -> Void) = { [weak self] (html, responseUrl) in + DispatchQueue.main.async { + guard let html = html else { + self?.showGenericError() + return + } + // While we'd normally expect this second request to be able to loaded via `...webView.load(request)`, for unknown + // reasons it wasn't working in that route - but was working when loaded via HTML string (in completion handler) - + // despite both responses being identical when inspected via a proxy server. + self?.previewWebViewContainer.webView.loadHTMLString(html, baseURL: responseUrl) + } + } + try self.fetcher.fetchMobileHTMLFromWikitext(articleURL: self.articleURL, wikitext: self.wikitext, mobileHTMLOutput: .editPreview, completion: completion) + } + + let pcsProductionCompletion: () throws -> Void = { [weak self] in + + guard let self = self else { + return + } + + let request = try self.fetcher.wikitextToMobileHTMLPreviewRequest(articleURL: self.articleURL, wikitext: self.wikitext, mobileHTMLOutput: .editPreview) + self.previewWebViewContainer.webView.load(request) + } + + do { + let environment = Configuration.current.environment + switch environment { + case .local(let options): + if options.contains(.localPCS) { + try pcsLocalAndStagingEnvironmentsCompletion() + return + } + try pcsProductionCompletion() + case .staging(let options): + if options.contains(.appsLabsforPCS) { + try pcsLocalAndStagingEnvironmentsCompletion() + return + } + try pcsProductionCompletion() + default: + try pcsProductionCompletion() + } + } catch { + showGenericError() + } + } + + override func apply(theme: Theme) { + super.apply(theme: theme) + if viewIfLoaded == nil { + return + } + previewWebViewContainer.apply(theme: theme) + } + + @objc func tappedWebViewBackground() { + dismissReferenceBackLinksViewController() + } +} + +// MARK: - References +extension EditPreviewViewController: WMFReferencePageViewAppearanceDelegate, ReferenceViewControllerDelegate, UIPageViewControllerDelegate { + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + didFinishAnimating(pageViewController) + } +} + +extension EditPreviewViewController: ReferenceBackLinksViewControllerDelegate, ReferenceShowing { + var webView: WKWebView { + return previewWebViewContainer.webView + } +} + +extension EditPreviewViewController: UIGestureRecognizerDelegate { + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + return shouldRecognizeSimultaneousGesture(recognizer: gestureRecognizer) + } +} + +extension EditPreviewViewController: ArticleWebMessageHandling { + func didRecieve(action: ArticleWebMessagingController.Action) { + switch action { + case .unknown(let href): + showExternalLinkInAlert(link: href) + case .backLink(let referenceId, let referenceText, let backLinks): + showReferenceBackLinks(backLinks, referenceId: referenceId, referenceText: referenceText) + case .reference(let index, let group): + showReferences(group, selectedIndex: index, animated: true) + case .link(let href, _, let title): + if let title = title, !title.isEmpty { + guard + let host = articleURL.host, + let encodedTitle = title.percentEncodedPageTitleForPathComponents, + let newArticleURL = Configuration.current.articleURLForHost(host, languageVariantCode: articleURL.wmf_languageVariantCode, appending: [encodedTitle]) else { + showInternalLinkInAlert(link: href) + break + } + showInternalLink(url: newArticleURL) + } else { + showExternalLinkInAlert(link: href) + } + case .scrollToAnchor(let anchor, let rect): + scrollToAnchorCompletions.popLast()?(anchor, rect) + default: + break + } + } + + internal func updateArticleMargins() { + messagingController.updateMargins(with: articleMargins, leadImageHeight: 0) + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + let marginUpdater: ((UIViewControllerTransitionCoordinatorContext) -> Void) = { _ in self.updateArticleMargins() } + coordinator.animate(alongsideTransition: marginUpdater) + } +} + +// MARK: - Context Menu + +extension EditPreviewViewController: ArticleContextMenuPresenting, WKUIDelegate { + var configuration: Configuration { + return Configuration.current + } + + func getPeekViewControllerAsync(for destination: Router.Destination, completion: @escaping (UIViewController?) -> Void) { + completion(getPeekViewController(for: destination)) + } + + func webView(_ webView: WKWebView, contextMenuConfigurationForElement elementInfo: WKContextMenuElementInfo, completionHandler: @escaping (UIContextMenuConfiguration?) -> Void) { + + self.contextMenuConfigurationForElement(elementInfo, completionHandler: completionHandler) + } + +// func webView(_ webView: WKWebView, contextMenuForElement elementInfo: WKContextMenuElementInfo, willCommitWithAnimator animator: UIContextMenuInteractionCommitAnimating) +// No function with this signature, as we don't want to have any context menu elements in preview - and we get that behavior by default by not implementing this. + + func getPeekViewController(for destination: Router.Destination) -> UIViewController? { + let dataStore = MWKDataStore.shared() + switch destination { + case .article(let articleURL): + return ArticlePeekPreviewViewController(articleURL: articleURL, dataStore: dataStore, theme: theme) + default: + return nil + } + } + + // This function needed is for ArticleContextMenuPresenting, but not applicable to EditPreviewVC + func hideFindInPage(_ completion: (() -> Void)? = nil) { + } + + var previewMenuItems: [UIMenuElement]? { + return nil + } +} + +extension EditPreviewViewController: EditingFlowViewController { + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditSaveViewController.storyboard b/Apps/Wikipedia/Wikipedia/Code/EditSaveViewController.storyboard new file mode 100644 index 0000000..37dc12f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditSaveViewController.storyboard @@ -0,0 +1,284 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/EditSaveViewController.swift b/Apps/Wikipedia/Wikipedia/Code/EditSaveViewController.swift new file mode 100644 index 0000000..a4607ba --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditSaveViewController.swift @@ -0,0 +1,478 @@ +import UIKit +import WMF + +struct SectionEditorChanges { + let newRevisionID: UInt64 +} + +protocol EditSaveViewControllerDelegate: NSObjectProtocol { + func editSaveViewControllerDidSave(_ editSaveViewController: EditSaveViewController, result: Result) + func editSaveViewControllerWillCancel(_ saveData: EditSaveViewController.SaveData) +} + +private enum NavigationMode : Int { + case wikitext + case abuseFilterWarning + case abuseFilterDisallow + case preview + case captcha +} + +class EditSaveViewController: WMFScrollViewController, Themeable, UITextFieldDelegate, UIScrollViewDelegate, WMFCaptchaViewControllerDelegate, EditSummaryViewDelegate { + + struct SaveData { + let summmaryText: String + let isMinorEdit: Bool + let shouldAddToWatchList: Bool + } + + var savedData: SaveData? + + var sectionID: Int? + var articleURL: URL? + var languageCode: String? + var dataStore: MWKDataStore? + + var wikitext = "" + var theme: Theme = .standard + weak var delegate: EditSaveViewControllerDelegate? + + // MARK: Event logging + var editFunnel: EditFunnel? + var editFunnelSource: EditFunnelSource = .unknown + var savedPagesFunnel: SavedPagesFunnel? + var loggedEditActions: NSMutableSet? + + private lazy var captchaViewController: WMFCaptchaViewController? = WMFCaptchaViewController.wmf_initialViewControllerFromClassStoryboard() + @IBOutlet private var captchaContainer: UIView! + @IBOutlet private var editSummaryVCContainer: UIView! + @IBOutlet private var licenseTitleTextView: UITextView! + @IBOutlet private var licenseLoginTextView: UITextView! + @IBOutlet private var textViews: [UITextView]! + @IBOutlet private var dividerHeightConstraits: [NSLayoutConstraint]! + @IBOutlet private var dividerViews: [UIView]! + @IBOutlet private var spacerAboveBottomDividerHeightConstrait: NSLayoutConstraint! + + @IBOutlet public var minorEditLabel: UILabel! + @IBOutlet public var minorEditButton: AutoLayoutSafeMultiLineButton! + @IBOutlet public var minorEditToggle: UISwitch! + @IBOutlet public var addToWatchlistLabel: UILabel! + @IBOutlet public var addToWatchlistButton: AutoLayoutSafeMultiLineButton! + @IBOutlet public var addToWatchlistToggle: UISwitch! + + @IBOutlet public var addToWatchlistStackView: UIStackView! + + @IBOutlet weak var stackView: UIStackView! + + @IBOutlet private var scrollContainer: UIView! + private var buttonSave: UIBarButtonItem? + private var buttonNext: UIBarButtonItem? + private var buttonX: UIBarButtonItem? + private var buttonLeftCaret: UIBarButtonItem? + private var abuseFilterCode = "" + private var summaryText = "" + + private var mode: NavigationMode = .preview { + didSet { + updateNavigation(for: mode) + } + } + private let wikiTextSectionUploader = WikiTextSectionUploader() + + private var licenseTitleTextViewAttributedString: NSAttributedString { + let localizedString = WMFLocalizedString("wikitext-upload-save-terms-and-licenses", languageCode: languageCode, value: "By publishing changes, you agree to the %1$@Terms of Use%2$@, and you irrevocably agree to release your contribution under the %3$@CC BY-SA 3.0%4$@ License and the %5$@GFDL%6$@. You agree that a hyperlink or URL is sufficient attribution under the Creative Commons license.", comment: "Text for information about the Terms of Use and edit licenses. Parameters:\n* %1$@ - app-specific non-text formatting, %2$@ - app-specific non-text formatting, %3$@ - app-specific non-text formatting, %4$@ - app-specific non-text formatting, %5$@ - app-specific non-text formatting, %6$@ - app-specific non-text formatting.") + + let substitutedString = String.localizedStringWithFormat( + localizedString, + "", + "", + "", + "" , + "", + "" + ) + + let attributedString = substitutedString.byAttributingHTML(with: .caption2, matching: traitCollection) + + return attributedString + } + + private var licenseLoginTextViewAttributedString: NSAttributedString { + let localizedString = WMFLocalizedString("wikitext-upload-save-anonymously-or-login", languageCode: languageCode, value: "Edits will be attributed to the IP address of your device. If you %1$@Log in%2$@ you will have more privacy.", comment: "Text informing user of draw-backs of not signing in before saving wikitext. Parameters:\n* %1$@ - app-specific non-text formatting, %2$@ - app-specific non-text formatting.") + + let substitutedString = String.localizedStringWithFormat( + localizedString, + "", // "#LOGIN_HREF" ensures 'byAttributingHTML' doesn't strip the anchor. The entire text view uses a tap recognizer so the string itself is unimportant. + "" + ) + + let attributedString = substitutedString.byAttributingHTML(with: .caption2, matching: traitCollection) + + return attributedString + } + + private func updateNavigation(for mode: NavigationMode) { + var backButton: UIBarButtonItem? + var forwardButton: UIBarButtonItem? + + switch mode { + case .wikitext: + backButton = buttonLeftCaret + forwardButton = buttonNext + case .abuseFilterWarning: + backButton = buttonLeftCaret + forwardButton = buttonSave + case .abuseFilterDisallow: + backButton = buttonLeftCaret + forwardButton = nil + case .preview: + backButton = buttonLeftCaret + forwardButton = buttonSave + case .captcha: + backButton = buttonX + forwardButton = buttonSave + } + navigationItem.leftBarButtonItem = backButton + navigationItem.rightBarButtonItem = forwardButton + } + + @objc private func goBack() { + if mode == .abuseFilterWarning { + editFunnel?.logAbuseFilterWarningBackForSectionEdit(abuseFilterName: abuseFilterCode, source: editFunnelSource, language: languageCode) + } + + delegate?.editSaveViewControllerWillCancel(SaveData(summmaryText: summaryText, isMinorEdit: minorEditToggle.isOn, shouldAddToWatchList: addToWatchlistToggle.isOn)) + + navigationController?.popViewController(animated: true) + } + + @objc private func goForward() { + switch mode { + case .abuseFilterWarning: + save() + editFunnel?.logAbuseFilterWarningIgnoreForSectionEdit(abuseFilterName: abuseFilterCode, source: editFunnelSource, language: languageCode) + case .captcha: + save() + default: + save() + } + } + + override func viewDidLoad() { + super.viewDidLoad() + + setupButtonsAndTitle() + mode = .preview + + for dividerHeightContraint in dividerHeightConstraits { + dividerHeightContraint.constant = 1.0 / UIScreen.main.scale + } + + // TODO: show this once we figure out how to handle watchlists (T214749) + addToWatchlistStackView.isHidden = true + + if let loggedEditActions = loggedEditActions, !loggedEditActions.contains(EditFunnel.Action.editSummaryShown) { + editFunnel?.logSectionEditSummaryShown(source: editFunnelSource, language: languageCode) + loggedEditActions.add(EditFunnel.Action.editSummaryShown) + } + updateTextViews() + apply(theme: theme) + + captchaViewController?.captchaDelegate = self + captchaViewController?.apply(theme: theme) + wmf_add(childController: captchaViewController, andConstrainToEdgesOfContainerView: captchaContainer) + + let vc = EditSummaryViewController(nibName: EditSummaryViewController.wmf_classStoryboardName(), bundle: nil) + vc.delegate = self + vc.apply(theme: theme) + vc.setLanguage(for: articleURL) + wmf_add(childController: vc, andConstrainToEdgesOfContainerView: editSummaryVCContainer) + + if dataStore?.authenticationManager.isLoggedIn ?? false { + licenseLoginTextView.isHidden = true + } + + if let savedData = savedData { + vc.updateInputText(to: savedData.summmaryText) + minorEditToggle.isOn = savedData.isMinorEdit + addToWatchlistToggle.isOn = savedData.shouldAddToWatchList + } + } + + + func setupSemanticContentAttibute() { + let semanticContentAttibute = MWKLanguageLinkController.semanticContentAttribute(forContentLanguageCode: languageCode) + for subview in stackView.subviews { + subview.semanticContentAttribute = semanticContentAttibute + } + licenseLoginTextView.semanticContentAttribute = semanticContentAttibute + licenseLoginTextView.textAlignment = semanticContentAttibute == .forceRightToLeft ? .right : .left + licenseTitleTextView.semanticContentAttribute = semanticContentAttibute + licenseTitleTextView.textAlignment = semanticContentAttibute == .forceRightToLeft ? .right : .left + } + + private func setupButtonsAndTitle() { + navigationItem.title = WMFLocalizedString("wikitext-preview-save-changes-title", value: "Save changes", comment: "Title for edit preview screens") + buttonX = UIBarButtonItem.wmf_buttonType(.X, target: self, action: #selector(self.goBack)) + buttonLeftCaret = UIBarButtonItem.wmf_buttonType(.caretLeft, target: self, action: #selector(self.goBack)) + + buttonSave = UIBarButtonItem(title: CommonStrings.publishTitle, style: .done, target: self, action: #selector(self.goForward)) + buttonSave?.tintColor = theme.colors.link + + minorEditLabel.text = WMFLocalizedString("edit-minor-text", languageCode: languageCode, value: "This is a minor edit", comment: "Text for minor edit label") + minorEditButton.setTitle(WMFLocalizedString("edit-minor-learn-more-text", languageCode: languageCode, value: "Learn more about minor edits", comment: "Text for minor edits learn more button"), for: .normal) + + addToWatchlistLabel.text = WMFLocalizedString("edit-watch-this-page-text", value: "Watch this page", comment: "Text for watch this page label") + addToWatchlistButton.setTitle(WMFLocalizedString("edit-watch-list-learn-more-text", value: "Learn more about watch lists", comment: "Text for watch lists learn more button"), for: .normal) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateTextViews() + } + + private func updateTextViews() { + licenseTitleTextView.attributedText = licenseTitleTextViewAttributedString + licenseLoginTextView.attributedText = licenseLoginTextViewAttributedString + applyThemeToTextViews() + setupSemanticContentAttibute() + } + + @IBAction public func licenseLoginLabelTapped(_ recognizer: UIGestureRecognizer?) { + if recognizer?.state == .ended { + guard let loginVC = WMFLoginViewController.wmf_initialViewControllerFromClassStoryboard() else { + assertionFailure("Expected view controller") + return + } + loginVC.funnel = WMFLoginFunnel() + loginVC.funnel?.logStart(fromEdit: editFunnel?.sessionToken) + loginVC.apply(theme: theme) + present(WMFThemeableNavigationController(rootViewController: loginVC, theme: theme), animated: true) + } + } + + private func highlightCaptchaSubmitButton(_ highlight: Bool) { + buttonSave?.isEnabled = highlight + } + + override func viewWillDisappear(_ animated: Bool) { + WMFAlertManager.sharedInstance.dismissAlert() + super.viewWillDisappear(animated) + } + + private func save() { + WMFAlertManager.sharedInstance.showAlert(WMFLocalizedString("wikitext-upload-save", value: "Publishing...", comment: "Alert text shown when changes to section wikitext are being published {{Identical|Publishing}}"), sticky: true, dismissPreviousAlerts: true, tapCallBack: nil) + + editFunnel?.logSectionSaveAttempt(source: editFunnelSource, language: languageCode) + + if savedPagesFunnel != nil { + savedPagesFunnel?.logEditAttempt(withArticleURL: articleURL) + } + + guard let sectionID = sectionID else { + assertionFailure("Could not get section to be edited") + return + } + + guard let editURL = articleURL else { + assertionFailure("Could not get url of section to be edited") + return + } + + wikiTextSectionUploader.uploadWikiText(wikitext, forArticleURL: editURL, section: "\(sectionID)", summary: summaryText, isMinorEdit: minorEditToggle.isOn, addToWatchlist: addToWatchlistToggle.isOn, baseRevID: nil, captchaId: captchaViewController?.captcha?.captchaID, captchaWord: captchaViewController?.solution, completion: { (result, error) in + DispatchQueue.main.async { + if let error = error { + self.handleEditFailure(with: error) + return + } + if let result = result { + self.handleEditSuccess(with: result) + } else { + self.handleEditFailure(with: RequestError.unexpectedResponse) + } + } + }) + + } + + private func handleEditSuccess(with result: [AnyHashable: Any]) { + let notifyDelegate: (Result) -> Void = { result in + DispatchQueue.main.async { + self.delegate?.editSaveViewControllerDidSave(self, result: result) + } + } + guard let fetchedData = result as? [String: Any], let newRevID = fetchedData["newrevid"] as? UInt64 else { + assertionFailure("Could not extract rev id as Int") + notifyDelegate(.failure(RequestError.unexpectedResponse)) + return + } + editFunnel?.logSectionSaved(source: editFunnelSource, revision: newRevID, language: languageCode) + notifyDelegate(.success(SectionEditorChanges(newRevisionID: newRevID))) + } + + private func handleEditFailure(with error: Error) { + let nsError = error as NSError + let errorType = WikiTextSectionUploaderErrorType.init(rawValue: nsError.code) ?? .unknown + + switch errorType { + case .needsCaptcha: + if mode == .captcha { + editFunnel?.logCaptchaFailedForSectionEdit(source: editFunnelSource, language: languageCode) + } + + let captchaUrl = URL(string: nsError.userInfo["captchaUrl"] as? String ?? "") + let captchaId = nsError.userInfo["captchaId"] as? String ?? "" + WMFAlertManager.sharedInstance.showErrorAlert(nsError, sticky: false, dismissPreviousAlerts: true, tapCallBack: nil) + captchaViewController?.captcha = WMFCaptcha(captchaID: captchaId, captchaURL: captchaUrl!) + editFunnel?.logCaptchaShownForSectionEdit(source: editFunnelSource, language: languageCode) + mode = .captcha + highlightCaptchaSubmitButton(false) + dispatchOnMainQueueAfterDelayInSeconds(0.1) { // Prevents weird animation. + self.captchaViewController?.captchaTextFieldBecomeFirstResponder() + } + case .abuseFilterDisallowed, .abuseFilterWarning, .abuseFilterOther: + wmf_hideKeyboard() + WMFAlertManager.sharedInstance.dismissAlert() // Hide "Publishing..." + + guard let displayError = nsError.userInfo[NSErrorUserInfoDisplayError] as? MediaWikiAPIDisplayError, + let currentTitle = articleURL?.wmf_title else { + return + } + + if errorType == .abuseFilterDisallowed { + mode = .abuseFilterDisallow + abuseFilterCode = displayError.code + editFunnel?.logAbuseFilterErrorForSectionEdit(abuseFilterName: abuseFilterCode, source: editFunnelSource, language: languageCode) + + wmf_showAbuseFilterDisallowPanel(messageHtml: displayError.messageHtml, linkBaseURL: displayError.linkBaseURL, currentTitle: currentTitle, theme: theme, goBackIsOnlyDismiss: false) + + } else { + mode = .abuseFilterWarning + abuseFilterCode = displayError.code + editFunnel?.logAbuseFilterWarningForSectionEdit(abuseFilterName: abuseFilterCode, source: editFunnelSource, language: languageCode) + + wmf_showAbuseFilterWarningPanel(messageHtml: displayError.messageHtml, linkBaseURL: displayError.linkBaseURL, currentTitle: currentTitle, theme: theme, goBackIsOnlyDismiss: false, publishAnywayTapHandler: { [weak self] _ in + + self?.dismiss(animated: true) { + self?.save() + } + + }) + } + + case .server, .unknown: + WMFAlertManager.sharedInstance.showErrorAlert(nsError, sticky: true, dismissPreviousAlerts: true, tapCallBack: nil) + editFunnel?.logSectionSaveError(source: editFunnelSource, language: languageCode, errorText: "other") + case .blocked: + + WMFAlertManager.sharedInstance.dismissAlert() // Hide "Publishing..." + + guard let displayError = nsError.userInfo[NSErrorUserInfoDisplayError] as? MediaWikiAPIDisplayError, + let currentTitle = articleURL?.wmf_title else { + return + } + + wmf_showBlockedPanel(messageHtml: displayError.messageHtml, linkBaseURL: displayError.linkBaseURL, currentTitle: currentTitle, theme: theme, image: UIImage(named: "error-icon")) + + default: + WMFAlertManager.sharedInstance.showErrorAlert(nsError, sticky: true, dismissPreviousAlerts: true, tapCallBack: nil) + editFunnel?.logSectionSaveError(source: editFunnelSource, language: languageCode, errorText: "other") + } + } + + internal func textFieldShouldReturn(_ textField: UITextField) -> Bool { + if let solution = captchaViewController?.solution { + if !solution.isEmpty { + save() + } + } + return true + } + + func captchaSiteURL() -> URL { + return articleURL?.wmf_site ?? Configuration.current.defaultSiteURL + } + + func captchaReloadPushed(_ sender: AnyObject) { + } + + func captchaHideSubtitle() -> Bool { + return true + } + + func captchaKeyboardReturnKeyTapped() { + save() + } + + func captchaSolutionChanged(_ sender: AnyObject, solutionText: String?) { + highlightCaptchaSubmitButton(((solutionText?.count ?? 0) == 0) ? false : true) + } + + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.paperBackground + scrollView.backgroundColor = theme.colors.paperBackground + + minorEditLabel.textColor = theme.colors.primaryText + minorEditButton.titleLabel?.textColor = theme.colors.link + addToWatchlistLabel.textColor = theme.colors.primaryText + addToWatchlistButton.titleLabel?.textColor = theme.colors.link + scrollContainer.backgroundColor = theme.colors.paperBackground + captchaContainer.backgroundColor = theme.colors.paperBackground + + applyThemeToTextViews() + + for dividerView in dividerViews { + dividerView.backgroundColor = theme.colors.tertiaryText + } + } + + private func applyThemeToTextViews() { + for textView in textViews { + textView.backgroundColor = theme.colors.paperBackground + textView.textColor = theme.colors.secondaryText + textView.linkTextAttributes = [NSAttributedString.Key.foregroundColor: theme.colors.link] + } + } + + func learnMoreButtonTapped(sender: UIButton) { + navigate(to: URL(string: "https://meta.wikimedia.org/wiki/Help:Edit_summary")) + } + + @IBAction public func minorEditButtonTapped(sender: UIButton) { + navigate(to: URL(string: "https://meta.wikimedia.org/wiki/Help:Minor_edit")) + } + + @IBAction public func watchlistButtonTapped(sender: UIButton) { + navigate(to: URL(string: "https://www.mediawiki.org/wiki/Help:Watching_pages")) + } + + func summaryChanged(newSummary: String) { + summaryText = newSummary + } + + func cannedButtonTapped(type: EditSummaryViewCannedButtonType) { + editFunnel?.logSectionEditSummaryTap(source: editFunnelSource, editSummaryType: type, language: languageCode) + } + + // Keep bottom divider and license/login labels at bottom of screen while remaining scrollable. + // (Having these bits scrollable is important for landscape, being covered by keyboard, captcha appearance, small screen devices, etc.) + private func adjustHeightOfSpacerAboveBottomDividerSoContentViewIsAtLeastHeightOfScrollView() { + spacerAboveBottomDividerHeightConstrait.constant = 0 + scrollContainer.setNeedsLayout() + scrollContainer.layoutIfNeeded() + spacerAboveBottomDividerHeightConstrait.constant = max(0, scrollView.frame.size.height - scrollContainer.frame.size.height) + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + adjustHeightOfSpacerAboveBottomDividerSoContentViewIsAtLeastHeightOfScrollView() + } +} + +extension EditSaveViewController: EditingFlowViewController { + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditSummaryViewController.swift b/Apps/Wikipedia/Wikipedia/Code/EditSummaryViewController.swift new file mode 100644 index 0000000..acf5248 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditSummaryViewController.swift @@ -0,0 +1,154 @@ +import UIKit +import WMF + +protocol EditSummaryViewDelegate: AnyObject { + func summaryChanged(newSummary: String) + func learnMoreButtonTapped(sender: UIButton) + func cannedButtonTapped(type: EditSummaryViewCannedButtonType) +} + +// Int because we use `tag` from storyboard buttons. +public enum EditSummaryViewCannedButtonType: Int { + case typo, grammar, link + + var eventLoggingKey: String { + switch self { + case .typo: + return "typo" + case .grammar: + return "grammar" + case .link: + return "links" + } + } +} + +class EditSummaryViewController: UIViewController, Themeable { + static let maximumSummaryLength = 255 + + public var theme: Theme = .standard + + public var languageCode: String? = "en" + + public weak var delegate: EditSummaryViewDelegate? + + @IBOutlet private weak var addSummaryLabel: UILabel! + @IBOutlet private weak var learnMoreButton: UIButton! + @IBOutlet private weak var summaryTextField: ThemeableTextField! + + @IBOutlet private weak var fixedTypoButton: UIButton! + @IBOutlet private weak var fixedGrammarButton: UIButton! + @IBOutlet private weak var addedLinksButton: UIButton! + @IBOutlet private var cannedEditSummaryButtons: [UIButton]! + + @IBOutlet weak var stackView: UIStackView! + @IBOutlet weak var labelStackView: UIStackView! + + private(set) var semanticContentAttribute: UISemanticContentAttribute? + + override func viewDidLoad() { + super.viewDidLoad() + + let placeholderText = WMFLocalizedString("edit-summary-placeholder-text", languageCode: languageCode, value: "How did you improve the article?", comment: "Placeholder text which appears initially in the free-form edit summary text box") + + cannedEditSummaryButtons.compactMap { $0.titleLabel }.forEach { + $0.numberOfLines = 1 + $0.setContentCompressionResistancePriority(.required, for: .horizontal) + } + + addSummaryLabel.text = WMFLocalizedString("edit-summary-add-summary-text", languageCode: languageCode, value: "Add an edit summary", comment: "Text for add summary label") + learnMoreButton.setTitle(WMFLocalizedString("edit-summary-learn-more-text", languageCode: languageCode, value: "Learn more", comment: "Text for learn more button"), for: .normal) + summaryTextField.placeholder = placeholderText + summaryTextField.delegate = self + summaryTextField.addTarget(self, action: #selector(self.textFieldDidChange), for: .editingChanged) + fixedTypoButton.setTitle(WMFLocalizedString("edit-summary-choice-fixed-typos", languageCode: languageCode, value: "Fixed typo", comment: "Button text for quick 'fixed typos' edit summary selection"), for: .normal) + fixedGrammarButton.setTitle(WMFLocalizedString("edit-summary-choice-fixed-grammar", languageCode: languageCode, value: "Fixed grammar", comment: "Button text for quick 'improved grammar' edit summary selection"), for: .normal) + addedLinksButton.setTitle(WMFLocalizedString("edit-summary-choice-linked-words", languageCode: languageCode, value: "Added links", comment: "Button text for quick 'link addition' edit summary selection"), for: .normal) + + setupSemanticContentAttibute() + apply(theme: theme) + } + + func setupSemanticContentAttibute() { + let semanticContentAttibute = MWKLanguageLinkController.semanticContentAttribute(forContentLanguageCode: languageCode) + + for subview in stackView.subviews { + subview.semanticContentAttribute = semanticContentAttibute + } + + summaryTextField.semanticContentAttribute = semanticContentAttibute + summaryTextField.textAlignment = semanticContentAttibute == .forceRightToLeft ? .right : .left + } + + @IBAction private func learnMoreButtonTapped(sender: UIButton) { + delegate?.learnMoreButtonTapped(sender: sender) + } + + @objc public func textFieldDidChange(textField: UITextField) { + notifyDelegateOfSummaryChange() + } + + private func notifyDelegateOfSummaryChange() { + delegate?.summaryChanged(newSummary: summaryTextField.text ?? "") + } + + @IBAction private func cannedSummaryButtonTapped(sender: UIButton) { + guard let senderLabel = sender.titleLabel?.text, + let buttonType = EditSummaryViewCannedButtonType(rawValue: sender.tag) else { + assertionFailure("Expected button information not found") + return + } + updateInputText(to: senderLabel) + delegate?.cannedButtonTapped(type: buttonType) + } + + func updateInputText(to text: String) { + summaryTextField.text = text + notifyDelegateOfSummaryChange() + } + + public func setLanguage(for pageURL: URL?) { + if let pageURL { + self.languageCode = pageURL.wmf_languageCode + + } + } + + + public func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.paperBackground + addSummaryLabel.textColor = theme.colors.secondaryText + learnMoreButton.titleLabel?.textColor = theme.colors.link + summaryTextField.apply(theme: theme) + cannedEditSummaryButtons.forEach { + $0.setTitleColor(theme.colors.tagText, for: .normal) + $0.backgroundColor = theme.colors.tagBackground + } + } +} + +extension EditSummaryViewController: UITextFieldDelegate { + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + // save() + return true + } + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + let newLength = (textField.text?.count ?? 0) + string.count - range.length + return newLength <= EditSummaryViewController.maximumSummaryLength + } +} + +public class SummaryButtonScrollView: UIScrollView { + @IBOutlet private var cannedEditSummaryButtons: [UIButton]! + private func sizeEncompassingTallestButton() -> CGSize { + let heightOfTallestButton = cannedEditSummaryButtons.map { $0.intrinsicContentSize.height }.max() + return CGSize(width: UIView.noIntrinsicMetric, height: heightOfTallestButton ?? UIView.noIntrinsicMetric) + } + override public var intrinsicContentSize: CGSize { + return sizeEncompassingTallestButton() + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditSummaryViewController.xib b/Apps/Wikipedia/Wikipedia/Code/EditSummaryViewController.xib new file mode 100644 index 0000000..5bcaa9a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditSummaryViewController.xib @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/EditToolbarView.swift b/Apps/Wikipedia/Wikipedia/Code/EditToolbarView.swift new file mode 100644 index 0000000..0ea2527 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditToolbarView.swift @@ -0,0 +1,38 @@ +class EditToolbarView: UIView, TextFormattingButtonsProviding { + weak var delegate: TextFormattingDelegate? + + @IBOutlet var separatorViews: [UIView] = [] + @IBOutlet var buttons: [TextFormattingButton] = [] + + override var intrinsicContentSize: CGSize { + let height = buttons.map { $0.intrinsicContentSize.height }.max() ?? UIView.noIntrinsicMetric + return CGSize(width: UIView.noIntrinsicMetric, height: height) + } + + override func awakeFromNib() { + super.awakeFromNib() + accessibilityElements = buttons + } +} + +extension EditToolbarView: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.inputAccessoryBackground + tintColor = theme.colors.link + if theme.hasInputAccessoryShadow { + layer.shadowOffset = CGSize(width: 0, height: -2) + layer.shadowRadius = 10 + layer.shadowOpacity = 1.0 + layer.shadowColor = theme.colors.shadow.cgColor + } else { + layer.shadowOffset = .zero + layer.shadowRadius = 0 + layer.shadowOpacity = 0 + layer.shadowColor = nil + } + separatorViews.forEach { $0.backgroundColor = theme.colors.border } + for button in buttons { + button.apply(theme: theme) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EditingWelcomeViewController.swift b/Apps/Wikipedia/Wikipedia/Code/EditingWelcomeViewController.swift new file mode 100644 index 0000000..e1f056a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EditingWelcomeViewController.swift @@ -0,0 +1,208 @@ +import UIKit + +@objc(WMFEditingWelcomeViewController) +final class EditingWelcomeViewController: WelcomeViewController { + private let beBoldDataSource = BeBoldDataSource() + private let impartialToneDataSource = ImpartialToneDataSource() + private let citeReliableSourcesDataSource = CiteReliableSourcesDataSource() + private let setKnowledgeFreeDataSource = SetKnowledgeFreeDataSource() + + @objc init(theme: Theme, completion: @escaping () -> Void) { + super.init(theme: theme, viewControllers: [ + WelcomeContainerViewController(dataSource: beBoldDataSource), + WelcomeContainerViewController(dataSource: impartialToneDataSource), + WelcomeContainerViewController(dataSource: citeReliableSourcesDataSource), + WelcomeContainerViewController(dataSource: setKnowledgeFreeDataSource) + ], completion: completion) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: Be bold + +final fileprivate class BeBoldDataSource: WelcomeContainerViewControllerDataSource { + let isFirst = true + + private let editAnimatedImageView = WelcomeAnimatedImageView(imageName: "editing-welcome/be-bold/edit", start: CGPoint(x: 59, y: 159), insertBelow: false) + private let plusesAnimatedImageView = WelcomeAnimatedImageView(imageName: "editing-welcome/be-bold/pluses") + + lazy var animationView: WelcomeAnimationView = { + return WelcomeAnimationView(staticImageNamed: "editing-welcome/be-bold/article", animatedImageViews: [editAnimatedImageView, plusesAnimatedImageView], propertyAnimator: propertyAnimator) + }() + + private lazy var propertyAnimator: UIViewPropertyAnimator = { + let editPropertyAnimator = UIViewPropertyAnimator(duration: 0.8, curve: .easeInOut) { + self.editAnimatedImageView.alpha = 1 + } + + editPropertyAnimator.addAnimations({ + self.plusesAnimatedImageView.alpha = 1 + }, delayFactor: 0.6) + + return editPropertyAnimator + }() + + lazy var panelViewController: WelcomePanelViewController = { + let contentText = WMFLocalizedString("editing-welcome-be-bold-subtitle", value: "Be bold but not reckless in updating articles. Do not agonize over making mistakes: every past version of a page is saved, so mistakes can be easily corrected by our community.", comment: "Subtitle for editing onboarding screen encouraging users to start editing Wikipedia articles") + let contentViewController = WelcomePanelLabelContentViewController(text: contentText) + return WelcomePanelViewController(titleLabelText: WMFLocalizedString("editing-welcome-be-bold-title", value: "Your voice is important", comment: "Title for editing onboarding screen encouraging users to start editing Wikipedia articles"), actionLabelText: CommonStrings.welcomePromiseTitle, actionButtonTitle: nil, contentViewController: contentViewController) + }() +} + +// MARK: Write in an impartial tone + +final fileprivate class ImpartialToneDataSource: WelcomeContainerViewControllerDataSource { + private let scaleBarAnimatedImageView = WelcomeAnimatedImageView(imageName: "editing-welcome/impartial-tone/bar", start: CGPoint(x: 73, y: 50), insertBelow: false, initialAlpha: 1) + private let scaleArticleAnimatedImageView = WelcomeAnimatedImageView(imageName: "editing-welcome/impartial-tone/article", start: CGPoint(x: 238, y: 61.5), initialAlpha: 1) + private let scaleSaturnAnimatedImageView = WelcomeAnimatedImageView(imageName: "editing-welcome/impartial-tone/saturn", start: CGPoint(x: 40, y: 61.5), initialAlpha: 1) + private let plusesAnimatedImageView = WelcomeAnimatedImageView(imageName: "editing-welcome/impartial-tone/pluses") + + lazy var animationView: WelcomeAnimationView = { + return WelcomeAnimationView(staticImageNamed: "editing-welcome/impartial-tone/stem", animatedImageViews: [scaleBarAnimatedImageView, scaleArticleAnimatedImageView, scaleSaturnAnimatedImageView, plusesAnimatedImageView], propertyAnimator: propertyAnimator) + }() + + private lazy var propertyAnimator: UIViewPropertyAnimator = { + let scalePropertyAnimator = UIViewPropertyAnimator(duration: 2.6, curve: .linear) + + scalePropertyAnimator.addAnimations { + self.scaleBarAnimatedImageView.transform = CGAffineTransform(rotationAngle: 15 * (.pi / 180)) + let scaleSaturnTranslationY = 0 - self.scaleBarAnimatedImageView.bounds.height * self.scaleBarAnimatedImageView.transform.a + let scaleArticleTranslationY = self.scaleBarAnimatedImageView.bounds.height * self.scaleBarAnimatedImageView.transform.a + self.scaleSaturnAnimatedImageView.transform = CGAffineTransform(translationX: 0, y: scaleSaturnTranslationY) + self.scaleArticleAnimatedImageView.transform = CGAffineTransform(translationX: 0, y: scaleArticleTranslationY) + } + + scalePropertyAnimator.addAnimations({ + self.scaleBarAnimatedImageView.transform = CGAffineTransform.identity + self.scaleSaturnAnimatedImageView.transform = CGAffineTransform.identity + self.scaleArticleAnimatedImageView.transform = CGAffineTransform.identity + }, delayFactor: 0.7) + + scalePropertyAnimator.addAnimations({ + self.plusesAnimatedImageView.alpha = 1 + }, delayFactor: 2.0) + + return scalePropertyAnimator + }() + + lazy var panelViewController: WelcomePanelViewController = { + let contentText = WMFLocalizedString("editing-welcome-impartial-tone-subtitle", value: "We strive for articles to be written in an impartial tone. When editing, aim to make a fair representation of the world as reliable sources describe it.", comment: "Subtitle for editing onboarding screen instructing users to use impartial tone when editing Wikipedia articles") + let contentViewController = WelcomePanelLabelContentViewController(text: contentText) + return WelcomePanelViewController(titleLabelText: WMFLocalizedString("editing-welcome-impartial-tone-title", value: "Write in an impartial tone", comment: "Title for editing onboarding screen instructing users to use impartial tone when editing Wikipedia articles"), actionLabelText: nil, actionButtonTitle: nil, contentViewController: contentViewController) + }() +} + +// MARK: Cite reliable sources + +final fileprivate class CiteReliableSourcesDataSource: WelcomeContainerViewControllerDataSource { + private let highlightAnimatedImageView = WelcomeAnimatedImageView(imageName: "editing-welcome/cite/highlight", start: CGPoint(x: 187, y: 96), insertBelow: false) + private let plusesAnimatedImageView = WelcomeAnimatedImageView(imageName: "editing-welcome/cite/pluses") + + lazy var animationView: WelcomeAnimationView = { + return WelcomeAnimationView(staticImageNamed: "editing-welcome/cite/article", animatedImageViews: [highlightAnimatedImageView, plusesAnimatedImageView], propertyAnimator: propertyAnimator) + }() + + private lazy var propertyAnimator: UIViewPropertyAnimator = { + let highlightPropertyAnimator = UIViewPropertyAnimator(duration: 1.0, curve: .easeInOut) + + highlightPropertyAnimator.addAnimations({ + self.highlightAnimatedImageView.alpha = 1 + }, delayFactor: 0.6) + + let plusesPropertyAnimator = UIViewPropertyAnimator(duration: 0.3, curve: .linear) { + self.plusesAnimatedImageView.alpha = 1 + } + + highlightPropertyAnimator.addCompletion { _ in + plusesPropertyAnimator.startAnimation() + } + + return highlightPropertyAnimator + }() + + lazy var panelViewController: WelcomePanelViewController = { + let contentText = WMFLocalizedString("editing-welcome-citations-subtitle", value: "All content must be verifiable. When adding new information to an article, editors should provide an inline citation to a reliable source that directly supports the contribution.", comment: "Subtitle for editing onboarding screen instructing users to cite reliable sources when editing Wikipedia articles") + let contentViewController = WelcomePanelLabelContentViewController(text: contentText) + return WelcomePanelViewController(titleLabelText: WMFLocalizedString("editing-welcome-citations-title", value: "Cite reliable sources", comment: "Title for editing onboarding screen instructing users to cite reliable sources when editing Wikipedia articles"), actionLabelText: nil, actionButtonTitle: nil, contentViewController: contentViewController) + }() +} + +// MARK: Set knowledge free + +final fileprivate class SetKnowledgeFreeDataSource: WelcomeContainerViewControllerDataSource { + private let plusesAnimatedImageView = WelcomeAnimatedImageView(imageName: "editing-welcome/set-knowledge-free/pluses") + + private lazy var articleAnimatedImageViews: [WelcomeAnimatedImageView] = { + let namePrefix = "editing-welcome/set-knowledge-free/article-" + let origin = CGPoint(x: 153, y: 85) + return [ + WelcomeAnimatedImageView(imageName: "\(namePrefix)16", start: origin, destination: CGPoint(x: 200, y: 118)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)16", start: origin, destination: CGPoint(x: 104, y: 25)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)16", start: origin, destination: CGPoint(x: 111, y: 144)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)16", start: origin, destination: CGPoint(x: 122, y: 93)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)14", start: origin, destination: CGPoint(x: 156, y: 62)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)16", start: origin, destination: CGPoint(x: 139, y: 55)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)16", start: origin, destination: CGPoint(x: 176, y: 26)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)16", start: origin, destination: CGPoint(x: 225, y: 24)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)15", start: origin, destination: CGPoint(x: 213, y: 30)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)11", start: origin, destination: CGPoint(x: 167, y: 119)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)16", start: origin, destination: CGPoint(x: 136, y: 167)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)14", start: origin, destination: CGPoint(x: 163, y: 136)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)14", start: origin, destination: CGPoint(x: 55, y: 92)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)15", start: origin, destination: CGPoint(x: 113, y: 113)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)13", start: origin, destination: CGPoint(x: 51, y: 26)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)2", start: origin, destination: CGPoint(x: 225, y: 47)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)12", start: origin, destination: CGPoint(x: 45, y: 67)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)11", start: origin, destination: CGPoint(x: 93, y: 29)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)13", start: origin, destination: CGPoint(x: 162, y: 33)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)9", start: origin, destination: CGPoint(x: 226, y: 31)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)8", start: origin, destination: CGPoint(x: 81, y: 79)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)11", start: origin, destination: CGPoint(x: 223, y: 106)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)6", start: origin, destination: CGPoint(x: 236, y: 85)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)5", start: origin, destination: CGPoint(x: 108, y: 68)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)4", start: origin, destination: CGPoint(x: 184, y: 95)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)3", start: origin, destination: CGPoint(x: 188, y: 51)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)2", start: origin, destination: CGPoint(x: 154, y: 160)), + WelcomeAnimatedImageView(imageName: "\(namePrefix)14", start: origin, destination: CGPoint(x: 76, y: 126)) + ] + }() + + lazy var animationView: WelcomeAnimationView = { + let animatedImageViews = articleAnimatedImageViews + [plusesAnimatedImageView] + return WelcomeAnimationView(staticImageNamed: "editing-welcome/set-knowledge-free/main-article", animatedImageViews: animatedImageViews, propertyAnimator: propertyAnimator) + }() + + private lazy var propertyAnimator: UIViewPropertyAnimator = { + let articlePropertyAnimator = UIViewPropertyAnimator(duration: 0.8, curve: .easeInOut) + + articlePropertyAnimator.addAnimations { + for imageView in self.articleAnimatedImageViews { + guard let destination = imageView.normalizedDestination else { + continue + } + imageView.alpha = 1 + imageView.frame.origin = destination + } + } + + let plusesPropertyAnimator = UIViewPropertyAnimator(duration: 0.5, curve: .linear) { + self.plusesAnimatedImageView.alpha = 1 + } + + articlePropertyAnimator.addCompletion { _ in + plusesPropertyAnimator.startAnimation() + } + + return articlePropertyAnimator + }() + + lazy var panelViewController: WelcomePanelViewController = { + let contentText = WMFLocalizedString("editing-welcome-set-knowledge-free-subtitle", value: "In order to give everyone access to the world’s knowledge, we need you to participate in its creation by reading, editing, and contributing to the topics that matter most to you.", comment: "Title for editing onboarding screen encouraging users to participate in the creation of Wikipedia content") + let contentViewController = WelcomePanelLabelContentViewController(text: contentText) + return WelcomePanelViewController(titleLabelText: WMFLocalizedString("editing-welcome-set-knowledge-free-title", value: "Set knowledge free", comment: "Title for editing onboarding screen encouraging users to participate in the creation of Wikipedia content"), actionLabelText: nil, actionButtonTitle: CommonStrings.getStartedTitle, contentViewController: contentViewController) + }() +} + diff --git a/Apps/Wikipedia/Wikipedia/Code/EmptyViewController.swift b/Apps/Wikipedia/Wikipedia/Code/EmptyViewController.swift new file mode 100644 index 0000000..04d0205 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EmptyViewController.swift @@ -0,0 +1,116 @@ +import Foundation +import WMF + +protocol EmptyViewControllerDelegate: AnyObject { + func triggeredRefresh(refreshCompletion: @escaping () -> Void) + func emptyViewScrollViewDidScroll(_ scrollView: UIScrollView) +} + +class EmptyViewController: UIViewController { + private let refreshControl = UIRefreshControl() + @IBOutlet var scrollView: UIScrollView! + @IBOutlet var emptyContainerView: UIView! + private var emptyView: WMFEmptyView? = nil + var canRefresh: Bool = false + weak var delegate: EmptyViewControllerDelegate? + var theme: Theme = .standard + @IBOutlet var emptyContainerViewTopConstraint: NSLayoutConstraint! + + var type: WMFEmptyViewType? { + didSet { + if oldValue != type { + emptyView?.removeFromSuperview() + emptyView = nil + + if let newType = type, + let emptyView = EmptyViewController.emptyView(of: newType, theme: self.theme, frame: .zero) { + emptyView.delegate = self + emptyContainerView.wmf_addSubviewWithConstraintsToEdges(emptyView) + + self.emptyView = emptyView + apply(theme: theme) + } + } + } + } + + override func viewDidLoad() { + super.viewDidLoad() + + scrollView.delegate = self + scrollView.alwaysBounceVertical = true + scrollView.contentInsetAdjustmentBehavior = .never + + if canRefresh { + refreshControl.layer.zPosition = -100 + refreshControl.addTarget(self, action: #selector(refreshControlActivated), for: .valueChanged) + + scrollView.addSubview(refreshControl) + scrollView.refreshControl = refreshControl + } + + apply(theme: theme) + } + + @objc private func refreshControlActivated() { + + delegate?.triggeredRefresh(refreshCompletion: { [weak self] in + self?.refreshControl.endRefreshing() + }) + } + + func centerEmptyView(topInset: CGFloat, topEmptyViewSpacing: CGFloat) { + guard viewIfLoaded != nil else { + return + } + scrollView.contentInset = UIEdgeInsets(top: topInset, left: 0, bottom: 0, right: 0) + emptyContainerViewTopConstraint.constant = topEmptyViewSpacing + } + + func centerEmptyView(within targetRect: CGRect) { + scrollView.contentInset = UIEdgeInsets(top: targetRect.minY, left: 0, bottom: 0, right: 0) + } + + private func determineTopOffset(emptyViewHeight: CGFloat) { + let availableHeight = view.bounds.height - scrollView.contentInset.top + let middle = availableHeight/2 + let heightMiddle = emptyViewHeight / 2 + let targetY = middle - heightMiddle + emptyContainerViewTopConstraint.constant = max(0, ceil(targetY)) + } +} + +extension EmptyViewController: Themeable { + func apply(theme: Theme) { + + self.theme = theme + + guard viewIfLoaded != nil else { + return + } + + if let emptyView = emptyView, + let bgKeyPath = theme.value(forKeyPath: emptyView.backgroundColorKeyPath) as? UIColor { + view.backgroundColor = bgKeyPath + scrollView.backgroundColor = bgKeyPath + (emptyView as Themeable).apply(theme: theme) + } else { + view.backgroundColor = theme.colors.paperBackground + scrollView.backgroundColor = theme.colors.paperBackground + } + + refreshControl.tintColor = theme.colors.refreshControlTint + } +} + +extension EmptyViewController: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + delegate?.emptyViewScrollViewDidScroll(scrollView) + } +} + +extension EmptyViewController: WMFEmptyViewDelegate { + func heightChanged(_ height: CGFloat) { + determineTopOffset(emptyViewHeight: height) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EmptyViewController.xib b/Apps/Wikipedia/Wikipedia/Code/EmptyViewController.xib new file mode 100644 index 0000000..890d9ba --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EmptyViewController.xib @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/EraseSavedArticlesView.swift b/Apps/Wikipedia/Wikipedia/Code/EraseSavedArticlesView.swift new file mode 100644 index 0000000..fe2e06e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EraseSavedArticlesView.swift @@ -0,0 +1,33 @@ +class EraseSavedArticlesView: UIView { + @IBOutlet weak var imageView: UIImageView! + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var button: UIButton! + @IBOutlet weak var separatorView: UIView! + @IBOutlet weak var footerLabel: UILabel! + + private var theme = Theme.standard + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + func updateFonts() { + button.titleLabel?.font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + } +} + +extension EraseSavedArticlesView: Themeable { + func apply(theme: Theme) { + self.theme = theme + backgroundColor = theme.colors.paperBackground + titleLabel.textColor = theme.colors.primaryText + titleLabel.backgroundColor = theme.colors.paperBackground + footerLabel.textColor = theme.colors.secondaryText + imageView.tintColor = theme.colors.icon == nil ? .white : theme.colors.icon + imageView.backgroundColor = theme.colors.iconBackground == nil ? .red600 : theme.colors.iconBackground + separatorView.backgroundColor = theme.colors.border + footerLabel.backgroundColor = theme.colors.paperBackground + button.titleLabel?.backgroundColor = theme.colors.paperBackground + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/EraseSavedArticlesView.xib b/Apps/Wikipedia/Wikipedia/Code/EraseSavedArticlesView.xib new file mode 100644 index 0000000..5cb24bc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EraseSavedArticlesView.xib @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/EventLoggingFunnel.h b/Apps/Wikipedia/Wikipedia/Code/EventLoggingFunnel.h new file mode 100644 index 0000000..fcc4801 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EventLoggingFunnel.h @@ -0,0 +1,162 @@ +@import Foundation; + +typedef NS_ENUM(NSUInteger, WMFEventLoggingMaxStringLength) { + WMFEventLoggingMaxStringLength_General = 99, ///< Recommended by analytics + WMFEventLoggingMaxStringLength_Snippet = 191 ///< MySQL length in practice +}; + +NS_ASSUME_NONNULL_BEGIN + +typedef NSString *EventLoggingCategory NS_TYPED_EXTENSIBLE_ENUM; +typedef NSString *EventLoggingLabel NS_TYPED_EXTENSIBLE_ENUM; + +extern EventLoggingCategory const EventLoggingCategoryFeed; +extern EventLoggingCategory const EventLoggingCategoryFeedDetail; +extern EventLoggingCategory const EventLoggingCategoryHistory; +extern EventLoggingCategory const EventLoggingCategoryPlaces; +extern EventLoggingCategory const EventLoggingCategoryArticle; +extern EventLoggingCategory const EventLoggingCategorySearch; +extern EventLoggingCategory const EventLoggingCategoryAddToList; +extern EventLoggingCategory const EventLoggingCategorySaved; +extern EventLoggingCategory const EventLoggingCategoryShared; +extern EventLoggingCategory const EventLoggingCategoryLogin; +extern EventLoggingCategory const EventLoggingCategorySetting; +extern EventLoggingCategory const EventLoggingCategoryLoginToSyncPopover; +extern EventLoggingCategory const EventLoggingCategoryEnableSyncPopover; +extern EventLoggingCategory const EventLoggingCategoryUnknown; + +extern EventLoggingLabel const EventLoggingLabelAnnouncement; +extern EventLoggingLabel const EventLoggingLabelArticleAnnouncement; +extern EventLoggingLabel const EventLoggingLabelFeaturedArticle; +extern EventLoggingLabel const EventLoggingLabelTopRead; +extern EventLoggingLabel const EventLoggingLabelReadMore; +extern EventLoggingLabel const EventLoggingLabelOnThisDay; +extern EventLoggingLabel const EventLoggingLabelRandom; +extern EventLoggingLabel const EventLoggingLabelNews; +extern EventLoggingLabel const EventLoggingLabelRelatedPages; +extern EventLoggingLabel const EventLoggingLabelArticleList; +extern EventLoggingLabel const EventLoggingLabelOutLink; +extern EventLoggingLabel const EventLoggingLabelSimilarPage; +extern EventLoggingLabel const EventLoggingLabelItems; +extern EventLoggingLabel const EventLoggingLabelLists; +extern EventLoggingLabel const EventLoggingLabelDefault; +extern EventLoggingLabel const EventLoggingLabelSyncEducation; +extern EventLoggingLabel const EventLoggingLabelLogin; +extern EventLoggingLabel const EventLoggingLabelSyncArticle; +extern EventLoggingLabel const EventLoggingLabelLocation; +extern EventLoggingLabel const EventLoggingLabelMainPage; +extern EventLoggingLabel const EventLoggingLabelContinueReading; +extern EventLoggingLabel const EventLoggingLabelPictureOfTheDay; + +/** + * Base class for EventLogging multi-stage funnels. + * + * Instantiate one of the subclasses at the beginning of the + * activity to be logged, and if necessary pass the funnel object + * down into further stages of your pipeline (eg from one View + * Controller to the next), then call the log* methods. + * + * Derived classes will contain specific log* methods for each + * potential logging action variant for readability in calling + * code. + */ +@interface EventLoggingFunnel : NSObject + +@property (nonatomic, strong) NSString *schema; +@property (nonatomic, assign) int revision; +/** + * Helper function that returns a persistent appInstallID. + * appInstallID is generated once per install. + */ +@property (nonatomic, readonly, nullable) NSString *appInstallID; +/** + * SessionID is reset when app is launched for the first time or resumed. + */ +@property (nonatomic, readonly, nullable) NSString *sessionID; +@property (nonatomic, readonly) NSString *timestamp; +@property (nonatomic, readonly) NSNumber *isAnon; + +/** + * Sampling rate used to calculate sampling ratio. + * Rate: Ratio: Percent: + * 1 1/1 100% + * 2 1/2 50% + * 3 1/3 33% + * ... + * 100 1/100 1% + */ +@property (nonatomic, assign) NSInteger rate; + +/** + * This constructor should be called internally by derived classes + * to encapsulate the schema name and version. + */ +- (id)initWithSchema:(NSString *)schema version:(int)revision; + +/** + * An optional preprocessing step before recording data passed + * to the 'log:' method(s). + * + * This can be convenient when many steps of a funnel require + * a common set of parameters, so they don't have to be repeated. + * + * Leave un-overridden if no preprocessing is needed. + */ +- (NSDictionary *)preprocessData:(NSDictionary *)eventData; + +/** + * The basic log: method takes a bare dictionary, which will + * get run through preprocessData: and then sent off to the + * background logging operation queue. + * + * Primary language as recorded in MWKLanguageLinkController will + * be used as the target of the logging request. + * + * For convenience, derived classes should contain specific + * log* methods for each potential logging action variant for + * readibility in calling code (and type safety on params!) + */ +- (void)log:(NSDictionary *)eventData; + +/** + * The basic log: method takes a bare dictionary, which will + * get run through preprocessData: and then sent off to the + * background logging operation queue. + * + * language will be used to determine the target wiki. + * If language is nil, primary language as recorded in + * MWKLanguageLinkController will be used instead. + * + * For convenience, derived classes should contain specific + * log* methods for each potential logging action variant for + * readibility in calling code (and type safety on params!) + */ +- (void)log:(NSDictionary *)eventData language:(nullable NSString *)language; + +/** + * In some cases logging should go to a specific wiki + * other than the one in the session. Call this as necessary. + * + * Wiki parameter is a dbname, not a domain or hostname! + */ +- (void)log:(NSDictionary *)eventData wiki:(NSString *)wiki; + +/** + * Called after eventData was logged through log:. + */ +- (void)logged:(NSDictionary *)eventData; + +/** + * Helper function to get the app's primary language. + * Falls back on English if primary language was not set. + */ +- (NSString *)primaryLanguage; + +/** + * Helper function to generate a per-use UUID. + */ +- (NSString *)singleUseUUID; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/EventLoggingFunnel.m b/Apps/Wikipedia/Wikipedia/Code/EventLoggingFunnel.m new file mode 100644 index 0000000..75fd945 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/EventLoggingFunnel.m @@ -0,0 +1,139 @@ +#import +#import + +EventLoggingCategory const EventLoggingCategoryFeed = @"feed"; +EventLoggingCategory const EventLoggingCategoryFeedDetail = @"feed_detail"; +EventLoggingCategory const EventLoggingCategoryHistory = @"history"; +EventLoggingCategory const EventLoggingCategoryPlaces = @"places"; +EventLoggingCategory const EventLoggingCategoryArticle = @"article"; +EventLoggingCategory const EventLoggingCategorySearch = @"search"; +EventLoggingCategory const EventLoggingCategoryAddToList = @"add_to_list"; +EventLoggingCategory const EventLoggingCategorySaved = @"saved"; +EventLoggingCategory const EventLoggingCategoryShared = @"shared"; +EventLoggingCategory const EventLoggingCategoryLogin = @"login"; +EventLoggingCategory const EventLoggingCategorySetting = @"setting"; +EventLoggingCategory const EventLoggingCategoryLoginToSyncPopover = @"login_to_sync_popover"; +EventLoggingCategory const EventLoggingCategoryEnableSyncPopover = @"enable_sync_popover"; +EventLoggingCategory const EventLoggingCategoryUnknown = @"unknown"; + +EventLoggingLabel const EventLoggingLabelAnnouncement = @"announcement"; +EventLoggingLabel const EventLoggingLabelArticleAnnouncement = @"article_announcement"; +EventLoggingLabel const EventLoggingLabelFeaturedArticle = @"featured_article"; +EventLoggingLabel const EventLoggingLabelTopRead = @"top_read"; +EventLoggingLabel const EventLoggingLabelReadMore = @"read_more"; +EventLoggingLabel const EventLoggingLabelRandom = @"random"; +EventLoggingLabel const EventLoggingLabelNews = @"news"; +EventLoggingLabel const EventLoggingLabelOnThisDay = @"on_this_day"; +EventLoggingLabel const EventLoggingLabelRelatedPages = @"related_pages"; +EventLoggingLabel const EventLoggingLabelArticleList = @"article_list"; +EventLoggingLabel const EventLoggingLabelOutLink = @"out_link"; +EventLoggingLabel const EventLoggingLabelSimilarPage = @"similar_page"; +EventLoggingLabel const EventLoggingLabelItems = @"items"; +EventLoggingLabel const EventLoggingLabelLists = @"lists"; +EventLoggingLabel const EventLoggingLabelDefault = @"default"; +EventLoggingLabel const EventLoggingLabelSyncEducation = @"sync_education"; +EventLoggingLabel const EventLoggingLabelLogin = @"login"; +EventLoggingLabel const EventLoggingLabelSyncArticle = @"sync_article"; +EventLoggingLabel const EventLoggingLabelLocation = @"location"; +EventLoggingLabel const EventLoggingLabelMainPage = @"main_page"; +EventLoggingLabel const EventLoggingLabelContinueReading = @"continue_reading"; +EventLoggingLabel const EventLoggingLabelPictureOfTheDay = @"picture_of_the_day"; + +@implementation EventLoggingFunnel + +- (id)initWithSchema:(NSString *)schema version:(int)revision { + if (self) { + self.schema = schema; + self.revision = revision; + self.rate = 1; + } + return self; +} + +- (NSDictionary *)preprocessData:(NSDictionary *)eventData { + return eventData; +} + +- (void)log:(NSDictionary *)eventData { + NSString *wiki = [self.primaryLanguage stringByAppendingString:@"wiki"]; + [self log:eventData wiki:wiki]; +} + +- (void)log:(NSDictionary *)eventData language:(nullable NSString *)language { + if (language) { + NSString *wiki = [language stringByAppendingString:@"wiki"]; + [self log:eventData wiki:wiki]; + } else { + [self log:eventData]; + } +} + +- (void)log:(NSDictionary *)eventData wiki:(NSString *)wiki { + WMFEventLoggingService *service = [WMFEventLoggingService sharedInstance]; + if (NSUserDefaults.standardUserDefaults.wmf_sendUsageReports) { + BOOL chosen = NO; + if (self.rate == 1) { + chosen = YES; + } else if (self.rate != 0) { + chosen = (self.getEventLogSamplingID % self.rate) == 0; + } + if (chosen) { + NSMutableDictionary *preprocessedEventData = [[self preprocessData:eventData] mutableCopy]; + [service logWithEvent:preprocessedEventData schema:self.schema revision:self.revision wiki:wiki]; + [self logged:eventData]; + } + } +} + +- (NSString *)primaryLanguage { + NSString *primaryLanguage = @"en"; + MWKLanguageLink *appLanguage = [MWKDataStore shared].languageLinkController.appLanguage; + if (appLanguage) { + primaryLanguage = appLanguage.languageCode; + } + assert(primaryLanguage); + return primaryLanguage; +} + +- (NSString *)singleUseUUID { + return [[NSUUID UUID] UUIDString]; +} + +- (void)logged:(NSDictionary *)eventData { +} + +- (NSString *)appInstallID { + return NSUserDefaults.standardUserDefaults.wmf_appInstallId; +} + +- (NSString *)sessionID { + return [[WMFEventLoggingService sharedInstance] sessionID]; +} + +- (NSString *)timestamp { + return [[NSDateFormatter wmf_rfc3339LocalTimeZoneFormatter] stringFromDate:[NSDate date]]; +} + +- (NSNumber *)isAnon { + // SINGLETONTODO + BOOL isAnon = !MWKDataStore.shared.authenticationManager.isLoggedIn; + return [NSNumber numberWithBool:isAnon]; +} + +/** + * Persistent random integer id used for sampling. + * + * @return integer sampling id + */ +- (NSInteger)getEventLogSamplingID { + NSNumber *samplingId = [[NSUserDefaults standardUserDefaults] objectForKey:@"EventLogSamplingID"]; + if (!samplingId) { + NSInteger intId = arc4random_uniform(UINT32_MAX); + [[NSUserDefaults standardUserDefaults] setInteger:intId forKey:@"EventLogSamplingID"]; + return intId; + } else { + return samplingId.integerValue; + } +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/ExploreCardViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ExploreCardViewController.swift new file mode 100644 index 0000000..17f802b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ExploreCardViewController.swift @@ -0,0 +1,715 @@ +import UIKit +import CocoaLumberjackSwift +import WMF + +protocol ExploreCardViewControllerDelegate: NestedCollectionViewContextMenuDelegate { + var saveButtonsController: SaveButtonsController { get } + var layoutCache: ColumnarCollectionViewControllerLayoutCache { get } + func exploreCardViewController(_ exploreCardViewController: ExploreCardViewController, didSelectItemAtIndexPath: IndexPath) +} + +struct ExploreSaveButtonUserInfo { + let indexPath: IndexPath + let kind: WMFContentGroupKind? + let midnightUTCDate: Date? +} + +class ExploreCardViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, CardContent, ColumnarCollectionViewLayoutDelegate { + + weak var delegate: (ExploreCardViewControllerDelegate & UIViewController)? + + lazy var layoutManager: ColumnarCollectionViewLayoutManager = { + return ColumnarCollectionViewLayoutManager(view: view, collectionView: collectionView) + }() + + lazy var layout: ColumnarCollectionViewLayout = { + return ColumnarCollectionViewLayout() + }() + + lazy var locationManager: LocationManagerProtocol = { + let locationManager = LocationManager() + locationManager.delegate = self + return locationManager + }() + + deinit { + if visibleLocationCellCount > 0 { + locationManager.stopMonitoringLocation() + } + } + + lazy var editController: CollectionViewEditController = { + let editController = CollectionViewEditController(collectionView: collectionView) + editController.delegate = self + return editController + }() + + var collectionView: UICollectionView { + return view as! UICollectionView + } + + var theme: Theme = Theme.standard + + var dataStore: MWKDataStore! + + // MARK: - View Lifecycle + + override func loadView() { + super.loadView() + self.view = UICollectionView(frame: .zero, collectionViewLayout: layout) + self.collectionView.delegate = self + self.collectionView.dataSource = self + } + + public func savedStateDidChangeForArticleWithKey(_ changedArticleKey: WMFInMemoryURLKey) { + for i in 0.. Void)? = nil) { + guard let delegateVC = delegate else { + super.present(viewControllerToPresent, animated: flag, completion: completion) + return + } + delegateVC.present(viewControllerToPresent, animated: flag, completion: completion) + } + + override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { + guard let delegateVC = delegate else { + super.dismiss(animated: flag, completion: completion) + return + } + delegateVC.dismiss(animated: flag, completion: completion) + } + + // MARK: - Data + private var visibleLocationCellCount: Int = 0 + + public var contentGroup: WMFContentGroup? { + willSet { + for indexPath in collectionView.indexPathsForVisibleItems { + guard let cell = collectionView.cellForItem(at: indexPath) else { + return + } + self.collectionView(collectionView, didEndDisplaying: cell, forItemAt: indexPath) + } + } + didSet { + reloadData() + } + } + + private func reloadData() { + contentHeightByWidth.removeAll() + if visibleLocationCellCount > 0 { + locationManager.stopMonitoringLocation() + } + visibleLocationCellCount = 0 + collectionView.reloadData() + } + + var contentHeightByWidth: [Int: CGFloat] = [:] + + public func contentHeight(forWidth width: CGFloat) -> CGFloat { + let widthInt = Int(round(width)) + if let cachedHeight = contentHeightByWidth[widthInt] { + return cachedHeight + } + let height = layout.layoutHeight(forWidth: width) + contentHeightByWidth[widthInt] = height + return height + } + + func numberOfSections(in collectionView: UICollectionView) -> Int { + guard contentGroup != nil else { + return 0 + } + return 1 + } + + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return numberOfItems + } + + var numberOfItems: Int { + return contentGroup?.countOfPreviewItems ?? 0 + } + + private func displayTypeAt(_ indexPath: IndexPath) -> WMFFeedDisplayType { + return contentGroup?.displayTypeForItem(at: indexPath.row) ?? .page + } + + private func resuseIdentifierFor(_ displayType: WMFFeedDisplayType) -> String { + switch displayType { + case .ranked: + return RankedArticleExploreCollectionViewCell.identifier + case .story: + return NewsExploreCollectionViewCell.identifier + case .event: + return OnThisDayExploreCollectionViewCell.identifier + case .photo: + return ImageCollectionViewCell.identifier + case .pageWithLocation: + return ArticleLocationExploreCollectionViewCell.identifier + case .pageWithLocationPlaceholder: + return ArticleLocationAuthorizationCollectionViewCell.identifier + case .page, .relatedPages, .mainPage, .compactList: + return ArticleRightAlignedImageExploreCollectionViewCell.identifier + case .announcement, .notification, .theme, .readingList: + return AnnouncementCollectionViewCell.identifier + default: + return ArticleFullWidthImageExploreCollectionViewCell.identifier + } + } + + func articleURL(at indexPath: IndexPath) -> URL? { + return contentGroup?.previewArticleURLForItemAtIndex(indexPath.row) + } + + private func article(at indexPath: IndexPath) -> WMFArticle? { + guard let url = articleURL(at: indexPath) else { + return nil + } + return dataStore.fetchArticle(with: url) + } + + // MARK: - cell configuration + + private func configureArticleCell(_ cell: UICollectionViewCell, forItemAt indexPath: IndexPath, with displayType: WMFFeedDisplayType, layoutOnly: Bool) { + guard let cell = cell as? ArticleCollectionViewCell, let articleURL = articleURL(at: indexPath), let article = dataStore?.fetchArticle(with: articleURL) else { + return + } + cell.configure(article: article, displayType: displayType, index: indexPath.row, theme: theme, layoutOnly: layoutOnly) + if let fullWidthCell = cell as? ArticleFullWidthImageCollectionViewCell { + fullWidthCell.saveButton.eventLoggingLabel = eventLoggingLabel + } + editController.configureSwipeableCell(cell, forItemAt: indexPath, layoutOnly: layoutOnly) + } + + private func configureLocationCell(_ cell: UICollectionViewCell, forItemAt indexPath: IndexPath, with displayType: WMFFeedDisplayType, layoutOnly: Bool) { + guard let cell = cell as? ArticleLocationExploreCollectionViewCell, let articleURL = articleURL(at: indexPath), let article = dataStore?.fetchArticle(with: articleURL) else { + return + } + cell.configure(article: article, displayType: displayType, index: indexPath.row, theme: theme, layoutOnly: layoutOnly) + if let authCell = cell as? ArticleLocationAuthorizationCollectionViewCell { + if locationManager.isAuthorized { + authCell.updateForLocationEnabled() + } else { + authCell.authorizeButton.setTitle(CommonStrings.localizedEnableLocationButtonTitle, for: .normal) + authCell.authorizationDelegate = self + } + authCell.authorizeDescriptionLabel.text = CommonStrings.localizedEnableLocationDescription + } + guard !layoutOnly else { + cell.configureForUnknownDistance() + return + } + cell.articleLocation = article.location + if locationManager.isAuthorized { + locationManager.startMonitoringLocation() + cell.update(userLocation: locationManager.location, heading: locationManager.heading) + } else { + cell.configureForUnknownDistance() + } + editController.configureSwipeableCell(cell, forItemAt: indexPath, layoutOnly: layoutOnly) + } + + private func configureNewsCell(_ cell: UICollectionViewCell, layoutOnly: Bool) { + guard let cell = cell as? NewsExploreCollectionViewCell, let story = contentGroup?.contentPreview as? WMFFeedNewsStory else { + return + } + cell.configure(with: story, dataStore: dataStore, showArticles: false, theme: theme, layoutOnly: layoutOnly) + cell.selectionDelegate = self + } + + private func configureOnThisDayCell(_ cell: UICollectionViewCell, forItemAt indexPath: IndexPath, layoutOnly: Bool) { + let index = indexPath.row + guard let cell = cell as? OnThisDayExploreCollectionViewCell, let events = contentGroup?.contentPreview as? [WMFFeedOnThisDayEvent], !events.isEmpty, events.indices.contains(index) else { + return + } + let event = events[index] + cell.configure(with: event, isFirst: events.indices.first == index, isLast: events.indices.last == index, dataStore: dataStore, theme: theme, layoutOnly: layoutOnly) + cell.selectionDelegate = self + } + + var footerText: String? { + if contentGroup?.contentGroupKind == .onThisDay, + collectionView.numberOfSections == 1, + let eventsCount = contentGroup?.countOfFullContent?.intValue { + let otherEventsCount = eventsCount - collectionView.numberOfItems(inSection: 0) + if otherEventsCount > 0 { + return CommonStrings.onThisDayFooterWith(with: otherEventsCount) + } else { + return contentGroup?.footerText + } + } else { + return contentGroup?.footerText + } + } + + private func configurePhotoCell(_ cell: UICollectionViewCell, layoutOnly: Bool) { + guard let cell = cell as? ImageCollectionViewCell, let imageInfo = contentGroup?.contentPreview as? WMFFeedImage else { + return + } + if !layoutOnly, let imageURL = contentGroup?.imageURLsCompatibleWithTraitCollection(traitCollection, dataStore: dataStore, viewSize: view.bounds.size)?.first { + cell.imageView.wmf_setImage(with: imageURL, detectFaces: true, onGPU: true, failure: WMFIgnoreErrorHandler, success: WMFIgnoreSuccessHandler) + } + if !imageInfo.imageDescription.isEmpty { + cell.captionIsRTL = imageInfo.imageDescriptionIsRTL + cell.caption = imageInfo.imageDescription.wmf_stringByRemovingHTML() + } else { + cell.caption = imageInfo.canonicalPageTitle + } + cell.apply(theme: theme) + } + + private func configureAnnouncementCell(_ cell: UICollectionViewCell, displayType: WMFFeedDisplayType, layoutOnly: Bool) { + guard let cell = cell as? AnnouncementCollectionViewCell else { + return + } + switch displayType { + case .announcement: + guard + let contentGroup = contentGroup, + let announcement = contentGroup.contentPreview as? WMFAnnouncement + else { + return + } + if let imageURL = announcement.imageURL { + cell.isImageViewHidden = false + if !layoutOnly { + cell.imageView.wmf_setImage(with: imageURL, detectFaces: false, onGPU: false, failure: WMFIgnoreErrorHandler, success: WMFIgnoreSuccessHandler) + } + } else { + cell.isImageViewHidden = true + } + cell.isUrgent = announcement.announcementType == .fundraising + cell.messageHTML = announcement.text + cell.actionButton.setTitle(announcement.actionTitle, for: .normal) + cell.captionHTML = announcement.captionHTML + cell.dismissButtonTitle = announcement.negativeText + if let imageViewHeight = announcement.imageHeight?.doubleValue, imageViewHeight > 0 { + cell.imageViewDimension = CGFloat(imageViewHeight) + } + case .notification: + cell.isImageViewHidden = false + cell.imageView.image = UIImage(named: "feed-card-notification") + cell.imageViewDimension = cell.imageView.image?.size.height ?? 0 + cell.messageHTML = WMFLocalizedString("notifications-center-feed-news-notification-text", value: "Editing notifications for all Wikimedia projects are now available through the app. Opt in to push notifications to keep up to date with your messages on Wikipedia while on the go.", comment: "Text shown to users to notify them that it is now possible to get push notifications for all Wikimedia projects through the app") + cell.actionButton.setTitle(WMFLocalizedString("notifications-center-feed-news-notification-button-text", value: "Turn on push notifications", comment: "Text for button to turn on push notifications"), for:.normal) + cell.dismissButton.setTitle(WMFLocalizedString("notifications-center-feed-news-notification-dismiss-button-text", value: "Not now", comment: "Text for the dismiss button on the explore feed notifications card"), for: .normal) + case .theme: + cell.isImageViewHidden = false + cell.imageView.image = UIImage(named: "feed-card-themes") + cell.imageViewDimension = cell.imageView.image?.size.height ?? 0 + cell.messageHTML = WMFLocalizedString("home-themes-prompt", value: "Adjust your Reading preferences including text size and theme from the article tool bar or in your user settings for a more comfortable reading experience.", comment: "Description on feed card that describes how to adjust reading preferences.") + cell.actionButton.setTitle(WMFLocalizedString("home-themes-action-title", value: "Manage preferences", comment: "Action on the feed card that describes the theme feature. Takes the user to manage theme preferences."), for:.normal) + case .readingList: + cell.isImageViewHidden = false + cell.imageView.image = UIImage(named: "feed-card-reading-list") + cell.imageViewDimension = cell.imageView.image?.size.height ?? 0 + cell.messageHTML = WMFLocalizedString("home-reading-list-prompt", value: "Your saved articles can now be organized into reading lists and synced across devices. Log in to allow your reading lists to be saved to your user preferences.", comment: "Description on feed card that describes reading lists.") + cell.actionButton.setTitle(CommonStrings.readingListLoginButtonTitle, for:.normal) + default: + break + } + cell.apply(theme: theme) + cell.delegate = self + } + + private func configure(cell: UICollectionViewCell, forItemAt indexPath: IndexPath, with displayType: WMFFeedDisplayType, layoutOnly: Bool) { + switch displayType { + case .pageWithLocation, .pageWithLocationPlaceholder: + configureLocationCell(cell, forItemAt: indexPath, with: displayType, layoutOnly: layoutOnly) + case .photo: + configurePhotoCell(cell, layoutOnly: layoutOnly) + case .story: + configureNewsCell(cell, layoutOnly: layoutOnly) + case .event: + configureOnThisDayCell(cell, forItemAt: indexPath, layoutOnly: layoutOnly) + case .theme, .notification, .announcement, .readingList: + configureAnnouncementCell(cell, displayType: displayType, layoutOnly: layoutOnly) + default: + configureArticleCell(cell, forItemAt: indexPath, with: displayType, layoutOnly: layoutOnly) + } + cell.layoutMargins = layout.itemLayoutMargins + } + + func updateLocationCells() { + let userLocation = locationManager.location + let heading = locationManager.heading + for cell in collectionView.visibleCells { + guard let cell = cell as? ArticleLocationExploreCollectionViewCell else { + return + } + cell.update(userLocation: userLocation, heading: heading) + } + } + + // MARK: - UICollectionViewDataSource + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let displayType = displayTypeAt(indexPath) + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: resuseIdentifierFor(displayType), for: indexPath) + configure(cell: cell, forItemAt: indexPath, with: displayType, layoutOnly: false) + return cell + } + + func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + if let cell = cell as? ArticleFullWidthImageCollectionViewCell, let article = article(at: indexPath) { + delegate?.saveButtonsController.willDisplay(saveButton: cell.saveButton, for: article, with: ExploreSaveButtonUserInfo(indexPath: indexPath, kind: contentGroup?.contentGroupKind, midnightUTCDate: contentGroup?.midnightUTCDate)) + } + if cell is ArticleLocationExploreCollectionViewCell { + visibleLocationCellCount += 1 + if locationManager.isAuthorized { + locationManager.startMonitoringLocation() + } + } + } + + func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + if let cell = cell as? ArticleFullWidthImageCollectionViewCell, let article = article(at: indexPath) { + delegate?.saveButtonsController.didEndDisplaying(saveButton: cell.saveButton, for: article) + } + if cell is ArticleLocationExploreCollectionViewCell { + visibleLocationCellCount -= 1 + if visibleLocationCellCount == 0 { + locationManager.stopMonitoringLocation() + } + } + editController.deconfigureSwipeableCell(cell, forItemAt: indexPath) + } + + // MARK: - UICollectionViewDelegate + + func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { + return contentGroup?.isSelectable ?? false + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + delegate?.exploreCardViewController(self, didSelectItemAtIndexPath: indexPath) + } + + // MARK: - ColumnarCollectionViewLayoutDelegate + + func collectionView(_ collectionView: UICollectionView, estimatedHeightForItemAt indexPath: IndexPath, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + let displayType = displayTypeAt(indexPath) + let reuseIdentifier = resuseIdentifierFor(displayType) + let key: String? + let articleKey: WMFInMemoryURLKey? = self.article(at: indexPath)?.inMemoryKey + let groupKey: WMFInMemoryURLKey? = contentGroup?.inMemoryKey + if displayType == .story || displayType == .event, let contentGroupKey = contentGroup?.inMemoryKey { + key = "\(contentGroupKey.userInfoString)-\(indexPath.row)" + } else { + key = articleKey?.userInfoString ?? groupKey?.userInfoString + } + let userInfo = "\(key ?? "")-\(displayType.rawValue)" + if let height = delegate?.layoutCache.cachedHeightForCellWithIdentifier(reuseIdentifier, columnWidth: columnWidth, userInfo: userInfo) { + return ColumnarCollectionViewLayoutHeightEstimate(precalculated: true, height: height) + } + var estimate = ColumnarCollectionViewLayoutHeightEstimate(precalculated: false, height: 100) + guard let placeholderCell = layoutManager.placeholder(forCellWithReuseIdentifier: reuseIdentifier) as? CollectionViewCell else { + return estimate + } + configure(cell: placeholderCell, forItemAt: indexPath, with: displayType, layoutOnly: true) + let height = placeholderCell.sizeThatFits(CGSize(width: columnWidth, height: UIView.noIntrinsicMetric), apply: false).height + delegate?.layoutCache.setHeight(height, forCellWithIdentifier: reuseIdentifier, columnWidth: columnWidth, groupKey: groupKey, articleKey: articleKey, userInfo: userInfo) + estimate.height = height + estimate.precalculated = true + return estimate + } + + func collectionView(_ collectionView: UICollectionView, estimatedHeightForHeaderInSection section: Int, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + return ColumnarCollectionViewLayoutHeightEstimate(precalculated: true, height: 0) + } + + func collectionView(_ collectionView: UICollectionView, estimatedHeightForFooterInSection section: Int, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + return ColumnarCollectionViewLayoutHeightEstimate(precalculated: true, height: 0) + } + + func collectionView(_ collectionView: UICollectionView, prefersWiderColumnForSectionAt index: UInt) -> Bool { + return true + } + + func collectionView(_ collectionView: UICollectionView, shouldShowFooterForSection section: Int) -> Bool { + return false + } + + func metrics(with size: CGSize, readableWidth: CGFloat, layoutMargins: UIEdgeInsets) -> ColumnarCollectionViewLayoutMetrics { + let kind = contentGroup?.contentGroupKind ?? .unknown + let itemLayoutMargins = ColumnarCollectionViewLayoutMetrics.defaultItemLayoutMargins + let layoutMargins: UIEdgeInsets + + // add additional spacing around the section + switch kind { + case .location: + layoutMargins = UIEdgeInsets(top: 18 - itemLayoutMargins.top, left: 0, bottom: 18 - itemLayoutMargins.bottom, right: 0) + case .locationPlaceholder: + layoutMargins = UIEdgeInsets(top: 22 - itemLayoutMargins.top, left: 0, bottom: 10 - itemLayoutMargins.bottom, right: 0) + case .topRead: + layoutMargins = UIEdgeInsets(top: 22 - itemLayoutMargins.top, left: 0, bottom: 22 - itemLayoutMargins.bottom, right: 0) + case .onThisDay: + layoutMargins = UIEdgeInsets(top: 22 - itemLayoutMargins.top, left: 0, bottom: 20 - itemLayoutMargins.bottom, right: 0) + case .relatedPages: + layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 25 - itemLayoutMargins.bottom, right: 0) + default: + layoutMargins = .zero + } + + return ColumnarCollectionViewLayoutMetrics.exploreCardMetrics(with: size, readableWidth: size.width, layoutMargins: layoutMargins) + } + + func userDidTapTurnOnNotifications() { + let pushSettingsVC = PushNotificationsSettingsViewController.init(authenticationManager: self.dataStore.authenticationManager, notificationsController: self.dataStore.notificationsController) + pushSettingsVC.apply(theme: self.theme) + self.navigationController?.pushViewController(pushSettingsVC, animated: true) + } + +} + +extension ExploreCardViewController: ActionDelegate, ShareableArticlesProvider { + func willPerformAction(_ action: Action) -> Bool { + guard let article = article(at: action.indexPath) else { + return false + } + guard action.type == .unsave else { + return self.editController.didPerformAction(action) + } + let alertController = ReadingListsAlertController() + let cancel = ReadingListsAlertActionType.cancel.action() + let delete = ReadingListsAlertActionType.unsave.action { _ = self.editController.didPerformAction(action) } + let actions = [cancel, delete] + alertController.showAlertIfNeeded(presenter: self, for: [article], with: actions) { showed in + if !showed { + _ = self.editController.didPerformAction(action) + } + } + return true + } + + func availableActions(at indexPath: IndexPath) -> [Action] { + guard let article = article(at: indexPath) else { + return [] + } + + var actions: [Action] = [] + + if article.isAnyVariantSaved { + actions.append(ActionType.unsave.action(with: self, indexPath: indexPath)) + } else { + actions.append(ActionType.save.action(with: self, indexPath: indexPath)) + } + + actions.append(ActionType.share.action(with: self, indexPath: indexPath)) + return actions + } + + func didPerformAction(_ action: Action) -> Bool { + let indexPath = action.indexPath + let sourceView = collectionView.cellForItem(at: indexPath) + switch action.type { + case .save: + if let articleURL = articleURL(at: indexPath) { + dataStore.savedPageList.addSavedPage(with: articleURL) + UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: CommonStrings.accessibilitySavedNotification) + ReadingListsFunnel.shared.logSaveInFeed(context: FeedFunnelContext(contentGroup), articleURL: articleURL, index: action.indexPath.item) + return true + } + case .unsave: + if let articleURL = articleURL(at: indexPath) { + dataStore.savedPageList.removeEntry(with: articleURL) + UIAccessibility.post(notification: UIAccessibility.Notification.announcement, argument: CommonStrings.accessibilityUnsavedNotification) + ReadingListsFunnel.shared.logUnsaveInFeed(context: FeedFunnelContext(contentGroup), articleURL: articleURL, index: action.indexPath.item) + return true + } + case .share: + FeedFunnel.shared.logFeedShareTapped(for: FeedFunnelContext(contentGroup), index: indexPath.item) + return share(article: article(at: indexPath), articleURL: articleURL(at: indexPath), at: indexPath, dataStore: dataStore, theme: theme, eventLoggingCategory: eventLoggingCategory, eventLoggingLabel: eventLoggingLabel, sourceView: sourceView) + default: + return false + } + return false + } +} + +extension ExploreCardViewController: SideScrollingCollectionViewCellDelegate { + func sideScrollingCollectionViewCell(_ sideScrollingCollectionViewCell: SideScrollingCollectionViewCell, didSelectArticleWithURL articleURL: URL, at indexPath: IndexPath) { + navigate(to: articleURL) + } +} + +extension ExploreCardViewController: AnnouncementCollectionViewCellDelegate { + func dismissAnnouncementCell(_ cell: AnnouncementCollectionViewCell) { + contentGroup?.markDismissed() + contentGroup?.updateVisibilityForUserIsLogged(in: dataStore.session.isAuthenticated) + do { + try dataStore.save() + } catch let error { + DDLogError("Error saving after cell dismissal: \(error)") + } + } + + func announcementCellDidTapDismiss(_ cell: AnnouncementCollectionViewCell) { + dismissAnnouncementCell(cell) + } + + func announcementCellDidTapActionButton(_ cell: AnnouncementCollectionViewCell) { + guard let kind = contentGroup?.contentGroupKind else { + return + } + switch kind { + case .theme: + NotificationCenter.default.post(name: .WMFNavigateToActivity, object: NSUserActivity.wmf_appearanceSettings()) + dismissAnnouncementCell(cell) + case .readingList: + wmf_showLoginViewController(theme: theme) + LoginFunnel.shared.logLoginStartInFeed() + dismissAnnouncementCell(cell) + case .notification: + userDidTapTurnOnNotifications() + dismissAnnouncementCell(cell) + default: + guard let announcement = contentGroup?.contentPreview as? WMFAnnouncement, + let url = announcement.actionURL else { + return + } + navigate(to: url, useSafari: true) + dismissAnnouncementCell(cell) + } + } + + func announcementCell(_ cell: AnnouncementCollectionViewCell, didTapLinkURL linkURL: URL) { + navigate(to: linkURL, useSafari: true) + } +} + +extension ExploreCardViewController: ArticlePreviewingDelegate { + func readMoreArticlePreviewActionSelected(with articleController: ArticleViewController) { + articleController.wmf_removePeekableChildViewControllers() + push(articleController, animated: true) + } + + func saveArticlePreviewActionSelected(with articleController: ArticleViewController, didSave: Bool, articleURL: URL) { + + } + + func shareArticlePreviewActionSelected(with articleController: ArticleViewController, shareActivityController: UIActivityViewController) { + articleController.wmf_removePeekableChildViewControllers() + present(shareActivityController, animated: true, completion: nil) + } + + func viewOnMapArticlePreviewActionSelected(with articleController: ArticleViewController) { + articleController.wmf_removePeekableChildViewControllers() + let placesURL = NSUserActivity.wmf_URLForActivity(of: .places, withArticleURL: articleController.articleURL) + UIApplication.shared.open(placesURL) + } +} + +extension ExploreCardViewController: ArticleLocationAuthorizationCollectionViewCellDelegate { + func articleLocationAuthorizationCollectionViewCellDidTapAuthorize(_ cell: ArticleLocationAuthorizationCollectionViewCell) { + UserDefaults.standard.wmf_setExploreDidPromptForLocationAuthorization(true) + if locationManager.authorizationStatus == .notDetermined { + locationManager.startMonitoringLocation() + return + } + UIApplication.shared.wmf_openAppSpecificSystemSettings() + } +} + +extension ExploreCardViewController: LocationManagerDelegate { + func locationManager(_ locationManager: LocationManagerProtocol, didUpdate location: CLLocation) { + updateLocationCells() + } + + func locationManager(_ locationManager: LocationManagerProtocol, didUpdate heading: CLHeading) { + updateLocationCells() + } + + func locationManager(_ locationManager: LocationManagerProtocol, didUpdateAuthorized authorized: Bool) { + UserDefaults.standard.wmf_setLocationAuthorized(authorized) + + for cell in collectionView.visibleCells { + guard let cell = cell as? ArticleLocationAuthorizationCollectionViewCell, locationManager.isAuthorized else { + return + } + cell.updateForLocationEnabled() + } + dataStore.feedContentController.updateContentSource(WMFNearbyContentSource.self, force: false, completion: nil) + } +} + +extension ExploreCardViewController: Themeable { + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + collectionView.backgroundColor = theme.colors.cardBackground + view.backgroundColor = theme.colors.cardBackground + } +} + +extension ExploreCardViewController: EventLoggingEventValuesProviding { + var eventLoggingLabel: EventLoggingLabel? { + return contentGroup?.eventLoggingLabel + } + + var eventLoggingCategory: EventLoggingCategory { + return EventLoggingCategory.feed + } +} + +// MARK: - Context Menu +extension ExploreCardViewController { + public func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { + guard indexPath.item < numberOfItems else { + return nil + } + + return delegate?.contextMenu(with: contentGroup, for: nil, at: indexPath.item) + } + + public func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { + delegate?.willCommitPreview(with: animator) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ExploreFeedSettingsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ExploreFeedSettingsViewController.swift new file mode 100644 index 0000000..ad66566 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ExploreFeedSettingsViewController.swift @@ -0,0 +1,336 @@ +import UIKit + +private class FeedCard: ExploreFeedSettingsItem { + let contentGroupKind: WMFContentGroupKind + let title: String + var subtitle: String? + let disclosureType: WMFSettingsMenuItemDisclosureType + var disclosureText: String? = nil + let iconName: String? + let iconColor: UIColor? + let iconBackgroundColor: UIColor? + var controlTag: Int = 0 + var isOn: Bool = true + + init(contentGroupKind: WMFContentGroupKind, displayType: ExploreFeedSettingsDisplayType) { + self.contentGroupKind = contentGroupKind + + var singleLanguageDescription: String? + + switch contentGroupKind { + case .news: + title = CommonStrings.inTheNewsTitle + singleLanguageDescription = WMFLocalizedString("explore-feed-preferences-in-the-news-description", value: "Articles about current events", comment: "Description of In the news section of Explore feed") + iconName = "in-the-news-mini" + iconColor = .gray400 + iconBackgroundColor = .gray200 + case .onThisDay: + title = CommonStrings.onThisDayTitle + singleLanguageDescription = WMFLocalizedString("explore-feed-preferences-on-this-day-description", value: "Events in history on this day", comment: "Description of On this day section of Explore feed") + iconName = "on-this-day-mini" + iconColor = .blue600 + iconBackgroundColor = .blue100 + case .featuredArticle: + title = CommonStrings.featuredArticleTitle + singleLanguageDescription = WMFLocalizedString("explore-feed-preferences-featured-article-description", value: "Daily featured article on Wikipedia", comment: "Description of Featured article section of Explore feed") + iconName = "featured-mini" + iconColor = .yellow600 + iconBackgroundColor = .yellow600.withAlphaComponent(0.3) + case .topRead: + title = CommonStrings.topReadTitle + singleLanguageDescription = WMFLocalizedString("explore-feed-preferences-top-read-description", value: "Daily most read articles", comment: "Description of Top read section of Explore feed") + iconName = "trending-mini" + iconColor = .blue600 + iconBackgroundColor = .blue100 + case .location: + fallthrough + case .locationPlaceholder: + title = CommonStrings.placesTabTitle + singleLanguageDescription = WMFLocalizedString("explore-feed-preferences-places-description", value: "Wikipedia articles near your location", comment: "Description of Places section of Explore feed") + iconName = "nearby-mini" + iconColor = .green600 + iconBackgroundColor = .green100 + case .random: + title = CommonStrings.randomizerTitle + singleLanguageDescription = WMFLocalizedString("explore-feed-preferences-randomizer-description", value: "Generate random articles to read", comment: "Description of Randomizer section of Explore feed") + iconName = "random-mini" + iconColor = .red600 + iconBackgroundColor = .red100 + case .pictureOfTheDay: + title = CommonStrings.pictureOfTheDayTitle + singleLanguageDescription = WMFLocalizedString("explore-feed-preferences-potd-description", value: "Daily featured image from Commons", comment: "Description of Picture of the day section of Explore feed") + iconName = "potd-mini" + iconColor = .purple600 + iconBackgroundColor = .purple600.withAlphaComponent(0.3) + case .continueReading: + title = CommonStrings.continueReadingTitle + singleLanguageDescription = WMFLocalizedString("explore-feed-preferences-continue-reading-description", value: "Quick link back to reading an open article", comment: "Description of Continue reading section of Explore feed") + iconName = "today-mini" + iconColor = .gray400 + iconBackgroundColor = .gray200 + case .relatedPages: + title = CommonStrings.relatedPagesTitle + singleLanguageDescription = WMFLocalizedString("explore-feed-preferences-related-pages-description", value: "Suggestions based on reading history", comment: "Description of Related pages section of Explore feed") + iconName = "recent-mini" + iconColor = .gray400 + iconBackgroundColor = .gray200 + default: + assertionFailure("Group of kind \(contentGroupKind) is not customizable") + title = "" + iconName = nil + iconColor = nil + iconBackgroundColor = nil + } + + if displayType == .singleLanguage { + subtitle = singleLanguageDescription + disclosureType = .switch + controlTag = Int(contentGroupKind.rawValue) + isOn = contentGroupKind.isInFeed + } else { + disclosureType = .viewControllerWithDisclosureText + disclosureText = multipleLanguagesDisclosureText(for: contentGroupKind) + subtitle = multipleLanguagesSubtitle(for: contentGroupKind) + } + } + + private func multipleLanguagesDisclosureText(for contentGroupKind: WMFContentGroupKind) -> String { + guard contentGroupKind.isGlobal else { + let preferredLanguages = MWKDataStore.shared().languageLinkController.preferredLanguages + let contentLanguageCodes = contentGroupKind.contentLanguageCodes + switch contentLanguageCodes.count { + case preferredLanguages.count: + return CommonStrings.onAllTitle + case 1...: + return CommonStrings.onTitle(contentLanguageCodes.count) + default: + return CommonStrings.offTitle + } + } + if contentGroupKind.isInFeed { + return CommonStrings.onTitle + } else { + return CommonStrings.offTitle + } + } + + func updateIsOn(for displayType: ExploreFeedSettingsDisplayType) { + guard displayType == .singleLanguage else { + return + } + isOn = contentGroupKind.isInFeed + } + + func updateDisclosureText(for displayType: ExploreFeedSettingsDisplayType) { + guard displayType == .multipleLanguages else { + return + } + disclosureText = multipleLanguagesDisclosureText(for: contentGroupKind) + } + + private func multipleLanguagesSubtitle(for contentGroupKind: WMFContentGroupKind) -> String { + if contentGroupKind.isGlobal { + return WMFLocalizedString("explore-feed-preferences-global-cards-subtitle", value: "Not language specific", comment: "Subtitle describing non-language specific feed cards") + } else { + let contentLanguageCodes = contentGroupKind.contentLanguageCodes + let preferredContentLanguageCodes = MWKDataStore.shared().languageLinkController.preferredLanguages.map { $0.contentLanguageCode } + let filteredLanguages = preferredContentLanguageCodes.filter { contentLanguageCodes.contains($0) } + return filteredLanguages.joined(separator: ", ").uppercased() + } + } + + func updateSubtitle(for displayType: ExploreFeedSettingsDisplayType) { + guard displayType == .multipleLanguages else { + return + } + subtitle = multipleLanguagesSubtitle(for: contentGroupKind) + } +} + +@objc(WMFExploreFeedSettingsViewController) +class ExploreFeedSettingsViewController: BaseExploreFeedSettingsViewController { + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + tableView.reloadData() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + if updateFeedBeforeViewDisappears { + feedContentController?.updateFeedSourcesUserInitiated(true) + } + } + + public var showCloseButton = false { + didSet { + if showCloseButton { + navigationItem.leftBarButtonItem = UIBarButtonItem.wmf_buttonType(.X, target: self, action: #selector(closeButtonPressed)) + } else { + navigationItem.leftBarButtonItem = nil + } + } + } + + override func viewDidLoad() { + super.viewDidLoad() + title = CommonStrings.exploreFeedTitle + assert(!preferredLanguages.isEmpty) + displayType = preferredLanguages.count == 1 ? .singleLanguage : .multipleLanguages + } + + @objc private func closeButtonPressed() { + dismiss(animated: true) + } + + // MARK: Items + + private lazy var feedCards: [FeedCard] = { + let inTheNews = FeedCard(contentGroupKind: .news, displayType: displayType) + let onThisDay = FeedCard(contentGroupKind: .onThisDay, displayType: displayType) + let featuredArticle = FeedCard(contentGroupKind: .featuredArticle, displayType: displayType) + let topRead = FeedCard(contentGroupKind: .topRead, displayType: displayType) + let places = FeedCard(contentGroupKind: .location, displayType: displayType) + let randomizer = FeedCard(contentGroupKind: .random, displayType: displayType) + let pictureOfTheDay = FeedCard(contentGroupKind: .pictureOfTheDay, displayType: displayType) + let continueReading = FeedCard(contentGroupKind: .continueReading, displayType: displayType) + let relatedPages = FeedCard(contentGroupKind: .relatedPages, displayType: displayType) + return [inTheNews, onThisDay, featuredArticle, topRead, places, randomizer, pictureOfTheDay, continueReading, relatedPages] + }() + + private lazy var globalCards: ExploreFeedSettingsGlobalCards = { + return ExploreFeedSettingsGlobalCards() + }() + + // MARK: Sections + + let togglingFeedCardsFooterText = WMFLocalizedString("explore-feed-preferences-languages-footer-text", value: "Hiding all Explore feed cards in all of your languages will turn off the Explore tab.", comment: "Text for explaining the effects of hiding all feed cards") + + private lazy var customizationSection: ExploreFeedSettingsSection = { + return ExploreFeedSettingsSection(headerTitle: WMFLocalizedString("explore-feed-preferences-customize-explore-feed", value: "Customize the Explore feed", comment: "Title of the Settings section that allows users to customize the Explore feed"), footerTitle: String.localizedStringWithFormat("%@ %@", WMFLocalizedString("explore-feed-preferences-customize-explore-feed-footer-text", value: "Hiding a card type will stop this card type from appearing in the Explore feed.", comment: "Text for explaining the effects of hiding feed cards"), togglingFeedCardsFooterText), items: feedCards) + }() + + private lazy var mainSection: ExploreFeedSettingsSection = { + return ExploreFeedSettingsSection(headerTitle: nil, footerTitle: WMFLocalizedString("explore-feed-preferences-turn-off-feed-disclosure", value: "Turning off the Explore tab will replace the Explore tab with a Settings tab.", comment: "Text for explaining the effects of turning off the Explore tab"), items: [ExploreFeedSettingsPrimary(for: .entireFeed)]) + }() + + private lazy var languagesSection: ExploreFeedSettingsSection? = { + guard displayType == .multipleLanguages else { + return nil + } + var items: [ExploreFeedSettingsItem] = languages + items.append(globalCards) + return ExploreFeedSettingsSection(headerTitle: CommonStrings.languagesTitle, footerTitle: togglingFeedCardsFooterText, items: items) + }() + + override var sections: [ExploreFeedSettingsSection] { + guard displayType == .multipleLanguages else { + return [customizationSection, mainSection] + } + guard let languagesSection = languagesSection else { + return [customizationSection, mainSection] + } + return [customizationSection, languagesSection, mainSection] + } + + // MARK: Toggling Explore feed + + private func turnOnExploreAlertController(turnedOn: @escaping () -> Void, cancelled: @escaping () -> Void) -> UIAlertController { + let alertController = UIAlertController(title: CommonStrings.turnOnExploreTabTitle, message: WMFLocalizedString("explore-feed-preferences-turn-on-explore-tab-message", value: "This will replace the Settings tab with the Explore tab, you can access Settings from the top of the Explore tab by tapping on the gear icon", comment: "Message for alert that allows users to turn on the Explore tab"), preferredStyle: .alert) + let turnOnExplore = UIAlertAction(title: CommonStrings.turnOnExploreActionTitle, style: .default, handler: { _ in + turnedOn() + }) + let cancel = UIAlertAction(title: CommonStrings.cancelActionTitle, style: .cancel, handler: { _ in + cancelled() + }) + alertController.addAction(turnOnExplore) + alertController.addAction(cancel) + return alertController + } + + private func turnOffExploreAlertController(turnedOff: @escaping () -> Void, cancelled: @escaping () -> Void) -> UIAlertController { + let alertController = UIAlertController(title: WMFLocalizedString("explore-feed-preferences-turn-off-explore-tab-title", value: "Turn off the Explore tab?", comment: "Title for alert that allows users to turn off the Explore tab"), message: WMFLocalizedString("explore-feed-preferences-turn-off-explore-tab-message", value: "The Explore tab can be turned back on in Explore feed settings", comment: "Message for alert that allows users to turn off the Explore tab"), preferredStyle: .alert) + let turnOffExplore = UIAlertAction(title: WMFLocalizedString("explore-feed-preferences-turn-off-explore-tab-action-title", value: "Turn off Explore", comment: "Title for action that allows users to turn off the Explore tab"), style: .destructive, handler: { _ in + turnedOff() + }) + let cancel = UIAlertAction(title: CommonStrings.cancelActionTitle, style: .cancel, handler: { _ in + cancelled() + }) + alertController.addAction(turnOffExplore) + alertController.addAction(cancel) + return alertController + } +} + +// MARK: - UITableViewDelegate + +extension ExploreFeedSettingsViewController { + @objc func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + defer { + tableView.deselectRow(at: indexPath, animated: true) + } + guard displayType == .multipleLanguages else { + return + } + let item = getItem(at: indexPath) + guard let feedCard = item as? FeedCard else { + return + } + let feedCardSettingsViewController = FeedCardSettingsViewController() + feedCardSettingsViewController.configure(with: item.title, dataStore: dataStore, contentGroupKind: feedCard.contentGroupKind, theme: theme) + navigationController?.pushViewController(feedCardSettingsViewController, animated: true) + } +} + +// MARK: - WMFSettingsTableViewCellDelegate + +extension ExploreFeedSettingsViewController { + + override func settingsTableViewCell(_ settingsTableViewCell: WMFSettingsTableViewCell!, didToggleDisclosureSwitch sender: UISwitch!) { + activeSwitch = sender + let controlTag = sender.tag + guard let feedContentController = feedContentController else { + assertionFailure("feedContentController is nil") + return + } + guard controlTag != -1 else { // main switch + if sender.isOn { + present(turnOnExploreAlertController(turnedOn: { + self.dataStore?.feedContentController.toggleAllContentGroupKinds(true, updateFeed: false) + UserDefaults.standard.defaultTabType = .explore + }, cancelled: { + sender.setOn(false, animated: true) + }), animated: true) + } else { + present(turnOffExploreAlertController(turnedOff: { + self.dataStore?.feedContentController.toggleAllContentGroupKinds(false, updateFeed: false) + UserDefaults.standard.defaultTabType = .settings + }, cancelled: { + sender.setOn(true, animated: true) + }), animated: true) + } + return + } + guard controlTag != -2 else { // global cards + feedContentController.toggleGlobalContentGroupKinds(sender.isOn, updateFeed: false) + return + } + if displayType == .singleLanguage { + guard let contentGroupKind = WMFContentGroupKind(rawValue: Int32(controlTag)) else { + assertionFailure("No content group kind for given control tag") + return + } + guard contentGroupKind.isCustomizable || contentGroupKind.isGlobal else { + assertionFailure("Content group kind \(contentGroupKind) is not customizable nor global") + return + } + feedContentController.toggleContentGroup(of: contentGroupKind, isOn: sender.isOn, updateFeed: false) + } else { + guard let language = languages.first(where: { $0.controlTag == controlTag }) else { + assertionFailure("No language for given control tag") + return + } + feedContentController.toggleContent(forSiteURL: language.siteURL, isOn: sender.isOn, waitForCallbackFromCoordinator: true, updateFeed: false) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ExploreViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ExploreViewController.swift new file mode 100644 index 0000000..3dba3e7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ExploreViewController.swift @@ -0,0 +1,1104 @@ +import UIKit +import WMF +import CocoaLumberjackSwift + +class ExploreViewController: ColumnarCollectionViewController, ExploreCardViewControllerDelegate, UISearchBarDelegate, CollectionViewUpdaterDelegate, ImageScaleTransitionProviding, DetailTransitionSourceProviding, EventLoggingEventValuesProviding { + + public var presentedContentGroupKey: String? + public var shouldRestoreScrollPosition = false + + @objc public weak var notificationsCenterPresentationDelegate: NotificationsCenterPresentationDelegate? + @objc public weak var settingsPresentationDelegate: SettingsPresentationDelegate? + + // MARK: - UIViewController + + override func viewDidLoad() { + super.viewDidLoad() + layoutManager.register(ExploreCardCollectionViewCell.self, forCellWithReuseIdentifier: ExploreCardCollectionViewCell.identifier, addPlaceholder: true) + + navigationItem.titleView = titleView + navigationBar.addUnderNavigationBarView(searchBarContainerView) + navigationBar.isUnderBarViewHidingEnabled = true + navigationBar.displayType = dataStore.authenticationManager.isLoggedIn ? .centeredLargeTitle : .largeTitle + navigationBar.shouldTransformUnderBarViewWithBar = true + navigationBar.isShadowHidingEnabled = true + + updateNotificationsCenterButton() + updateSettingsButton() + updateNavigationBarVisibility() + + isRefreshControlEnabled = true + collectionView.refreshControl?.layer.zPosition = 0 + + title = CommonStrings.exploreTabTitle + + NotificationCenter.default.addObserver(self, selector: #selector(exploreFeedPreferencesDidSave(_:)), name: NSNotification.Name.WMFExploreFeedPreferencesDidSave, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(articleDidChange(_:)), name: NSNotification.Name.WMFArticleUpdated, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(articleDeleted(_:)), name: NSNotification.Name.WMFArticleDeleted, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(pushNotificationBannerDidDisplayInForeground(_:)), name: .pushNotificationBannerDidDisplayInForeground, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(viewContextDidReset(_:)), name: NSNotification.Name.WMFViewContextDidReset, object: nil) + +#if UI_TEST + if UserDefaults.standard.wmf_isFastlaneSnapshotInProgress() { + collectionView.decelerationRate = .fast + } +#endif + } + + @objc var isGranularUpdatingEnabled: Bool = true { + didSet { + collectionViewUpdater?.isGranularUpdatingEnabled = isGranularUpdatingEnabled + } + } + + deinit { + NotificationCenter.default.removeObserver(self) + NSObject.cancelPreviousPerformRequests(withTarget: self) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + startMonitoringReachabilityIfNeeded() + showOfflineEmptyViewIfNeeded() + imageScaleTransitionView = nil + detailTransitionSourceRect = nil + logFeedImpressionAfterDelay() + dataStore.remoteNotificationsController.loadNotifications(force: false) + } + + override func viewWillHaveFirstAppearance(_ animated: Bool) { + super.viewWillHaveFirstAppearance(animated) + setupFetchedResultsController() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + isGranularUpdatingEnabled = true + restoreScrollPositionIfNeeded() + + // Terrible hack to make back button text appropriate for iOS 14 - need to set the title on `WMFAppViewController`. For all app tabs, this is set in `viewWillAppear`. + (parent as? WMFAppViewController)?.navigationItem.backButtonTitle = title + } + + private func restoreScrollPositionIfNeeded() { + guard + shouldRestoreScrollPosition, + let presentedContentGroupKey = presentedContentGroupKey, + let contentGroup = fetchedResultsController?.fetchedObjects?.first(where: { $0.key == presentedContentGroupKey }), + let indexPath = fetchedResultsController?.indexPath(forObject: contentGroup) + else { + return + } + collectionView.scrollToItem(at: indexPath, at: [], animated: false) + self.shouldRestoreScrollPosition = false + self.presentedContentGroupKey = nil + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + dataStore.feedContentController.dismissCollapsedContentGroups() + stopMonitoringReachability() + isGranularUpdatingEnabled = false + } + + @objc func updateNotificationsCenterButton() { + if self.dataStore.authenticationManager.isLoggedIn { + let numberOfUnreadNotifications = try? dataStore.remoteNotificationsController.numberOfUnreadNotifications() + let hasUnreadNotifications = numberOfUnreadNotifications?.intValue ?? 0 != 0 + let bellImage = BarButtonImageStyle.notificationsButtonImage(theme: theme, indicated: hasUnreadNotifications) + let notificationsBarButton = UIBarButtonItem(image: bellImage, style: .plain, target: self, action: #selector(userDidTapNotificationsCenter)) + notificationsBarButton.accessibilityLabel = hasUnreadNotifications ? CommonStrings.notificationsCenterBadgeTitle : CommonStrings.notificationsCenterTitle + navigationItem.leftBarButtonItem = notificationsBarButton + navigationBar.displayType = .centeredLargeTitle + } else { + navigationItem.leftBarButtonItem = nil + navigationBar.displayType = .largeTitle + } + navigationBar.updateNavigationItems() + } + + @objc public func updateNavigationBarVisibility() { + navigationBar.isBarHidingEnabled = UIAccessibility.isVoiceOverRunning ? false : true + } + + func updateSettingsButton() { + let settingsBarButtonItem = UIBarButtonItem(image: BarButtonImageStyle.settingsButtonImage(theme: theme), style: .plain, target: self, action: #selector(userDidTapSettings)) + settingsBarButtonItem.accessibilityLabel = CommonStrings.settingsTitle + navigationItem.rightBarButtonItem = settingsBarButtonItem + navigationBar.updateNavigationItems() + } + + // MARK: - NavBar + + @objc func titleBarButtonPressed(_ sender: UIButton?) { + scrollToTop() + } + + @objc public var titleButton: UIView { + return titleView + } + + lazy var longTitleButton: UIButton = { + let longTitleButton = UIButton(type: .custom) + longTitleButton.adjustsImageWhenHighlighted = true + longTitleButton.setImage(UIImage(named: "wikipedia"), for: .normal) + longTitleButton.sizeToFit() + longTitleButton.addTarget(self, action: #selector(titleBarButtonPressed), for: .touchUpInside) + longTitleButton.isAccessibilityElement = false + return longTitleButton + }() + + lazy var titleView: UIView = { + let titleView = UIView(frame: longTitleButton.bounds) + titleView.addSubview(longTitleButton) + titleView.isAccessibilityElement = false + return titleView + }() + + @objc func userDidTapSettings() { + settingsPresentationDelegate?.userDidTapSettings(from: self) + } + + // MARK: - Refresh + + override func refreshControlActivated() { + super.refreshControlActivated() + } + + open override func refresh() { + FeedFunnel.shared.logFeedRefreshed() + updateFeedSources(with: nil, userInitiated: true) { + + } + } + + // MARK: - Scroll + + var isLoadingOlderContent: Bool = false + override func scrollViewDidScroll(_ scrollView: UIScrollView) { + super.scrollViewDidScroll(scrollView) + guard !isLoadingOlderContent else { + return + } + + let ratio: CGFloat = scrollView.contentOffset.y / (scrollView.contentSize.height - scrollView.bounds.size.height) + if ratio < 0.8 { + return + } + + let lastSectionIndex = numberOfSectionsInExploreFeed - 1 + guard lastSectionIndex >= 0 else { + return + } + + let lastItemIndex = numberOfItemsInSection(lastSectionIndex) - 1 + guard lastItemIndex >= 0 else { + return + } + + guard let lastGroup = group(at: IndexPath(item: lastItemIndex, section: lastSectionIndex)) else { + return + } + let now = Date() + let midnightUTC: Date = (now as NSDate).wmf_midnightUTCDateFromLocal + guard let lastGroupMidnightUTC = lastGroup.midnightUTCDate else { + return + } + + let calendar = NSCalendar.wmf_gregorian() + let days: Int = calendar?.wmf_days(from: lastGroupMidnightUTC, to: midnightUTC) ?? 0 + guard days < Int(WMFExploreFeedMaximumNumberOfDays) else { + return + } + + guard let nextOldestDate: Date = calendar?.date(byAdding: .day, value: -1, to: lastGroupMidnightUTC, options: .matchStrictly) else { + return + } + + isLoadingOlderContent = true + FeedFunnel.shared.logFeedRefreshed() + updateFeedSources(with: (nextOldestDate as NSDate).wmf_midnightLocalDateForEquivalentUTC, userInitiated: false) { + self.isLoadingOlderContent = false + } + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + logFeedImpressionAfterDelay() + } + + // MARK: - Event logging + + private func logFeedImpressionAfterDelay() { + NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(logFeedImpression), object: nil) + perform(#selector(logFeedImpression), with: self, afterDelay: 3) + } + + @objc private func logFeedImpression() { + for indexPath in collectionView.indexPathsForVisibleItems { + guard let group = group(at: indexPath), group.undoType == .none, let itemFrame = collectionView.layoutAttributesForItem(at: indexPath)?.frame else { + continue + } + let visibleRectOrigin = CGPoint(x: collectionView.contentOffset.x, y: collectionView.contentOffset.y + navigationBar.visibleHeight) + let visibleRectSize = view.layoutMarginsGuide.layoutFrame.size + let itemCenter = CGPoint(x: itemFrame.midX, y: itemFrame.midY) + let visibleRect = CGRect(origin: visibleRectOrigin, size: visibleRectSize) + let isUnobstructed = visibleRect.contains(itemCenter) + guard isUnobstructed else { + continue + } + FeedFunnel.shared.logFeedImpression(for: FeedFunnelContext(group)) + } + } + + // MARK: - Search + + public var wantsCustomSearchTransition: Bool { + return true + } + + lazy var searchBarContainerView: UIView = { + let searchContainerView = UIView() + searchBar.translatesAutoresizingMaskIntoConstraints = false + searchContainerView.addSubview(searchBar) + let leading = searchContainerView.layoutMarginsGuide.leadingAnchor.constraint(equalTo: searchBar.leadingAnchor) + let trailing = searchContainerView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: searchBar.trailingAnchor) + let top = searchContainerView.topAnchor.constraint(equalTo: searchBar.topAnchor) + let bottom = searchContainerView.bottomAnchor.constraint(equalTo: searchBar.bottomAnchor) + searchContainerView.addConstraints([leading, trailing, top, bottom]) + return searchContainerView + }() + + private var _scribbleIgnoringDelegate: Any? = nil + + private var scribbleIgnoringDelegate: ScribbleIgnoringInteractionDelegate? { + if _scribbleIgnoringDelegate == nil { + _scribbleIgnoringDelegate = ScribbleIgnoringInteractionDelegate() + } + return _scribbleIgnoringDelegate as? ScribbleIgnoringInteractionDelegate + } + + lazy var searchBar: UISearchBar = { + let searchBar = UISearchBar() + searchBar.delegate = self + searchBar.returnKeyType = .search + searchBar.searchBarStyle = .minimal + searchBar.placeholder = WMFLocalizedString("search-field-placeholder-text", value: "Search Wikipedia", comment: "Search field placeholder text") + + // Disable Scribble on this placeholder text field + if UIDevice.current.userInterfaceIdiom == .pad, + let scribbleIgnoringDelegate = scribbleIgnoringDelegate { + let existingInteractions = searchBar.searchTextField.interactions + existingInteractions.forEach { searchBar.searchTextField.removeInteraction($0) } + let scribbleIgnoringInteraction = UIScribbleInteraction(delegate: scribbleIgnoringDelegate) + searchBar.searchTextField.addInteraction(scribbleIgnoringInteraction) + } + + return searchBar + }() + + @objc func ensureWikipediaSearchIsShowing() { + if self.navigationBar.underBarViewPercentHidden > 0 { + self.navigationBar.setNavigationBarPercentHidden(0, underBarViewPercentHidden: 0, extendedViewPercentHidden: 0, topSpacingPercentHidden: 1, animated: true) + } + } + + // MARK: - UISearchBarDelegate + + func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool { + let searchActivity = NSUserActivity.wmf_searchView() + NotificationCenter.default.post(name: .WMFNavigateToActivity, object: searchActivity) + return false + } + + // MARK: - State + + @objc var dataStore: MWKDataStore! + private var fetchedResultsController: NSFetchedResultsController? + private var collectionViewUpdater: CollectionViewUpdater? + + private var wantsDeleteInsertOnNextItemUpdate: Bool = false + + private func setupFetchedResultsController() { + let fetchRequest: NSFetchRequest = WMFContentGroup.fetchRequest() + fetchRequest.predicate = NSPredicate(format: "isVisible == YES && (placement == NULL || placement == %@)", "feed") + fetchRequest.sortDescriptors = [NSSortDescriptor(key: "midnightUTCDate", ascending: false), NSSortDescriptor(key: "dailySortPriority", ascending: true), NSSortDescriptor(key: "date", ascending: false)] + let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: dataStore.viewContext, sectionNameKeyPath: "midnightUTCDate", cacheName: nil) + fetchedResultsController = frc + let updater = CollectionViewUpdater(fetchedResultsController: frc, collectionView: collectionView) + collectionViewUpdater = updater + updater.delegate = self + updater.isSlidingNewContentInFromTheTopEnabled = true + updater.performFetch() + } + + private func group(at indexPath: IndexPath) -> WMFContentGroup? { + guard let frc = fetchedResultsController, frc.isValidIndexPath(indexPath) else { + return nil + } + return frc.object(at: indexPath) + } + + private func groupKey(at indexPath: IndexPath) -> WMFInMemoryURLKey? { + return group(at: indexPath)?.inMemoryKey + } + + lazy var saveButtonsController: SaveButtonsController = { + let sbc = SaveButtonsController(dataStore: dataStore) + sbc.delegate = self + return sbc + }() + + var numberOfSectionsInExploreFeed: Int { + guard let sections = fetchedResultsController?.sections else { + return 0 + } + return sections.count + } + + func numberOfItemsInSection(_ section: Int) -> Int { + guard let sections = fetchedResultsController?.sections, sections.count > section else { + return 0 + } + return sections[section].numberOfObjects + } + + override func numberOfSections(in collectionView: UICollectionView) -> Int { + return numberOfSectionsInExploreFeed + } + + private func resetRefreshControl() { + guard let refreshControl = collectionView.refreshControl, + refreshControl.isRefreshing else { + return + } + refreshControl.endRefreshing() + } + + lazy var reachabilityNotifier: ReachabilityNotifier = { + let notifier = ReachabilityNotifier(Configuration.current.defaultSiteDomain) { [weak self] (reachable, flags) in + if reachable { + DispatchQueue.main.async { + self?.updateFeedSources(userInitiated: false) + } + } else { + DispatchQueue.main.async { + self?.showOfflineEmptyViewIfNeeded() + + } + } + } + return notifier + }() + + private func stopMonitoringReachability() { + reachabilityNotifier.stop() + } + + private func startMonitoringReachabilityIfNeeded() { + guard numberOfSectionsInExploreFeed == 0 else { + stopMonitoringReachability() + return + } + reachabilityNotifier.start() + } + + private func showOfflineEmptyViewIfNeeded() { + guard isViewLoaded && fetchedResultsController != nil else { + return + } + + guard numberOfSectionsInExploreFeed == 0 else { + wmf_hideEmptyView() + return + } + + guard !wmf_isShowingEmptyView() else { + return + } + + guard !reachabilityNotifier.isReachable else { + return + } + + resetRefreshControl() + wmf_showEmptyView(of: .noFeed, theme: theme, frame: view.bounds) + } + + var isLoadingNewContent = false + + @objc(updateFeedSourcesWithDate:userInitiated:completion:) + public func updateFeedSources(with date: Date? = nil, userInitiated: Bool, completion: @escaping () -> Void = { }) { + assert(Thread.isMainThread) + guard !isLoadingNewContent else { + completion() + return + } + isLoadingNewContent = true + if date == nil, let refreshControl = collectionView.refreshControl, !refreshControl.isRefreshing { + #if UI_TEST + #else + refreshControl.beginRefreshing() + #endif + if numberOfSectionsInExploreFeed == 0 { + scrollToTop() + } + } + self.dataStore.feedContentController.updateFeedSources(with: date, userInitiated: userInitiated) { + DispatchQueue.main.async { + self.isLoadingNewContent = false + self.resetRefreshControl() + if date == nil { + self.startMonitoringReachabilityIfNeeded() + self.showOfflineEmptyViewIfNeeded() + } + completion() + } + } + } + + override func contentSizeCategoryDidChange(_ notification: Notification?) { + layoutCache.reset() + super.contentSizeCategoryDidChange(notification) + } + + // MARK: - ImageScaleTransitionProviding + + var imageScaleTransitionView: UIImageView? + + // MARK: - DetailTransitionSourceProviding + + var detailTransitionSourceRect: CGRect? + + // MARK: - UICollectionViewDataSource + + override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return numberOfItemsInSection(section) + } + + override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let maybeCell = collectionView.dequeueReusableCell(withReuseIdentifier: ExploreCardCollectionViewCell.identifier, for: indexPath) + guard let cell = maybeCell as? ExploreCardCollectionViewCell else { + return maybeCell + } + cell.apply(theme: theme) + configure(cell: cell, forItemAt: indexPath, layoutOnly: false) + return cell + } + + override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + guard kind == UICollectionView.elementKindSectionHeader else { + abort() + } + guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CollectionViewHeader.identifier, for: indexPath) as? CollectionViewHeader else { + abort() + } + configureHeader(header, for: indexPath.section) + return header + } + + // MARK: - UICollectionViewDelegate + + func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { + guard let group = group(at: indexPath) else { + return false + } + return group.isSelectable + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + var titleAreaTapped = false + if let cell = collectionView.cellForItem(at: indexPath) as? ExploreCardCollectionViewCell { + detailTransitionSourceRect = view.convert(cell.frame, from: collectionView) + if + let vc = cell.cardContent as? ExploreCardViewController, + vc.collectionView.numberOfSections > 0, vc.collectionView.numberOfItems(inSection: 0) > 0, + let cell = vc.collectionView.cellForItem(at: IndexPath(item: 0, section: 0)) as? ArticleCollectionViewCell + { + imageScaleTransitionView = cell.imageView.isHidden ? nil : cell.imageView + } else { + imageScaleTransitionView = nil + } + titleAreaTapped = cell.titleAreaTapped + } + guard let group = group(at: indexPath) else { + return + } + + presentedContentGroupKey = group.key + + // When a random article title is tapped, show the previewed article, not another random article + let useRandomArticlePreviewItem = titleAreaTapped && group.moreType == .pageWithRandomButton + + if !useRandomArticlePreviewItem, let vc = group.detailViewControllerWithDataStore(dataStore, theme: theme) { + push(vc, context: FeedFunnelContext(group), index: indexPath.item, animated: true) + return + } + + if let vc = group.detailViewControllerForPreviewItemAtIndex(0, dataStore: dataStore, theme: theme) { + if vc is WMFImageGalleryViewController { + present(vc, animated: true) + FeedFunnel.shared.logFeedCardOpened(for: FeedFunnelContext(group)) + } else { + push(vc, context: FeedFunnelContext(group), index: indexPath.item, animated: true) + } + return + } + } + + func configureHeader(_ header: CollectionViewHeader, for sectionIndex: Int) { + guard collectionView(collectionView, numberOfItemsInSection: sectionIndex) > 0 else { + return + } + guard let group = group(at: IndexPath(item: 0, section: sectionIndex)) else { + return + } + header.title = (group.midnightUTCDate as NSDate?)?.wmf_localizedRelativeDateFromMidnightUTCDate() + header.apply(theme: theme) + } + + func createNewCardVCFor(_ cell: ExploreCardCollectionViewCell) -> ExploreCardViewController { + let cardVC = ExploreCardViewController() + cardVC.delegate = self + cardVC.dataStore = dataStore + cardVC.view.autoresizingMask = [] + addChild(cardVC) + cell.cardContent = cardVC + cardVC.didMove(toParent: self) + return cardVC + } + + func configure(cell: ExploreCardCollectionViewCell, forItemAt indexPath: IndexPath, layoutOnly: Bool) { + let cardVC = cell.cardContent as? ExploreCardViewController ?? createNewCardVCFor(cell) + guard let group = group(at: indexPath) else { + return + } + cardVC.contentGroup = group + cell.title = group.headerTitle + cell.subtitle = group.headerSubTitle + cell.footerTitle = cardVC.footerText + cell.isCustomizationButtonHidden = !(group.contentGroupKind.isCustomizable || group.contentGroupKind.isGlobal) + cell.undoType = group.undoType + cell.apply(theme: theme) + cell.delegate = self + if group.undoType == .contentGroupKind { + indexPathsForCollapsedCellsThatCanReappear.insert(indexPath) + } + } + + override func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + + self.theme = theme + updateNotificationsCenterButton() + updateSettingsButton() + + searchBar.apply(theme: theme) + searchBarContainerView.backgroundColor = theme.colors.paperBackground + collectionView.backgroundColor = .clear + view.backgroundColor = theme.colors.paperBackground + for cell in collectionView.visibleCells { + guard let themeable = cell as? Themeable else { + continue + } + themeable.apply(theme: theme) + } + for header in collectionView.visibleSupplementaryViews(ofKind: UICollectionView.elementKindSectionHeader) { + guard let themeable = header as? Themeable else { + continue + } + themeable.apply(theme: theme) + } + } + + // MARK: - ColumnarCollectionViewLayoutDelegate + + override func collectionView(_ collectionView: UICollectionView, estimatedHeightForItemAt indexPath: IndexPath, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + guard let group = group(at: indexPath) else { + return ColumnarCollectionViewLayoutHeightEstimate(precalculated: true, height: 0) + } + let identifier = ExploreCardCollectionViewCell.identifier + let userInfo = "evc-cell-\(group.inMemoryKey?.userInfoString ?? "")" + if let cachedHeight = layoutCache.cachedHeightForCellWithIdentifier(identifier, columnWidth: columnWidth, userInfo: userInfo) { + return ColumnarCollectionViewLayoutHeightEstimate(precalculated: true, height: cachedHeight) + } + var estimate = ColumnarCollectionViewLayoutHeightEstimate(precalculated: false, height: 100) + guard let placeholderCell = layoutManager.placeholder(forCellWithReuseIdentifier: ExploreCardCollectionViewCell.identifier) as? ExploreCardCollectionViewCell else { + return estimate + } + configure(cell: placeholderCell, forItemAt: indexPath, layoutOnly: true) + estimate.height = placeholderCell.sizeThatFits(CGSize(width: columnWidth, height: UIView.noIntrinsicMetric), apply: false).height + estimate.precalculated = true + layoutCache.setHeight(estimate.height, forCellWithIdentifier: identifier, columnWidth: columnWidth, groupKey: group.inMemoryKey, userInfo: userInfo) + return estimate + } + + override func collectionView(_ collectionView: UICollectionView, estimatedHeightForHeaderInSection section: Int, forColumnWidth columnWidth: CGFloat) -> ColumnarCollectionViewLayoutHeightEstimate { + guard let group = self.group(at: IndexPath(item: 0, section: section)), let date = group.midnightUTCDate, date < Date() else { + return ColumnarCollectionViewLayoutHeightEstimate(precalculated: true, height: 0) + } + var estimate = ColumnarCollectionViewLayoutHeightEstimate(precalculated: false, height: 100) + guard let header = layoutManager.placeholder(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: CollectionViewHeader.identifier) as? CollectionViewHeader else { + return estimate + } + configureHeader(header, for: section) + estimate.height = header.sizeThatFits(CGSize(width: columnWidth, height: UIView.noIntrinsicMetric), apply: false).height + estimate.precalculated = true + return estimate + } + + override func metrics(with size: CGSize, readableWidth: CGFloat, layoutMargins: UIEdgeInsets) -> ColumnarCollectionViewLayoutMetrics { + return ColumnarCollectionViewLayoutMetrics.exploreViewMetrics(with: size, readableWidth: readableWidth, layoutMargins: layoutMargins) + } + + override func collectionView(_ collectionView: UICollectionView, shouldShowFooterForSection section: Int) -> Bool { + return false + } + + // MARK: - ExploreCardViewControllerDelegate + + func exploreCardViewController(_ exploreCardViewController: ExploreCardViewController, didSelectItemAtIndexPath indexPath: IndexPath) { + guard + let contentGroup = exploreCardViewController.contentGroup, + let vc = contentGroup.detailViewControllerForPreviewItemAtIndex(indexPath.row, dataStore: dataStore, theme: theme) else { + return + } + + if let cell = exploreCardViewController.collectionView.cellForItem(at: indexPath) { + detailTransitionSourceRect = view.convert(cell.frame, from: exploreCardViewController.collectionView) + if let articleCell = cell as? ArticleCollectionViewCell, !articleCell.imageView.isHidden { + imageScaleTransitionView = articleCell.imageView + } else { + imageScaleTransitionView = nil + } + } + + if let otdvc = vc as? OnThisDayViewController { + otdvc.initialEvent = (contentGroup.contentPreview as? [Any])?[indexPath.item] as? WMFFeedOnThisDayEvent + } + + let context = FeedFunnelContext(contentGroup) + presentedContentGroupKey = contentGroup.key + switch contentGroup.detailType { + case .gallery: + present(vc, animated: true) + FeedFunnel.shared.logFeedCardOpened(for: context) + default: + push(vc, context: context, index: indexPath.item, animated: true) + } + } + + // MARK: - Prefetching + + override func imageURLsForItemAt(_ indexPath: IndexPath) -> Set? { + guard let contentGroup = group(at: indexPath) else { + return nil + } + return contentGroup.imageURLsCompatibleWithTraitCollection(traitCollection, dataStore: dataStore) + } + + #if DEBUG + override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { + guard motion == .motionShake else { + return + } + dataStore.feedContentController.debugChaos() + } + #endif + + // MARK: - CollectionViewUpdaterDelegate + + var needsReloadVisibleCells = false + var indexPathsForCollapsedCellsThatCanReappear = Set() + + private func reloadVisibleCells() { + for indexPath in collectionView.indexPathsForVisibleItems { + guard let cell = collectionView.cellForItem(at: indexPath) as? ExploreCardCollectionViewCell else { + continue + } + configure(cell: cell, forItemAt: indexPath, layoutOnly: false) + } + } + + func collectionViewUpdater(_ updater: CollectionViewUpdater, didUpdate collectionView: UICollectionView) { + + guard needsReloadVisibleCells else { + return + } + + reloadVisibleCells() + + needsReloadVisibleCells = false + layout.currentSection = nil + } + + func collectionViewUpdater(_ updater: CollectionViewUpdater, updateItemAtIndexPath indexPath: IndexPath, in collectionView: UICollectionView) { + layoutCache.invalidateGroupKey(groupKey(at: indexPath)) + collectionView.collectionViewLayout.invalidateLayout() + if wantsDeleteInsertOnNextItemUpdate { + layout.currentSection = indexPath.section + collectionView.deleteItems(at: [indexPath]) + collectionView.insertItems(at: [indexPath]) + } else { + needsReloadVisibleCells = true + } + } + + // MARK: Event logging + + var eventLoggingCategory: EventLoggingCategory { + return .feed + } + + var eventLoggingLabel: EventLoggingLabel? { + return previewed.context?.label + } + + // MARK: - For NestedCollectionViewContextMenuDelegate + private var previewed: (context: FeedFunnelContext?, indexPathItem: Int?) + + func contextMenu(with contentGroup: WMFContentGroup? = nil, for articleURL: URL? = nil, at itemIndex: Int) -> UIContextMenuConfiguration? { + guard let contentGroup = contentGroup, let vc = viewController(for: contentGroup, at: itemIndex) else { + return nil + } + + let previewProvider: () -> UIViewController? = { + return vc + } + return UIContextMenuConfiguration(identifier: nil, previewProvider: previewProvider) { (suggestedActions) -> UIMenu? in + if let articleVC = vc as? ArticleViewController { + return UIMenu(title: "", image: nil, identifier: nil, options: [], children: articleVC.contextMenuItems) + } else { + return nil + } + } + } + + func viewController(for contentGroup: WMFContentGroup, at itemIndex: Int) -> UIViewController? { + previewed.context = FeedFunnelContext(contentGroup) + + if let viewControllerToCommit = contentGroup.detailViewControllerForPreviewItemAtIndex(itemIndex, dataStore: dataStore, theme: theme) { + if let potd = viewControllerToCommit as? WMFImageGalleryViewController { + potd.setOverlayViewTopBarHidden(true) + } else if let avc = viewControllerToCommit as? ArticleViewController { + avc.articlePreviewingDelegate = self + avc.wmf_addPeekableChildViewController(for: avc.articleURL, dataStore: dataStore, theme: theme) + } else if let otdVC = viewControllerToCommit as? OnThisDayViewController { + otdVC.initialEvent = (contentGroup.contentPreview as? [Any])?[itemIndex] as? WMFFeedOnThisDayEvent + otdVC.navigationMode = .bar + } else if let newsVC = viewControllerToCommit as? NewsViewController { + newsVC.navigationMode = .bar + } + + previewed.indexPathItem = itemIndex + FeedFunnel.shared.logFeedCardPreviewed(for: previewed.context, index: itemIndex) + + return viewControllerToCommit + } else if contentGroup.contentGroupKind != .random { + return contentGroup.detailViewControllerWithDataStore(dataStore, theme: theme) + } else { + return nil + } + } + + func willCommitPreview(with animator: UIContextMenuInteractionCommitAnimating) { + guard let viewControllerToCommit = animator.previewViewController else { + assertionFailure("Should be able to find previewed VC") + return + } + animator.addCompletion { [weak self] in + guard let self = self else { + return + } + if let potd = viewControllerToCommit as? WMFImageGalleryViewController { + potd.setOverlayViewTopBarHidden(false) + self.present(potd, animated: false) + FeedFunnel.shared.logFeedCardOpened(for: self.previewed.context) + } else if let avc = viewControllerToCommit as? ArticleViewController { + avc.wmf_removePeekableChildViewControllers() + self.push(avc, context: self.previewed.context, index: self.previewed.indexPathItem, animated: false) + } else if let otdVC = viewControllerToCommit as? OnThisDayViewController { + otdVC.navigationMode = .detail + self.push(viewControllerToCommit, context: self.previewed.context, index: self.previewed.indexPathItem, animated: true) + } else if let newsVC = viewControllerToCommit as? NewsViewController { + newsVC.navigationMode = .detail + self.push(viewControllerToCommit, context: self.previewed.context, index: self.previewed.indexPathItem, animated: true) + } else { + self.push(viewControllerToCommit, context: self.previewed.context, index: self.previewed.indexPathItem, animated: true) + } + } + } + + // MARK: ArticlePreviewingDelegate + + override func shareArticlePreviewActionSelected(with articleController: ArticleViewController, shareActivityController: UIActivityViewController) { + super.shareArticlePreviewActionSelected(with: articleController, shareActivityController: shareActivityController) + FeedFunnel.shared.logFeedShareTapped(for: previewed.context, index: previewed.indexPathItem) + } + + override func readMoreArticlePreviewActionSelected(with articleController: ArticleViewController) { + articleController.wmf_removePeekableChildViewControllers() + push(articleController, context: previewed.context, index: previewed.indexPathItem, animated: true) + } + + override func saveArticlePreviewActionSelected(with articleController: ArticleViewController, didSave: Bool, articleURL: URL) { + if didSave { + ReadingListsFunnel.shared.logSaveInFeed(context: previewed.context, articleURL: articleURL, index: previewed.indexPathItem) + } else { + ReadingListsFunnel.shared.logUnsaveInFeed(context: previewed.context, articleURL: articleURL, index: previewed.indexPathItem) + } + } + + var addArticlesToReadingListVCDidDisappear: (() -> Void)? = nil +} + +// MARK: - Analytics +extension ExploreViewController { + private func logArticleSavedStateChange(_ wasArticleSaved: Bool, saveButton: SaveButton?, article: WMFArticle, userInfo: Any?) { + guard let articleURL = article.url else { + assert(false, "Article missing url: \(article)") + return + } + guard + let userInfo = userInfo as? ExploreSaveButtonUserInfo, + let midnightUTCDate = userInfo.midnightUTCDate, + let kind = userInfo.kind + else { + assert(false, "Article missing user info: \(article)") + return + } + let index = userInfo.indexPath.item + if wasArticleSaved { + ReadingListsFunnel.shared.logSaveInFeed(saveButton: saveButton, articleURL: articleURL, kind: kind, index: index, date: midnightUTCDate) + } else { + ReadingListsFunnel.shared.logUnsaveInFeed(saveButton: saveButton, articleURL: articleURL, kind: kind, index: index, date: midnightUTCDate) + } + } +} + +extension ExploreViewController: SaveButtonsControllerDelegate { + func didSaveArticle(_ saveButton: SaveButton?, didSave: Bool, article: WMFArticle, userInfo: Any?) { + let logSavedEvent = { + self.logArticleSavedStateChange(didSave, saveButton: saveButton, article: article, userInfo: userInfo) + } + if isPresentingAddArticlesToReadingListVC() { + addArticlesToReadingListVCDidDisappear = logSavedEvent + } else { + logSavedEvent() + } + } + + func willUnsaveArticle(_ article: WMFArticle, userInfo: Any?) { + if article.userCreatedReadingListsCount > 0 { + let alertController = ReadingListsAlertController() + alertController.showAlert(presenter: self, article: article) + } else { + saveButtonsController.updateSavedState() + } + } + + func showAddArticlesToReadingListViewController(for article: WMFArticle) { + let addArticlesToReadingListViewController = AddArticlesToReadingListViewController(with: dataStore, articles: [article], moveFromReadingList: nil, theme: theme) + addArticlesToReadingListViewController.delegate = self + let navigationController = WMFThemeableNavigationController(rootViewController: addArticlesToReadingListViewController, theme: self.theme) + navigationController.isNavigationBarHidden = true + present(navigationController, animated: true) + } + + private func isPresentingAddArticlesToReadingListVC() -> Bool { + guard let navigationController = presentedViewController as? UINavigationController else { + return false + } + return navigationController.viewControllers.contains { $0 is AddArticlesToReadingListViewController } + } +} + +extension ExploreViewController: AddArticlesToReadingListDelegate { + func addArticlesToReadingListWillClose(_ addArticlesToReadingList: AddArticlesToReadingListViewController) { + } + + func addArticlesToReadingListDidDisappear(_ addArticlesToReadingList: AddArticlesToReadingListViewController) { + addArticlesToReadingListVCDidDisappear?() + addArticlesToReadingListVCDidDisappear = nil + } + + func addArticlesToReadingList(_ addArticlesToReadingList: AddArticlesToReadingListViewController, didAddArticles articles: [WMFArticle], to readingList: ReadingList) { + } +} + +extension ExploreViewController: ReadingListsAlertControllerDelegate { + func readingListsAlertController(_ readingListsAlertController: ReadingListsAlertController, didSelectUnsaveForArticle: WMFArticle) { + saveButtonsController.updateSavedState() + } +} + +extension ExploreViewController: ExploreCardCollectionViewCellDelegate { + func exploreCardCollectionViewCellWantsCustomization(_ cell: ExploreCardCollectionViewCell) { + guard let vc = cell.cardContent as? ExploreCardViewController, + let group = vc.contentGroup else { + return + } + guard let sheet = menuActionSheetForGroup(group) else { + return + } + sheet.popoverPresentationController?.sourceView = cell.customizationButton + sheet.popoverPresentationController?.sourceRect = cell.customizationButton.bounds + present(sheet, animated: true) + } + + private func save() { + do { + try self.dataStore.save() + } catch let error { + DDLogError("Error saving after cell customization update: \(error)") + } + } + + @objc func exploreFeedPreferencesDidSave(_ note: Notification) { + DispatchQueue.main.async { + for indexPath in self.indexPathsForCollapsedCellsThatCanReappear { + guard self.fetchedResultsController?.isValidIndexPath(indexPath) ?? false else { + continue + } + self.layoutCache.invalidateGroupKey(self.groupKey(at: indexPath)) + self.collectionView.collectionViewLayout.invalidateLayout() + } + self.indexPathsForCollapsedCellsThatCanReappear = [] + } + } + + @objc func articleDidChange(_ note: Notification) { + guard + let article = note.object as? WMFArticle, + let articleKey = article.inMemoryKey + else { + return + } + + var needsReload = false + if article.hasChangedValuesForCurrentEventThatAffectPreviews, layoutCache.invalidateArticleKey(articleKey) { + needsReload = true + collectionView.collectionViewLayout.invalidateLayout() + } else if !article.hasChangedValuesForCurrentEventThatAffectSavedState { + return + } + + let visibleIndexPathsWithChanges = collectionView.indexPathsForVisibleItems.filter { (indexPath) -> Bool in + guard let contentGroup = group(at: indexPath) else { + return false + } + return contentGroup.previewArticleKeys.contains(articleKey) + } + + guard !visibleIndexPathsWithChanges.isEmpty else { + return + } + + for indexPath in visibleIndexPathsWithChanges { + guard let cell = collectionView.cellForItem(at: indexPath) as? ExploreCardCollectionViewCell else { + continue + } + if needsReload { + configure(cell: cell, forItemAt: indexPath, layoutOnly: false) + } else if let cardVC = cell.cardContent as? ExploreCardViewController { + cardVC.savedStateDidChangeForArticleWithKey(articleKey) + } + } + } + + @objc func articleDeleted(_ note: Notification) { + guard let articleKey = note.userInfo?[WMFArticleDeletedNotificationUserInfoArticleKeyKey] as? WMFInMemoryURLKey else { + return + } + layoutCache.invalidateArticleKey(articleKey) + } + + @objc func viewContextDidReset(_ note: Notification) { + collectionView.reloadData() + } + + private func menuActionSheetForGroup(_ group: WMFContentGroup) -> UIAlertController? { + guard group.contentGroupKind.isCustomizable || group.contentGroupKind.isGlobal else { + return nil + } + let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + let customizeExploreFeed = UIAlertAction(title: CommonStrings.customizeExploreFeedTitle, style: .default) { (_) in + let exploreFeedSettingsViewController = ExploreFeedSettingsViewController() + exploreFeedSettingsViewController.showCloseButton = true + exploreFeedSettingsViewController.dataStore = self.dataStore + exploreFeedSettingsViewController.apply(theme: self.theme) + let themeableNavigationController = WMFThemeableNavigationController(rootViewController: exploreFeedSettingsViewController, theme: self.theme) + themeableNavigationController.modalPresentationStyle = .formSheet + self.present(themeableNavigationController, animated: true) + } + let hideThisCard = UIAlertAction(title: WMFLocalizedString("explore-feed-preferences-hide-card-action-title", value: "Hide this card", comment: "Title for action that allows users to hide a feed card"), style: .default) { (_) in + FeedFunnel.shared.logFeedCardDismissed(for: FeedFunnelContext(group)) + group.undoType = .contentGroup + self.wantsDeleteInsertOnNextItemUpdate = true + self.save() + } + guard let title = group.headerTitle else { + assertionFailure("Expected header title for group \(group.contentGroupKind)") + return nil + } + let hideAllCards = UIAlertAction(title: String.localizedStringWithFormat(WMFLocalizedString("explore-feed-preferences-hide-feed-cards-action-title", value: "Hide all “%@” cards", comment: "Title for action that allows users to hide all feed cards of given type - %@ is replaced with feed card type"), title), style: .default) { (_) in + let feedContentController = self.dataStore.feedContentController + // If there's only one group left it means that we're about to show an alert about turning off the Explore tab. In those cases, we don't want to provide the option to undo. + if feedContentController.countOfVisibleContentGroupKinds > 1 { + group.undoType = .contentGroupKind + self.wantsDeleteInsertOnNextItemUpdate = true + } + feedContentController.toggleContentGroup(of: group.contentGroupKind, isOn: false, waitForCallbackFromCoordinator: true, apply: true, updateFeed: false) + FeedFunnel.shared.logFeedCardDismissed(for: FeedFunnelContext(group)) + } + let cancel = UIAlertAction(title: CommonStrings.cancelActionTitle, style: .cancel) + sheet.addAction(hideThisCard) + if !(group.contentGroupKind == WMFContentGroupKind.notification) { + sheet.addAction(hideAllCards) + } + sheet.addAction(customizeExploreFeed) + sheet.addAction(cancel) + + return sheet + } + + func exploreCardCollectionViewCellWantsToUndoCustomization(_ cell: ExploreCardCollectionViewCell) { + guard let vc = cell.cardContent as? ExploreCardViewController, + let group = vc.contentGroup else { + return + } + FeedFunnel.shared.logFeedCardRetained(for: FeedFunnelContext(group)) + if group.undoType == .contentGroupKind { + dataStore.feedContentController.toggleContentGroup(of: group.contentGroupKind, isOn: true, waitForCallbackFromCoordinator: false, apply: true, updateFeed: false) + } + group.undoType = .none + wantsDeleteInsertOnNextItemUpdate = true + if let indexPath = fetchedResultsController?.indexPath(forObject: group) { + indexPathsForCollapsedCellsThatCanReappear.remove(indexPath) + } + save() + } + +} + +// MARK: - EventLoggingSearchSourceProviding +extension ExploreViewController: EventLoggingSearchSourceProviding { + var searchSource: String { + return "top_of_feed" + } +} + +// MARK: - Notifications Center +extension ExploreViewController { + + @objc func userDidTapNotificationsCenter() { + notificationsCenterPresentationDelegate?.userDidTapNotificationsCenter(from: self) + } + + @objc func pushNotificationBannerDidDisplayInForeground(_ notification: Notification) { + dataStore.remoteNotificationsController.loadNotifications(force: true) + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/FLAnimatedImage+SafeForSwift.h b/Apps/Wikipedia/Wikipedia/Code/FLAnimatedImage+SafeForSwift.h new file mode 100644 index 0000000..453e059 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/FLAnimatedImage+SafeForSwift.h @@ -0,0 +1,10 @@ +@import Foundation; +#import "FLAnimatedImage.h" + +@interface FLAnimatedImage (SafeForSwift) + ++ (nullable FLAnimatedImage *)wmf_animatedImageWithData:(nullable NSData *)data; + +@property (nonatomic, readonly, nullable) UIImage *wmf_staticImage; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/FLAnimatedImage+SafeForSwift.m b/Apps/Wikipedia/Wikipedia/Code/FLAnimatedImage+SafeForSwift.m new file mode 100644 index 0000000..e4496cc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/FLAnimatedImage+SafeForSwift.m @@ -0,0 +1,13 @@ +#import + +@implementation FLAnimatedImage (SafeForSwift) + ++ (nullable FLAnimatedImage *)wmf_animatedImageWithData:(nullable NSData *)data { + return [[FLAnimatedImage alloc] initWithAnimatedGIFData:data]; +} + +- (nullable UIImage *)wmf_staticImage { + return self.posterImage; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/FakeProgressLoading.swift b/Apps/Wikipedia/Wikipedia/Code/FakeProgressLoading.swift new file mode 100644 index 0000000..2017864 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/FakeProgressLoading.swift @@ -0,0 +1,5 @@ +import Foundation + +protocol FakeProgressLoading { + var fakeProgressController: FakeProgressController { get } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/FeedCardSettingsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/FeedCardSettingsViewController.swift new file mode 100644 index 0000000..2b065a3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/FeedCardSettingsViewController.swift @@ -0,0 +1,88 @@ +import UIKit + +private extension WMFContentGroupKind { + var togglingFeedCardFooterText: String { + switch self { + case .news: + return WMFLocalizedString("explore-feed-preferences-show-news-footer-text", value: "Turning off the In the news card will turn the card off in all available languages.", comment: "Text describing the effects of turning off the In the news card") + case .featuredArticle: + return WMFLocalizedString("explore-feed-preferences-show-featured-article-footer-text", value: "Turning off the Featured article card will turn the card off in all available languages.", comment: "Text describing the effects of turning off the Featured article card") + case .topRead: + return WMFLocalizedString("explore-feed-preferences-show-top-read-footer-text", value: "Turning off the Top read card will turn the card off in all available languages.", comment: "Text describing the effects of turning off the Top read card") + case .onThisDay: + return WMFLocalizedString("explore-feed-preferences-show-on-this-day-footer-text", value: "Turning off the On this day card will turn the card off in all available languages.", comment: "Text describing the effects of turning off the On this day card") + case .continueReading: + fallthrough + case .relatedPages: + fallthrough + case .pictureOfTheDay: + return WMFLocalizedString("explore-feed-preferences-global-card-footer-text", value: "This card is not language specific, turning off this card will remove it from your Explore feed.", comment: "Text describing the effects of turning off a global card") + case .locationPlaceholder: + fallthrough + case .location: + return WMFLocalizedString("explore-feed-preferences-show-places-footer-text", value: "Turning off the Places card will turn the card off in all available languages.", comment: "Text describing the effects of turning off the Places card") + case .random: + return WMFLocalizedString("explore-feed-preferences-show-randomizer-footer-text", value: "Turning off the Randomizer card will turn the card off in all available languages.", comment: "Text describing the effects of turning off the Randomizer card") + default: + assertionFailure("\(self) is not customizable") + return "" + } + } +} + +class FeedCardSettingsViewController: BaseExploreFeedSettingsViewController { + private var contentGroupKind: WMFContentGroupKind = .unknown + + func configure(with title: String, dataStore: MWKDataStore?, contentGroupKind: WMFContentGroupKind, theme: Theme) { + self.title = title + self.dataStore = dataStore + self.contentGroupKind = contentGroupKind + self.theme = theme + displayType = .detail(contentGroupKind) + } + + // MARK: Sections + + private lazy var togglingFeedCardFooterText: String = { + return contentGroupKind.togglingFeedCardFooterText + }() + + private lazy var mainSection: ExploreFeedSettingsSection = { + return ExploreFeedSettingsSection(headerTitle: nil, footerTitle: togglingFeedCardFooterText, items: [ExploreFeedSettingsPrimary(for: .singleFeedCard(contentGroupKind))]) + }() + + private lazy var languagesSection: ExploreFeedSettingsSection = { + return ExploreFeedSettingsSection(headerTitle: CommonStrings.languagesTitle, footerTitle: String.localizedStringWithFormat("%@ %@", WMFLocalizedString("explore-feed-preferences-additional-languages-footer-text", value: "Additional languages can be added in the ‘My languages’ settings page.", comment: "Text explaining how to add additional languages"), togglingFeedCardFooterText), items: languages) + }() + + override var sections: [ExploreFeedSettingsSection] { + if contentGroupKind.isGlobal { + return [mainSection] + } else { + return [mainSection, languagesSection] + } + } + +} + +// MARK: - WMFSettingsTableViewCellDelegate + +extension FeedCardSettingsViewController { + override func settingsTableViewCell(_ settingsTableViewCell: WMFSettingsTableViewCell!, didToggleDisclosureSwitch sender: UISwitch!) { + activeSwitch = sender + let controlTag = sender.tag + guard let feedContentController = feedContentController else { + assertionFailure("feedContentController is nil") + return + } + guard controlTag != -1 else { // main switch + feedContentController.toggleContentGroup(of: contentGroupKind, isOn: sender.isOn, updateFeed: false) + return + } + guard let language = languages.first(where: { $0.controlTag == sender.tag }) else { + assertionFailure("No language for a given control tag") + return + } + feedContentController.toggleContentGroup(of: contentGroupKind, isOn: sender.isOn, forSiteURL: language.siteURL, updateFeed: false) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/FeedFunnel.swift b/Apps/Wikipedia/Wikipedia/Code/FeedFunnel.swift new file mode 100644 index 0000000..bbad95d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/FeedFunnel.swift @@ -0,0 +1,199 @@ +// https://meta.wikimedia.org/wiki/Schema:MobileWikiAppiOSFeed + +@objc public class FeedFunnelContext: NSObject { + let label: EventLoggingLabel? + let key: String? + let midnightUTCDate: Date? + let siteURLString: String? + @objc(initWithContentGroup:) + convenience init(_ group: WMFContentGroup?) { + self.init(label: group?.eventLoggingLabel, key: group?.key, midnightUTCDate: group?.midnightUTCDate, siteURLString: group?.siteURLString) + } + init(label: EventLoggingLabel?, key: String?, midnightUTCDate: Date?, siteURLString: String?) { + self.label = label + self.key = key + self.midnightUTCDate = midnightUTCDate + self.siteURLString = siteURLString + super.init() + } +} + +@objc public final class FeedFunnel: EventLoggingFunnel, EventLoggingStandardEventProviding { + @objc public static let shared = FeedFunnel() + + private override init() { + super.init(schema: "MobileWikiAppiOSFeed", version: 18280649) + } + + private enum Action: String { + case impression + case openCard = "open_card" + case dismiss + case retain + case refresh + case preview + case readStart = "read_start" + case shareTap = "share_tap" + case closeCard = "close_card" + } + + private func event(category: EventLoggingCategory, label: EventLoggingLabel?, action: Action, measureAge: NSNumber? = nil, measurePosition: Int? = nil, measureTime: Double? = nil, measureMaxViewed: Double? = nil) -> [String: Any] { + let category = category.rawValue + let action = action.rawValue + + var event: [String: Any] = ["category": category, "action": action, "primary_language": primaryLanguage(), "is_anon": isAnon] + if let label = label?.rawValue { + event["label"] = label + } + if let measureAge = measureAge { + event["measure_age"] = measureAge.intValue + } + if let measurePosition = measurePosition { + event["measure_position"] = measurePosition + } + if let measureTime = measureTime { + event["measure_time"] = Int(round(measureTime)) + } + if let measureMaxViewed = measureMaxViewed { + event["measure_max_viewed"] = min(100, Int(floor(measureMaxViewed))) + } + return event + } + + override public func preprocessData(_ eventData: [AnyHashable: Any]) -> [AnyHashable: Any] { + return wholeEvent(with: eventData) + } + + // MARK: - Feed + + @objc public func logFeedCardOpened(for context: FeedFunnelContext?) { + startMeasuringTime(for: context?.label, key: context?.key) + log(event(category: .feed, label: context?.label, action: .openCard, measureAge: measureAge(for: context?.midnightUTCDate)), language: language(from: context?.siteURLString)) + } + + @objc public func logFeedCardDismissed(for context: FeedFunnelContext?) { + log(event(category: .feed, label: context?.label, action: .dismiss, measureAge: measureAge(for: context?.midnightUTCDate)), language: language(from: context?.siteURLString)) + } + + @objc public func logFeedCardRetained(for context: FeedFunnelContext?) { + log(event(category: .feed, label: context?.label, action: .retain, measureAge: measureAge(for: context?.midnightUTCDate)), language: language(from: context?.siteURLString)) + } + + @objc public func logFeedCardPreviewed(for context: FeedFunnelContext?, index: Int) { + log(event(category: .feed, label: context?.label, action: .preview, measureAge: measureAge(for: context?.midnightUTCDate), measurePosition: measurePosition(for: context?.label, index: index)), language: language(from: context?.siteURLString)) + } + + public func logFeedCardReadingStarted(for context: FeedFunnelContext?, index: Int?) { + log(event(category: .feed, label: context?.label, action: .readStart, measureAge: measureAge(for: context?.midnightUTCDate), measurePosition: measurePosition(for: context?.label, index: index)), language: language(from: context?.siteURLString)) + } + + public func logFeedShareTapped(for context: FeedFunnelContext?, index: Int?) { + log(event(category: .feed, label: context?.label, action: .shareTap, measureAge: measureAge(for: context?.midnightUTCDate), measurePosition: measurePosition(for: context?.label, index: index)), language: language(from: context?.siteURLString)) + } + + public func logFeedRefreshed() { + log(event(category: .feed, label: nil, action: .refresh)) + } + + @objc public func logFeedImpression(for context: FeedFunnelContext?) { + log(event(category: .feed, label: context?.label, action: .impression, measureAge: measureAge(for: context?.midnightUTCDate)), language: language(from: context?.siteURLString)) + } + + // MARK: Feed detail + + public func logArticleInFeedDetailPreviewed(for context: FeedFunnelContext?, index: Int?) { + log(event(category: .feedDetail, label: context?.label, action: .preview, measureAge: measureAge(for: context?.midnightUTCDate), measurePosition: index), language: language(from: context?.siteURLString)) + } + + public func logArticleInFeedDetailReadingStarted(for context: FeedFunnelContext?, index: Int?, maxViewed: Double) { + log(event(category: .feedDetail, label: context?.label, action: .readStart, measureAge: measureAge(for: context?.midnightUTCDate), measurePosition: index, measureTime: measureTime(key: context?.key), measureMaxViewed: maxViewed), language: language(from: context?.siteURLString)) + startMeasuringTime(for: context?.label, key: context?.key) + } + + public func logFeedCardClosed(for context: FeedFunnelContext?, maxViewed: Double) { + log(event(category: .feedDetail, label: context?.label, action: .closeCard, measureAge: measureAge(for: context?.midnightUTCDate), measureTime: measureTime(key: context?.key), measureMaxViewed: maxViewed), language: language(from: context?.siteURLString)) + } + + public func logFeedDetailShareTapped(for context: FeedFunnelContext?, index: Int?, midnightUTCDate: Date?) { + log(event(category: .feedDetail, label: context?.label, action: .shareTap, measureAge: measureAge(for: context?.midnightUTCDate), measurePosition: index), language: language(from: context?.siteURLString)) + } + + public func logFeedDetailShareTapped(for context: FeedFunnelContext?, index: Int?) { + log(event(category: .feedDetail, label: context?.label, action: .shareTap, measureAge: measureAge(for: context?.midnightUTCDate), measurePosition: index), language: language(from: context?.siteURLString)) + } + + // MARK: Utilities + + public var fetchedContentGroupsInFeedController: NSFetchedResultsController? + + private func measureAge(for midnightUTCDate: Date?) -> NSNumber? { + guard let date = midnightUTCDate else { + return nil + } + let now = NSDate().wmf_midnightUTCDateFromLocal + return NSNumber(integerLiteral: NSCalendar.wmf_gregorian().wmf_days(from: date, to: now)) + } + + private func measurePosition(for label: EventLoggingLabel?, index: Int?) -> Int? { + guard let label = label else { + return nil + } + switch label { + case .onThisDay: + fallthrough + case .news: + return nil + default: + return index + } + } + + private var contentGroupKeysToStartTimes = [String: Date]() + + private func shouldMeasureTime(for label: EventLoggingLabel?) -> Bool { + guard let label = label else { + return false + } + switch label { + case .topRead: + fallthrough + case .relatedPages: + fallthrough + case .onThisDay: + fallthrough + case .news: + fallthrough + case .location: + return true + default: + return false + } + } + + private func startMeasuringTime(for label: EventLoggingLabel?, key: String?) { + guard shouldMeasureTime(for: label) else { + return + } + guard let key = key else { + assertionFailure() + return + } + contentGroupKeysToStartTimes[key] = Date() + } + + private func measureTime(key: String?) -> Double? { + guard let key = key, let startTime = contentGroupKeysToStartTimes[key] else { + return nil + } + let measureTime = fabs(startTime.timeIntervalSinceNow) + contentGroupKeysToStartTimes.removeValue(forKey: key) + return measureTime + } + + private func language(from siteURLString: String?) -> String? { + guard let siteURLString = siteURLString else { + return nil + } + return URL(string: siteURLString)?.wmf_languageCode + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/FindAndReplaceKeyboardBar.swift b/Apps/Wikipedia/Wikipedia/Code/FindAndReplaceKeyboardBar.swift new file mode 100644 index 0000000..b5414dd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/FindAndReplaceKeyboardBar.swift @@ -0,0 +1,408 @@ +import UIKit + +@objc (WMFFindAndReplaceKeyboardBarDelegate) +protocol FindAndReplaceKeyboardBarDelegate: AnyObject { + func keyboardBar(_ keyboardBar: FindAndReplaceKeyboardBar, didChangeSearchTerm searchTerm: String?) + func keyboardBarDidTapClose(_ keyboardBar: FindAndReplaceKeyboardBar) + func keyboardBarDidTapClear(_ keyboardBar: FindAndReplaceKeyboardBar) + func keyboardBarDidTapPrevious(_ keyboardBar: FindAndReplaceKeyboardBar) + func keyboardBarDidTapNext(_ keyboardBar: FindAndReplaceKeyboardBar?) + func keyboardBarDidTapReturn(_ keyboardBar: FindAndReplaceKeyboardBar) + func keyboardBarDidTapReplace(_ keyboardBar: FindAndReplaceKeyboardBar, replaceText: String, replaceType: ReplaceType) +} + +protocol FindAndReplaceKeyboardBarDisplayDelegate: AnyObject { + func keyboardBarDidTapReplaceSwitch(_ keyboardBar: FindAndReplaceKeyboardBar) + func keyboardBarDidShow(_ keyboardBar: FindAndReplaceKeyboardBar) + func keyboardBarDidHide(_ keyboardBar: FindAndReplaceKeyboardBar) +} + +@objc enum ReplaceType: Int { + case replaceSingle + case replaceAll + + var text: String { + switch self { + case .replaceSingle: return WMFLocalizedString("replace-infolabel-method-replace", value: "Replace", comment: "Title for label indicating which replace method they have currently selected. This is for replacing a single instance.") + case .replaceAll: return WMFLocalizedString("replace-infolabel-method-replace-all", value: "Replace all", comment: "Title for label indicating which replace method they have currently selected. This is for replacing all instances.") + } + } + + var accessibilityText: String { + switch self { + case .replaceSingle: return WMFLocalizedString("replace-buttons-replace-accessibility", value: "Replace single instance", comment: "Accessibility text for describing the type of replace the user is set to or about to perform. This is for replacing a single instance.") + case .replaceAll: return WMFLocalizedString("replace-buttons-replace-all-accessibility", value: "Replace all instances", comment: "Accessibility text for describing the type of replace the user is set to or about to perform. This is for replacing all instances.") + } + } +} + +struct FindMatchPlacement { + var index: UInt? + var total: UInt +} + +@objc (WMFFindAndReplaceKeyboardBar) +final class FindAndReplaceKeyboardBar: UIInputView { + @IBOutlet private var outerStackView: UIStackView! + @IBOutlet private var outerStackViewLeadingConstraint: NSLayoutConstraint! + @IBOutlet private var outerStackViewTrailingConstraint: NSLayoutConstraint! + + @IBOutlet private var findStackView: UIStackView! + @IBOutlet private var findTextField: UITextField! + @IBOutlet private var findTextFieldContainer: UIView! + @IBOutlet private var magnifyImageView: UIImageView! + @IBOutlet private var currentMatchLabel: UILabel! + @IBOutlet private var findClearButton: UIButton! + @IBOutlet private var closeButton: UIButton! + @IBOutlet private var nextButton: UIButton! + @IBOutlet private var nextPrevButtonStackView: UIStackView! + @IBOutlet private var previousButton: UIButton! + + @IBOutlet private var replaceStackView: UIStackView! + @IBOutlet private var replaceTextField: UITextField! + @IBOutlet private var replaceTextFieldContainer: UIView! + @IBOutlet private var pencilImageView: UIImageView! + @IBOutlet private var replaceTypeLabel: UILabel! + @IBOutlet private var replacePlaceholderLabel: UILabel! + @IBOutlet private var replaceClearButton: UIButton! + @IBOutlet private var replaceButton: UIButton! + @IBOutlet private(set) var replaceSwitchButton: UIButton! + + @objc weak var delegate: FindAndReplaceKeyboardBarDelegate? + weak var displayDelegate: FindAndReplaceKeyboardBarDisplayDelegate? + + // represents current match label values + private var matchPlacement = FindMatchPlacement(index: 0, total: 0) { + didSet { + if matchPlacement.index == nil && matchPlacement.total > 0 { + currentMatchLabel.text = String.localizedStringWithFormat("%lu", matchPlacement.total) + } else if let index = matchPlacement.index { + let format = WMFLocalizedString("find-infolabel-number-matches", value: "%1$@ / %2$@", comment: "Displayed to indicate how many matches were found even if no matches. Separator can be customized depending on the language. %1$@ is replaced with the numerator, %2$@ is replaced with the denominator.") + currentMatchLabel.text = String.localizedStringWithFormat(format, NSNumber(value: index), NSNumber(value: matchPlacement.total)) + } + } + } + + var replaceType: ReplaceType = .replaceSingle { + didSet { + if oldValue != replaceType { + updateReplaceLabelState() + } + } + } + + var isShowingReplace: Bool = false { + didSet { + if oldValue != isShowingReplace { + updateShowingReplaceState() + } + } + } + + @objc var isVisible: Bool { + return findTextField.isFirstResponder || replaceTextField.isFirstResponder + } + + override func awakeFromNib() { + super.awakeFromNib() + hideUndoRedoIcons() + previousButton.isEnabled = false + nextButton.isEnabled = false + + setupStaticAccessibilityLabels() + + updateShowingReplaceState() + updateReplaceLabelState() + updateReplaceButtonsState() + } + + override var intrinsicContentSize: CGSize { + return CGSize(width: UIView.noIntrinsicMetric, height: 46) + } + + @objc func updateMatchCounts(index: Int, total: UInt) { + updateMatchPlacement(index: index, total: total) + updatePreviousNextButtonsState(total: total) + updateReplaceButtonsState() + } + + @objc func show() { + findTextField.becomeFirstResponder() + displayDelegate?.keyboardBarDidShow(self) + } + + @objc func hide() { + findTextField.resignFirstResponder() + replaceTextField.resignFirstResponder() + displayDelegate?.keyboardBarDidHide(self) + } + + func reset() { + resetFind() + resetReplace() + } + + @objc func resetFind() { + findTextField.text = nil + currentMatchLabel.text = nil + findClearButton.isHidden = true + matchPlacement.index = 0 + matchPlacement.total = 0 + updateMatchCounts(index: 0, total: 0) + updatePreviousNextButtonsState(total: 0) + } + + // MARK: IBActions + + @IBAction func tappedFindClear() { + delegate?.keyboardBarDidTapClear(self) + if !isVisible { + show() + } + } + + @IBAction func tappedReplaceClear() { + replaceTextField.text = nil + updateReplaceLabelState() + updateReplaceButtonsState() + } + + @IBAction func tappedClose() { + delegate?.keyboardBarDidTapClose(self) + } + + @IBAction func tappedNext() { + delegate?.keyboardBarDidTapNext(self) + } + + @IBAction func tappedPrevious() { + delegate?.keyboardBarDidTapPrevious(self) + } + + @IBAction func tappedReplace() { + + guard let replaceText = replaceTextField.text else { + return + } + delegate?.keyboardBarDidTapReplace(self, replaceText: replaceText, replaceType: replaceType) + } + + @IBAction func tappedReplaceSwitch() { + displayDelegate?.keyboardBarDidTapReplaceSwitch(self) + } + + @IBAction func textFieldDidChange(_ sender: UITextField) { + let count = sender.text?.count ?? 0 + + switch sender { + case findTextField: + delegate?.keyboardBar(self, didChangeSearchTerm: findTextField.text) + findClearButton.isHidden = count == 0 + updateReplaceButtonsState() + case replaceTextField: + updateReplaceButtonsState() + updateReplaceLabelState() + default: + break + } + } +} + +// MARK: UITextFieldDelegate + +extension FindAndReplaceKeyboardBar: UITextFieldDelegate { + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + + switch textField { + case findTextField: + delegate?.keyboardBarDidTapReturn(self) + case replaceTextField: + + guard let replaceText = replaceTextField.text, + let findText = findTextField.text, + matchPlacement.total > 0, + findText.count > 0, + replaceText.count > 0 else { + return false + } + + delegate?.keyboardBarDidTapReplace(self, replaceText: replaceText, replaceType: replaceType) + default: + break + } + + return true + } + + func textFieldDidBeginEditing(_ textField: UITextField) { + updateReplaceLabelState() + updateReplaceButtonsState() + } +} + +// MARK: Themeable + +extension FindAndReplaceKeyboardBar: Themeable { + func apply(theme: Theme) { + tintColor = theme.colors.link + + findTextField.keyboardAppearance = theme.keyboardAppearance + findTextField.textColor = theme.colors.primaryText + findTextFieldContainer.backgroundColor = theme.colors.keyboardBarSearchFieldBackground + closeButton.tintColor = theme.colors.secondaryText + previousButton.tintColor = theme.colors.secondaryText + nextButton.tintColor = theme.colors.secondaryText + magnifyImageView.tintColor = theme.colors.secondaryText + findClearButton.tintColor = theme.colors.secondaryText + currentMatchLabel.textColor = theme.colors.tertiaryText + + replaceTextField.keyboardAppearance = theme.keyboardAppearance + replaceTextField.textColor = theme.colors.primaryText + replaceTextFieldContainer.backgroundColor = theme.colors.keyboardBarSearchFieldBackground + replaceButton.tintColor = theme.colors.secondaryText + replaceSwitchButton.tintColor = theme.colors.secondaryText + pencilImageView.tintColor = theme.colors.secondaryText + replaceClearButton.tintColor = theme.colors.secondaryText + replaceTypeLabel.textColor = theme.colors.tertiaryText + replacePlaceholderLabel.textColor = theme.colors.tertiaryText + + } +} + +// MARK: Private + +private extension FindAndReplaceKeyboardBar { + + func setupStaticAccessibilityLabels() { + findTextField.accessibilityLabel = WMFLocalizedString("find-textfield-accessibility", value: "Find", comment: "Accessibility label for the find text field.") + findClearButton.accessibilityLabel = WMFLocalizedString("find-clear-button-accessibility", value: "Clear find", comment: "Accessibility label for the clear values X button in the find textfield.") + closeButton.accessibilityLabel = CommonStrings.closeButtonAccessibilityLabel + nextButton.accessibilityLabel = WMFLocalizedString("find-next-button-accessibility", value: "Next find result", comment: "Accessibility label for the next button when traversing find results.") + previousButton.accessibilityLabel = WMFLocalizedString("find-previous-button-accessibility", value: "Previous find result", comment: "Accessibility label for the previous button when traversing find results.") + replaceTextField.accessibilityLabel = WMFLocalizedString("replace-textfield-accessibility", value: "Replace", comment: "Accessibility label for the replace text field.") + replaceTypeLabel.isAccessibilityElement = false + replacePlaceholderLabel.isAccessibilityElement = false + replaceClearButton.accessibilityLabel = WMFLocalizedString("replace-clear-button-accessibility", value: "Clear replace", comment: "Accessibility label for the clear values X button in the replace textfield.") + } + + func hideUndoRedoIcons() { + if findTextField.responds(to: #selector(getter: inputAssistantItem)) { + findTextField.inputAssistantItem.leadingBarButtonGroups = [] + findTextField.inputAssistantItem.trailingBarButtonGroups = [] + } + + if replaceTextField.responds(to: #selector(getter: inputAssistantItem)) { + replaceTextField.inputAssistantItem.leadingBarButtonGroups = [] + replaceTextField.inputAssistantItem.trailingBarButtonGroups = [] + } + } + + func resetReplace() { + replaceTextField.text = nil + replaceType = .replaceSingle + } + + func updateMatchPlacement(index: Int, total: UInt) { + + guard let findText = findTextField.text, + findText.count > 0 else { + currentMatchLabel.text = nil + return + } + + if total > 0 && index == -1 { + matchPlacement = FindMatchPlacement(index: nil, total: total) + } else { + matchPlacement = FindMatchPlacement(index: UInt(max(0, index + 1)), total: total) + } + } + + func updatePreviousNextButtonsState(total: UInt) { + previousButton.isEnabled = total >= 2 + nextButton.isEnabled = total >= 2 + } + + func updateReplaceLabelState() { + + let count = replaceTextField.text?.count ?? 0 + replacePlaceholderLabel.text = WMFLocalizedString("replace-textfield-placeholder", value: "Replace with…", comment: "Placeholder text seen in replace textfield before textfield is focused.") + + switch (replaceTextField.isFirstResponder, count) { + case (false, 0): + replaceTypeLabel.text = nil // niling out so longer placeholder strings will bump up against the X button + replaceTypeLabel.isHidden = true + replacePlaceholderLabel.isHidden = false + case (true, 0): + replaceTypeLabel.text = nil + replaceTypeLabel.isHidden = true + replacePlaceholderLabel.isHidden = true + case (_, 1...): + replaceTypeLabel.text = replaceType.text + replaceTypeLabel.isHidden = false + replacePlaceholderLabel.isHidden = true + default: + assertionFailure("Unexpected replace label state") + } + + let replaceMethodAccessibleFormat = WMFLocalizedString("replace-method-button-accessibility", value: "Replace method. Set to %1$@. Select to change.", comment: "Accessibility label for replace method switch button in Find and Replace. %1$@ is replaced by \"Replace single instance\" or \"Replace all instances\"") + replaceSwitchButton.accessibilityLabel = String.localizedStringWithFormat(replaceMethodAccessibleFormat, replaceType.accessibilityText) + + let replaceAccessibleFormat = WMFLocalizedString("replace-button-accessibility", value: "Perform %1$@.", comment: "Accessibility label for button that triggers replace action. %1$@ is replaced by \"Replace single instance\" or \"Replace all instances\"") + replaceButton.accessibilityLabel = String.localizedStringWithFormat(replaceAccessibleFormat, replaceType.accessibilityText) + } + + func updateReplaceButtonsState() { + let count = replaceTextField.text?.count ?? 0 + replaceButton.isEnabled = count > 0 && matchPlacement.total > 0 && replaceTextField.text != findTextField.text + replaceClearButton.isHidden = count == 0 + } + + func updateShowingReplaceState() { + + if isShowingReplace { + replaceStackView.isHidden = false + closeButton.isHidden = true + findStackView.addArrangedSubview(nextPrevButtonStackView) + outerStackViewLeadingConstraint.constant = 18 + outerStackViewTrailingConstraint.constant = 18 + } else { + replaceStackView.isHidden = true + closeButton.isHidden = false + findStackView.insertArrangedSubview(nextPrevButtonStackView, at: 0) + outerStackViewLeadingConstraint.constant = 10 + outerStackViewTrailingConstraint.constant = 5 + } + if isShowingReplace { + accessibilityElements = [findTextField, currentMatchLabel, findClearButton, previousButton, nextButton, replaceTextField, replaceClearButton, replaceButton, replaceSwitchButton].compactMap { $0 as Any } + } else { + accessibilityElements = [previousButton, nextButton, findTextField, currentMatchLabel, findClearButton, closeButton].compactMap { $0 as Any } + } + } +} + +#if TEST +// MARK: Helpers for testing +extension FindAndReplaceKeyboardBar { + func setFindTextForTesting(_ text: String) { + findTextField.text = text + textFieldDidChange(findTextField) + } + + func setReplaceTextForTesting(_ text: String) { + replaceTextField.text = text + textFieldDidChange(replaceTextField) + } + + func tapNextForTesting() { + tappedNext() + } + + func tapReplaceForTesting() { + tappedReplace() + } + + var matchPlacementForTesting: FindMatchPlacement { + return matchPlacement + } +} +#endif diff --git a/Apps/Wikipedia/Wikipedia/Code/FocusNavigationView.swift b/Apps/Wikipedia/Wikipedia/Code/FocusNavigationView.swift new file mode 100644 index 0000000..240ee38 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/FocusNavigationView.swift @@ -0,0 +1,45 @@ +import UIKit + +protocol FocusNavigationViewDelegate: AnyObject { + func focusNavigationViewDidTapClose(_ focusNavigationView: FocusNavigationView) +} + +final class FocusNavigationView: UIView { + + @IBOutlet var titleLabelVerticalConstraints: [NSLayoutConstraint]! + @IBOutlet private var titleLabel: UILabel! + @IBOutlet private var closeButton: UIButton! + @IBOutlet private var divView: UIView! + + weak var delegate: FocusNavigationViewDelegate? + + func configure(titleText: String, closeButtonAccessibilityText: String, traitCollection: UITraitCollection, isTitleAccessible: Bool = false) { + + titleLabel.text = titleText + titleLabel.isAccessibilityElement = isTitleAccessible + titleLabel.font = UIFont.wmf_font(.mediumHeadline, compatibleWithTraitCollection: traitCollection) + closeButton.accessibilityLabel = closeButtonAccessibilityText + + updateLayout(for: traitCollection) + } + + func updateLayout(for traitCollection: UITraitCollection) { + titleLabelVerticalConstraints.forEach { (constraint) in + constraint.constant = traitCollection.verticalSizeClass == .compact ? 5 : 15 + } + } + + @IBAction func tappedClose() { + delegate?.focusNavigationViewDidTapClose(self) + } + +} + +extension FocusNavigationView: Themeable { + func apply(theme: Theme) { + titleLabel.textColor = theme.colors.primaryText + closeButton.tintColor = theme.colors.secondaryText + backgroundColor = theme.colors.paperBackground + divView.backgroundColor = theme.colors.midBackground + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/FocusNavigationView.xib b/Apps/Wikipedia/Wikipedia/Code/FocusNavigationView.xib new file mode 100644 index 0000000..6b0cd6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/FocusNavigationView.xib @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/FontSizeSliderViewController.swift b/Apps/Wikipedia/Wikipedia/Code/FontSizeSliderViewController.swift new file mode 100644 index 0000000..da33734 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/FontSizeSliderViewController.swift @@ -0,0 +1,97 @@ +import UIKit + +@objc(WMFFontSizeSliderViewController) +class FontSizeSliderViewController: UIViewController { + + @IBOutlet fileprivate var slider: SWStepSlider! + + @IBOutlet weak var tSmallImageView: UIImageView! + @IBOutlet weak var tLargeImageView: UIImageView! + + fileprivate var maximumValue: Int? + fileprivate var currentValue: Int? + + fileprivate var theme = Theme.standard + + @objc static let WMFArticleFontSizeMultiplierKey = "WMFArticleFontSizeMultiplier" + @objc static let WMFArticleFontSizeUpdatedNotification = "WMFArticleFontSizeUpdatedNotification" + + let fontSizeMultipliers = [WMFFontSizeMultiplier.extraSmall, WMFFontSizeMultiplier.small, WMFFontSizeMultiplier.medium, WMFFontSizeMultiplier.large, WMFFontSizeMultiplier.extraLarge, WMFFontSizeMultiplier.extraExtraLarge, WMFFontSizeMultiplier.extraExtraExtraLarge] + + override func viewDidLoad() { + super.viewDidLoad() + + if let max = maximumValue { + if let current = currentValue { + setValues(0, maximum: max, current: current) + maximumValue = nil + currentValue = nil + } + } + apply(theme: self.theme) + + slider.isAccessibilityElement = true + slider.accessibilityTraits = UIAccessibilityTraits.adjustable + slider.accessibilityLabel = CommonStrings.textSizeSliderAccessibilityLabel + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + setValuesWithSteps(fontSizeMultipliers.count, current: indexOfCurrentFontSize()) + } + + func setValuesWithSteps(_ steps: Int, current: Int) { + if self.isViewLoaded { + setValues(0, maximum: steps - 1, current: current) + } else { + maximumValue = steps - 1 + currentValue = current + } + } + + func setValues(_ minimum: Int, maximum: Int, current: Int) { + slider.minimumValue = minimum + slider.maximumValue = maximum + slider.value = current + } + + @IBAction func fontSliderValueChanged(_ slider: SWStepSlider) { + if slider.value > fontSizeMultipliers.count { + return + } + + let multiplier = fontSizeMultipliers[slider.value].rawValue + let userInfo = [FontSizeSliderViewController.WMFArticleFontSizeMultiplierKey: multiplier] + + NotificationCenter.default.post(name: Notification.Name(FontSizeSliderViewController.WMFArticleFontSizeUpdatedNotification), object: nil, userInfo: userInfo) + + setValuesWithSteps(fontSizeMultipliers.count, current: indexOfCurrentFontSize()) + } + + func indexOfCurrentFontSize() -> Int { + if let fontSize = UserDefaults.standard.wmf_articleFontSizeMultiplier() as? Int, let multiplier = WMFFontSizeMultiplier(rawValue: fontSize) { + return fontSizeMultipliers.firstIndex(of: multiplier)! + } + return fontSizeMultipliers.count / 2 + } +} + +extension FontSizeSliderViewController: Themeable { + func apply(theme: Theme) { + self.theme = theme + + guard viewIfLoaded != nil else { + return + } + + view.backgroundColor = theme.colors.midBackground + slider.backgroundColor = theme.colors.midBackground + tSmallImageView.tintColor = theme.colors.secondaryText + tLargeImageView.tintColor = theme.colors.secondaryText + + if self.parent is AppearanceSettingsViewController { + view.backgroundColor = theme.colors.paperBackground + slider.backgroundColor = theme.colors.paperBackground + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/FontSizeSliderViewController.xib b/Apps/Wikipedia/Wikipedia/Code/FontSizeSliderViewController.xib new file mode 100644 index 0000000..09609c1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/FontSizeSliderViewController.xib @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/GlobalUserInfoFetcher.swift b/Apps/Wikipedia/Wikipedia/Code/GlobalUserInfoFetcher.swift new file mode 100644 index 0000000..daa0e6e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/GlobalUserInfoFetcher.swift @@ -0,0 +1,49 @@ +import Foundation + +enum GlobalUserInfoFetcherError: Error { + case cannotExtractMergedGroups + case unableToFindEditCount +} + +class GlobalUserInfoFetcher: Fetcher { + + func fetchEditCount(guiUser: String, siteURL: URL, completion: @escaping ((Result) -> Void)) { + + let parameters = [ + "action": "query", + "meta": "globaluserinfo", + "guiuser": guiUser, + "guiprop": "groups|merged|unattached", + "format": "json" + ] + + performMediaWikiAPIGET(for: siteURL, with: parameters, cancellationKey: nil) { (result, response, error) in + + if let error = error { + completion(.failure(error)) + return + } + + guard + let query = result?["query"] as? [String : Any], + let userinfo = query["globaluserinfo"] as? [String : Any], + let merged = userinfo["merged"] as? [[String: Any]] else { + completion(.failure(GlobalUserInfoFetcherError.cannotExtractMergedGroups)) + return + } + + for dict in merged { + if let responseURLString = dict["url"] as? String, + let responseURL = URL(string: responseURLString), + siteURL == responseURL, + let editCount = dict["editcount"] as? Int { + + completion(.success(editCount)) + return + } + } + + completion(.failure(GlobalUserInfoFetcherError.unableToFindEditCount)) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/GroupedAccessibilityView.swift b/Apps/Wikipedia/Wikipedia/Code/GroupedAccessibilityView.swift new file mode 100644 index 0000000..43acf73 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/GroupedAccessibilityView.swift @@ -0,0 +1,71 @@ +import UIKit + +class GroupedAccessibilityView: UIView { + + var arrangedAccessibilityViews: [UIView] = [] + + // convienene outlets + @IBOutlet weak var accessibilityView0: UIView? + @IBOutlet weak var accessibilityView1: UIView? + @IBOutlet weak var accessibilityView2: UIView? + @IBOutlet weak var accessibilityView3: UIView? + + override func awakeFromNib() { + super.awakeFromNib() + wmf_disableSubviewAccessibility() + isAccessibilityElement = true + accessibilityTraits = UIAccessibilityTraits.link + + guard let first = accessibilityView0 else { + return + } + arrangedAccessibilityViews.append(first) + + guard let second = accessibilityView1 else { + return + } + arrangedAccessibilityViews.append(second) + + guard let third = accessibilityView2 else { + return + } + arrangedAccessibilityViews.append(third) + + guard let fourth = accessibilityView3 else { + return + } + arrangedAccessibilityViews.append(fourth) + } + + override var accessibilityLabel: String? { + get { + var combinedAccessibilityLabel = "" + if let superLabel = super.accessibilityLabel { + combinedAccessibilityLabel += superLabel + "\n" + } + for view in arrangedAccessibilityViews { + guard let accessibilityLabel = view.accessibilityLabel else { + continue + } + combinedAccessibilityLabel += accessibilityLabel + "\n" + } + return combinedAccessibilityLabel + } + set { + super.accessibilityLabel = newValue + } + } + +} + + +extension UIView { + + func wmf_disableSubviewAccessibility() { + for view in subviews { + view.isAccessibilityElement = false + view.wmf_disableSubviewAccessibility() + } + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/HTTPCookieStorage+Migration.swift b/Apps/Wikipedia/Wikipedia/Code/HTTPCookieStorage+Migration.swift new file mode 100644 index 0000000..b4c28fd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/HTTPCookieStorage+Migration.swift @@ -0,0 +1,39 @@ +@objc extension HTTPCookieStorage { + public func cookiesWithNamePrefix(_ prefix: String, for domain: String) -> [HTTPCookie] { + guard let cookies = cookies, !cookies.isEmpty else { + return [] + } + let standardizedPrefix = prefix.lowercased().precomposedStringWithCanonicalMapping + let standardizedDomain = domain.lowercased().precomposedStringWithCanonicalMapping + return cookies.filter({ (cookie) -> Bool in + return cookie.domain.lowercased().precomposedStringWithCanonicalMapping == standardizedDomain && cookie.name.lowercased().precomposedStringWithCanonicalMapping.hasPrefix(standardizedPrefix) + }) + } + + public func copyCookiesWithNamePrefix(_ prefix: String, for domain: String, to toDomains: [String]) { + let cookies = cookiesWithNamePrefix(prefix, for: domain) + for toDomain in toDomains { + for cookie in cookies { + var properties = cookie.properties ?? [:] + properties[.domain] = toDomain + guard let copiedCookie = HTTPCookie(properties: properties) else { + continue + } + setCookie(copiedCookie) + } + } + } + + @objc public static func migrateCookiesToSharedStorage() { + let legacyStorage = HTTPCookieStorage.shared + let sharedStorage = Session.sharedCookieStorage + guard let legacyCookies = legacyStorage.cookies else { + return + } + + for legacyCookie in legacyCookies { + sharedStorage.setCookie(legacyCookie) + legacyStorage.deleteCookie(legacyCookie) + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/HelpViewController.swift b/Apps/Wikipedia/Wikipedia/Code/HelpViewController.swift new file mode 100644 index 0000000..8c90096 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/HelpViewController.swift @@ -0,0 +1,347 @@ +import MessageUI +import CocoaLumberjackSwift +import WMF + +@objc(WMFHelpViewController) +class HelpViewController: SinglePageWebViewController { + static let faqURLString = "https://m.mediawiki.org/wiki/Wikimedia_Apps/iOS_FAQ" + static let emailAddress = "ios-support@wikimedia.org" + static let emailSubject = "Bug:" + let dataStore: MWKDataStore + + @objc init?(dataStore: MWKDataStore, theme: Theme) { + guard let faqURL = URL(string: HelpViewController.faqURLString) else { + return nil + } + self.dataStore = dataStore + super.init(url: faqURL, theme: theme) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + required init(url: URL, theme: Theme) { + fatalError("init(url:theme:) has not been implemented") + } + + required init(url: URL, theme: Theme, doesUseSimpleNavigationBar: Bool = false) { + fatalError("init(url:theme:doesUseSimpleNavigationBar:) has not been implemented") + } + + lazy var sendEmailToolbarItem: UIBarButtonItem = { + return UIBarButtonItem(title: WMFLocalizedString("button-report-a-bug", value: "Report a bug", comment: "Button text for reporting a bug"), style: .plain, target: self, action: #selector(sendEmail)) + }() + + lazy var exportUserDataToolbarItem: UIBarButtonItem = { + return UIBarButtonItem(title: WMFLocalizedString("export-user-data-title", value: "Export User Data", comment: "Button title for exporting user data"), style: .plain, target: self, action: #selector(exportUserData)) + }() + + lazy var activityIndicator: UIActivityIndicatorView = { + let activityIndicator = UIActivityIndicatorView() + activityIndicator.color = theme.colors.primaryText + return activityIndicator + }() + + lazy var spinnerToolbarItem: UIBarButtonItem = { + return UIBarButtonItem(customView: activityIndicator) + }() + + override func viewDidLoad() { + super.viewDidLoad() + navigationItem.rightBarButtonItem = nil + setupToolbar() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + } + + private func setupToolbarItems(isExportingUserData: Bool) { + + let exportItem = isExportingUserData ? spinnerToolbarItem : exportUserDataToolbarItem + if isExportingUserData { + activityIndicator.startAnimating() + } else { + activityIndicator.stopAnimating() + } + + let leadingSpace: CGFloat = isExportingUserData ? 30 : 8 + + self.toolbar.items = [UIBarButtonItem.wmf_barButtonItem(ofFixedWidth: leadingSpace), exportItem, UIBarButtonItem.flexibleSpaceToolbar(), sendEmailToolbarItem, UIBarButtonItem.wmf_barButtonItem(ofFixedWidth: 8)] + } + + private func setupToolbar() { + enableToolbar() + setupToolbarItems(isExportingUserData: false) + setToolbarHidden(false, animated: false) + } + + enum UserDataExportError: Error { + case unableToDetectSystemFreeSpace + case unableToDetectLogSize + } + + @objc func exportUserData() { + + let confirmationTitle = WMFLocalizedString("export-user-data-confirmation-title", value: "Share app library?", comment: "Title of confirmation modal after user taps \"Export User Data\" button.") + let confirmationMessage = WMFLocalizedString("export-user-data-confirmation-message", value: "Sharing your app library includes data about your Reading lists and history, preferences, and Explore feed content. This data file should only be shared with a trusted recipient to use for technical diagnostic purposes.", comment: "Message of confirmation modal after user taps \"Export User Data\" button.") + let shareAction = UIAlertAction(title: CommonStrings.shareActionTitle, style: .default) { _ in + self.kickoffExportUserDataProcess() + } + let cancelAction = UIAlertAction(title: CommonStrings.cancelActionTitle, style: .cancel) + wmf_showAlert(title: confirmationTitle, message: confirmationMessage, actions: [shareAction, cancelAction]) + } + + @objc func sendEmail() { + let address = HelpViewController.emailAddress + let subject = HelpViewController.emailSubject + let body = "\n\n\n\nVersion: \(WikipediaAppUtils.versionedUserAgent())" + let mailto = "mailto:\(address)?subject=\(subject)&body=\(body)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) + + guard let encodedMailto = mailto, let mailtoURL = URL(string: encodedMailto), UIApplication.shared.canOpenURL(mailtoURL) else { + WMFAlertManager.sharedInstance.showErrorAlertWithMessage(CommonStrings.noEmailClient, sticky: false, dismissPreviousAlerts: false) + return + } + + UIApplication.shared.open(mailtoURL) + } + +} + +extension HelpViewController: FileManagerDelegate { + func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, copyingItemAt srcURL: URL, to dstURL: URL) -> Bool { + return true + } + + func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, copyingItemAtPath srcPath: String, toPath dstPath: String) -> Bool { + return true + } +} + +// MARK: Exporting User Data Helpers + +private extension HelpViewController { + func userHasEnoughSpace(containerURL: URL, logFilePath: String?) throws -> Bool { + + let systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String) + + guard let systemFreeSpace = systemAttributes[.systemFreeSize] as? Int64 else { + throw UserDataExportError.unableToDetectSystemFreeSpace + } + + var logSize: Int64 = 0 + if let logFilePath = logFilePath { + let logAttributes = try FileManager.default.attributesOfItem(atPath: logFilePath) + guard let unwrappedLogSize = logAttributes[.size] as? Int64 else { + throw UserDataExportError.unableToDetectLogSize + } + + logSize = unwrappedLogSize + } + + let containerSize = FileManager.default.sizeOfDirectory(at: containerURL) + + return systemFreeSpace > containerSize + logSize + } + + func saveSyncedReadingListResultsToAppContainer(completion: @escaping () -> Void) { + guard dataStore.authenticationManager.isLoggedIn else { + completion() + return + } + + let readingListsController = self.dataStore.readingListsController + let apiController = readingListsController.apiController + let appSettingsShowSavedReadingList = readingListsController.isDefaultListEnabled + let appSettingsSyncSavedArticlesAndLists = readingListsController.isSyncEnabled + + let dispatchQueue = DispatchQueue.global(qos: .userInitiated) + dispatchQueue.async { + + let sharedCache = SharedContainerCache.init(fileName: "User Data Export Sync Info", defaultCache: { UserDataExportSyncInfo(serverReadingLists: [], serverReadingListEntries: [], appSettingsSyncSavedArticlesAndLists: false, appSettingsShowSavedReadingList: false) }) + + apiController.getAllReadingLists { (serverReadingLists, _, _) in + dispatchQueue.async { + let group = DispatchGroup() + + var serverReadingListEntries: [APIReadingListEntry] = [] + + for remoteReadingList in serverReadingLists { + group.enter() + apiController.getAllEntriesForReadingListWithID(readingListID: remoteReadingList.id, completion: { (entries, error) in + dispatchQueue.async { + serverReadingListEntries.append(contentsOf: entries) + group.leave() + } + }) + } + + group.notify(queue: dispatchQueue) { + let userDataExportSyncInfo = UserDataExportSyncInfo(serverReadingLists: serverReadingLists, serverReadingListEntries: serverReadingListEntries, appSettingsSyncSavedArticlesAndLists: appSettingsSyncSavedArticlesAndLists, appSettingsShowSavedReadingList: appSettingsShowSavedReadingList) + sharedCache.saveCache(userDataExportSyncInfo) + completion() + } + } + } + } + } + + func copyLogFile(temporaryAppContainerURL: URL, fileManager: FileManager) { + let newLogURL = temporaryAppContainerURL.appendingPathComponent("console.log") + + if let logFilePath = DDLog.wmf_currentLogFilePath() { + do { + let logFileURL = URL(fileURLWithPath: logFilePath) + try fileManager.copyItem(at: logFileURL, to: newLogURL) + } catch let error { + DDLogError("Error moving log file to app container: \(error)") + } + } + } + + func removeUnnecessaryFilesAndSubdirectories(temporaryAppContainerURL: URL, fileManager: FileManager) { + var urlsToRemove: [URL] = [] + urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("Event Logging")) + urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("Event Platform")) + urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("Library")) + urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent(SharedContainerCacheCommonNames.pushNotificationsCache).appendingPathExtension("json")) + urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("RemoteNotifications").appendingPathExtension("sqlite")) + urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("RemoteNotifications").appendingPathExtension("sqlite-shm")) + urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent("RemoteNotifications").appendingPathExtension("sqlite-wal")) + urlsToRemove.append(temporaryAppContainerURL.appendingPathComponent(SharedContainerCacheCommonNames.widgetCache).appendingPathExtension("json")) + for url in urlsToRemove { + do { + try fileManager.removeItem(at: url) + } catch let error { + DDLogError("Error deleting unnecessary files from app container: \(error)") + } + } + } + + func zipUpAndShare(temporaryAppContainerURL: URL, fileManager: FileManager) { + + // Inspired by https://recoursive.com/2021/02/25/create_zip_archive_using_only_foundation/ + var zipURL: URL? + var error: NSError? + + let coordinator = NSFileCoordinator() + + coordinator.coordinate(readingItemAt: temporaryAppContainerURL, options: [.forUploading], error: &error) { (url) in + + do { + zipURL = try fileManager.url( + for: .itemReplacementDirectory, + in: .userDomainMask, + appropriateFor: url, + create: true + ).appendingPathComponent("app-container.zip") + + if let zipURL = zipURL { + try fileManager.moveItem(at: url, to: zipURL) + } + + + } catch let error { + DDLogError("Error moving zip file to temporary directory: \(error)") + DispatchQueue.main.async { + self.handleGenericUserExportError() + } + return + } + + } + + if let zipURL = zipURL, + error == nil { + DispatchQueue.main.async { + self.setupToolbarItems(isExportingUserData: false) + let avc = UIActivityViewController(activityItems: [zipURL], applicationActivities: nil) + avc.popoverPresentationController?.barButtonItem = self.exportUserDataToolbarItem + self.present(avc, animated: true) + } + } else { + if let error = error { + DDLogError("Error zipping up app container: \(error)") + } else { + DDLogError("Error zipping up app container.") + } + + DispatchQueue.main.async { + self.handleGenericUserExportError() + } + } + + do { + try fileManager.removeItem(at: temporaryAppContainerURL) + } catch let error { + DDLogError("Error deleting temp app container: \(error).") + } + + fileManager.delegate = nil + } + + private func handleGenericUserExportError() { + self.wmf_showAlertWithMessage(WMFLocalizedString("export-user-data-generic-error", value: "There was an error while exporting your data. Please try again later.", comment: "Error message displayed after user has tried exporting their data and an error occurs.")) + self.setupToolbarItems(isExportingUserData: false) + } + + func kickoffExportUserDataProcess() { + // Set export button to spinner + setupToolbarItems(isExportingUserData: true) + + saveSyncedReadingListResultsToAppContainer(completion: { + + DispatchQueue.global(qos: .userInitiated).async { [weak self] in + + guard let self = self else { + return + } + + FileManager.default.delegate = self + + let fileManager = FileManager.default + let containerURL = fileManager.wmf_containerURL() + let logFilePath = DDLog.wmf_currentLogFilePath() + + // Because we'll be copying the app container over to something that can be zipped up, we must check that we have at least enough free space for another container + log file + var enoughSpace: Bool = false + do { + enoughSpace = try self.userHasEnoughSpace(containerURL: containerURL, logFilePath: logFilePath) + } catch { + DDLogError("Error determining if there's enough free space: \(error)") + DispatchQueue.main.async { + self.handleGenericUserExportError() + } + return + } + + guard enoughSpace else { + DispatchQueue.main.async { + self.wmf_showAlertWithMessage(WMFLocalizedString("export-user-data-space-error", value: "You do not have enough device space to export your user data. Please try again later.", comment: "Error message displayed after user has tried exporting their data and the system determines it doesn't have space to do so.")) + self.setupToolbarItems(isExportingUserData: false) + } + return + } + + // First copy container into temporary directory + let temporaryAppContainerURL = fileManager.temporaryDirectory.appendingPathComponent(WMFApplicationGroupIdentifier) + + do { + try fileManager.copyItem(at: containerURL, to: temporaryAppContainerURL) + } catch let error { + DDLogError("Error copying container into temporary directory: \(error)") + DispatchQueue.main.async { + self.handleGenericUserExportError() + } + return + } + + // Add log file, prune, zip and export temporary app container directory + self.copyLogFile(temporaryAppContainerURL: temporaryAppContainerURL, fileManager: fileManager) + self.removeUnnecessaryFilesAndSubdirectories(temporaryAppContainerURL: temporaryAppContainerURL, fileManager: fileManager) + self.zipUpAndShare(temporaryAppContainerURL: temporaryAppContainerURL, fileManager: fileManager) + } + }) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/HintController.swift b/Apps/Wikipedia/Wikipedia/Code/HintController.swift new file mode 100644 index 0000000..8288db4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/HintController.swift @@ -0,0 +1,225 @@ +@objc(WMFHintPresenting) +protocol HintPresenting: AnyObject { + var hintController: HintController? { get set } +} + +class HintController: NSObject { + typealias Context = [String: Any] + + typealias HintPresentingViewController = UIViewController & HintPresenting + private weak var presenter: HintPresentingViewController? + + private let hintViewController: HintViewController + + private var containerView = UIView() + private var containerViewConstraint: (top: NSLayoutConstraint?, bottom: NSLayoutConstraint?) + + private var task: DispatchWorkItem? + + var theme = Theme.standard + + // if true, hint will extend below safe area to the bottom of the view, and hint content within will align to safe area + // must also override extendsUnderSafeArea to true in HintViewController + var extendsUnderSafeArea: Bool { + return false + } + + init(hintViewController: HintViewController) { + self.hintViewController = hintViewController + super.init() + hintViewController.delegate = self + } + + var isHintHidden: Bool { + return containerView.superview == nil + } + + private var hintVisibilityTime: TimeInterval = 13 { + didSet { + guard hintVisibilityTime != oldValue else { + return + } + dismissHint() + } + } + + func dismissHint(completion: (() -> Void)? = nil) { + self.task?.cancel() + let task = DispatchWorkItem { [weak self] in + self?.setHintHidden(true) + if let completion = completion { + completion() + } + } + DispatchQueue.main.asyncAfter(deadline: .now() + hintVisibilityTime , execute: task) + self.task = task + } + + @objc func toggle(presenter: HintPresentingViewController, context: Context?, theme: Theme) { + self.presenter = presenter + apply(theme: theme) + } + + func toggle(presenter: HintPresentingViewController, context: Context?, theme: Theme, subview: UIView? = nil, additionalBottomSpacing: CGFloat = 0, setPrimaryColor: ((inout UIColor?) -> Void)? = nil, setBackgroundColor: ((inout UIColor?) -> Void)? = nil) { + self.subview = subview + self.additionalBottomSpacing = additionalBottomSpacing + setPrimaryColor?(&hintViewController.primaryColor) + setBackgroundColor?(&hintViewController.backgroundColor) + self.presenter = presenter + apply(theme: theme) + } + + private var subview: UIView? + private var additionalBottomSpacing: CGFloat = 0 + + private func addHint(to presenter: HintPresentingViewController) { + guard isHintHidden else { + return + } + + containerView.translatesAutoresizingMaskIntoConstraints = false + + var bottomAnchor: NSLayoutYAxisAnchor = extendsUnderSafeArea ? presenter.view.bottomAnchor : presenter.view.safeAreaLayoutGuide.bottomAnchor + + if let wmfVCPresenter = presenter as? ViewController { // not ideal, violates encapsulation + wmfVCPresenter.view.insertSubview(containerView, belowSubview: wmfVCPresenter.toolbar) + if !wmfVCPresenter.isToolbarHidden && wmfVCPresenter.toolbar.superview != nil { + bottomAnchor = wmfVCPresenter.toolbar.topAnchor + } + } else if let subview = subview { + presenter.view.insertSubview(containerView, belowSubview: subview) + } else { + presenter.view.addSubview(containerView) + } + + // `containerBottomConstraint` is activated when the hint is visible + containerViewConstraint.bottom = containerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0 - additionalBottomSpacing) + + // `containerTopConstraint` is activated when the hint is hidden + containerViewConstraint.top = containerView.topAnchor.constraint(equalTo: bottomAnchor) + + let leadingConstraint = containerView.leadingAnchor.constraint(equalTo: presenter.view.leadingAnchor) + let trailingConstraint = containerView.trailingAnchor.constraint(equalTo: presenter.view.trailingAnchor) + + NSLayoutConstraint.activate([containerViewConstraint.top!, leadingConstraint, trailingConstraint]) + + if presenter.isKind(of: SearchResultsViewController.self) { + presenter.wmf_hideKeyboard() + } + + hintViewController.view.setContentHuggingPriority(.required, for: .vertical) + hintViewController.view.setContentCompressionResistancePriority(.required, for: .vertical) + containerView.setContentHuggingPriority(.required, for: .vertical) + containerView.setContentCompressionResistancePriority(.required, for: .vertical) + + presenter.wmf_add(childController: hintViewController, andConstrainToEdgesOfContainerView: containerView) + + containerView.superview?.layoutIfNeeded() + } + + private func removeHint() { + task?.cancel() + hintViewController.willMove(toParent: nil) + hintViewController.view.removeFromSuperview() + hintViewController.removeFromParent() + containerView.removeFromSuperview() + resetHint() + } + + func resetHint() { + hintVisibilityTime = 13 + hintViewController.viewType = .default + } + + func setHintHidden(_ hidden: Bool, completion: (() -> Void)? = nil) { + guard + isHintHidden != hidden, + let presenter = presenter, + presenter.presentedViewController == nil + else { + if let completion = completion { + completion() + } + return + } + + presenter.hintController = self + + if !hidden { + // add hint before animation starts + addHint(to: presenter) + } + + self.adjustSpacingIfPresenterHasSecondToolbar(hintHidden: hidden) + + UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut], animations: { + if hidden { + self.containerViewConstraint.bottom?.isActive = false + self.containerViewConstraint.top?.isActive = true + } else { + self.containerViewConstraint.top?.isActive = false + self.containerViewConstraint.bottom?.isActive = true + } + self.containerView.superview?.layoutIfNeeded() + }, completion: { (_) in + // remove hint after animation is completed + if hidden { + self.adjustSpacingIfPresenterHasSecondToolbar(hintHidden: hidden) + self.removeHint() + if let completion = completion { + completion() + } + } else { + self.dismissHint(completion: completion) + } + }) + } + + @objc func dismissHintDueToUserInteraction() { + guard !self.isHintHidden else { + return + } + self.hintVisibilityTime = 0 + } + + private func adjustSpacingIfPresenterHasSecondToolbar(hintHidden: Bool) { + guard + let viewController = presenter as? ViewController, + !viewController.isSecondToolbarHidden && !hintHidden + else { + return + } + viewController.setSecondToolbarHidden(true, animated: true) + } +} + +extension HintController: HintViewControllerDelegate { + func hintViewControllerWillDisappear(_ hintViewController: HintViewController) { + setHintHidden(true) + } + + func hintViewControllerHeightDidChange(_ hintViewController: HintViewController) { + adjustSpacingIfPresenterHasSecondToolbar(hintHidden: isHintHidden) + } + + func hintViewControllerViewTypeDidChange(_ hintViewController: HintViewController, newViewType: HintViewController.ViewType) { + guard newViewType == .confirmation else { + return + } + setHintHidden(false) + } + + func hintViewControllerDidPeformConfirmationAction(_ hintViewController: HintViewController) { + setHintHidden(true) + } + + func hintViewControllerDidFailToCompleteDefaultAction(_ hintViewController: HintViewController) { + setHintHidden(true) + } +} + +extension HintController: Themeable { + func apply(theme: Theme) { + hintViewController.apply(theme: theme) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/HintViewController.swift b/Apps/Wikipedia/Wikipedia/Code/HintViewController.swift new file mode 100644 index 0000000..9d86796 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/HintViewController.swift @@ -0,0 +1,142 @@ +import UIKit + +protocol HintViewControllerDelegate: AnyObject { + func hintViewControllerWillDisappear(_ hintViewController: HintViewController) + func hintViewControllerHeightDidChange(_ hintViewController: HintViewController) + func hintViewControllerViewTypeDidChange(_ hintViewController: HintViewController, newViewType: HintViewController.ViewType) + func hintViewControllerDidPeformConfirmationAction(_ hintViewController: HintViewController) + func hintViewControllerDidFailToCompleteDefaultAction(_ hintViewController: HintViewController) +} + +class HintViewController: UIViewController { + @IBOutlet weak var defaultView: UIView! + @IBOutlet weak var defaultLabel: UILabel! + @IBOutlet weak var defaultImageView: UIImageView! + + @IBOutlet weak var confirmationView: UIView! + @IBOutlet weak var confirmationLabel: UILabel! + @IBOutlet weak var confirmationImageView: UIImageView! + @IBOutlet weak var confirmationAccessoryButton: UIButton! + + @IBOutlet weak var warningView: UIView! + @IBOutlet weak var warningLabel: UILabel! + @IBOutlet weak var warningSubtitleLabel: UILabel! + + @IBOutlet var safeAreaBottomConstraint: NSLayoutConstraint! + @IBOutlet var viewBottomConstraint: NSLayoutConstraint! + + var backgroundColor: UIColor? + var primaryColor: UIColor? + + // if true, hint will extend below safe area to the bottom of the view, and hint content within will align to safe area + // must also override extendsUnderSafeArea to true in HintController + var extendsUnderSafeArea: Bool { + return false + } + + weak var delegate: HintViewControllerDelegate? + + var theme = Theme.standard + + enum ViewType { + case `default` + case confirmation + case warning + } + + var viewType: ViewType = .default { + didSet { + switch viewType { + case .default: + warningView.isHidden = true + confirmationView.isHidden = true + defaultView.isHidden = false + case .confirmation: + warningView.isHidden = true + confirmationView.isHidden = false + defaultView.isHidden = true + case .warning: + warningView.isHidden = false + confirmationView.isHidden = true + defaultView.isHidden = true + } + delegate?.hintViewControllerViewTypeDidChange(self, newViewType: viewType) + } + } + + override var nibName: String? { + return "HintViewController" + } + + override func viewDidLoad() { + super.viewDidLoad() + configureSubviews() + apply(theme: theme) + let isRTL = view.effectiveUserInterfaceLayoutDirection == .rightToLeft + confirmationAccessoryButton.imageView?.transform = isRTL ? CGAffineTransform(scaleX: -1, y: 1) : CGAffineTransform.identity + + safeAreaBottomConstraint.isActive = extendsUnderSafeArea + viewBottomConstraint.isActive = !extendsUnderSafeArea + + updateFonts() + + view.setNeedsLayout() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + delegate?.hintViewControllerWillDisappear(self) + } + + private var previousHeight: CGFloat = 0.0 + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + if previousHeight != view.frame.size.height { + delegate?.hintViewControllerHeightDidChange(self) + } + previousHeight = view.frame.size.height + } + + open func configureSubviews() { + + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + defaultLabel.font = UIFont.wmf_font(.mediumSubheadline, compatibleWithTraitCollection: traitCollection) + confirmationLabel.font = UIFont.wmf_font(.mediumSubheadline, compatibleWithTraitCollection: traitCollection) + warningLabel.font = UIFont.wmf_font(.mediumSubheadline, compatibleWithTraitCollection: traitCollection) + warningSubtitleLabel.font = UIFont.wmf_font(.caption1, compatibleWithTraitCollection: traitCollection) + } +} + +extension HintViewController { + @IBAction open func performDefaultAction(sender: Any) { + + } + + @IBAction open func performConfirmationAction(sender: Any) { + + } +} + +extension HintViewController: Themeable { + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + + view.backgroundColor = backgroundColor ?? (viewType == .warning ? theme.colors.hintWarningBackground : theme.colors.hintBackground) + defaultLabel?.textColor = primaryColor ?? theme.colors.link + confirmationLabel?.textColor = primaryColor ?? theme.colors.link + confirmationAccessoryButton.tintColor = primaryColor ?? theme.colors.link + defaultImageView.tintColor = primaryColor ?? theme.colors.link + warningLabel?.textColor = primaryColor ?? theme.colors.hintWarningText + warningSubtitleLabel?.textColor = primaryColor ?? theme.colors.primaryText + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/HintViewController.xib b/Apps/Wikipedia/Wikipedia/Code/HintViewController.xib new file mode 100644 index 0000000..e51e5dd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/HintViewController.xib @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/HistoryViewController.swift b/Apps/Wikipedia/Wikipedia/Code/HistoryViewController.swift new file mode 100644 index 0000000..d8a1d0a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/HistoryViewController.swift @@ -0,0 +1,97 @@ +import UIKit +import WMF + +@objc(WMFHistoryViewController) +class HistoryViewController: ArticleFetchedResultsViewController { + + override func setupFetchedResultsController(with dataStore: MWKDataStore) { + let articleRequest = WMFArticle.fetchRequest() + articleRequest.predicate = NSPredicate(format: "viewedDate != NULL") + articleRequest.sortDescriptors = [NSSortDescriptor(keyPath: \WMFArticle.viewedDateWithoutTime, ascending: false), NSSortDescriptor(keyPath: \WMFArticle.viewedDate, ascending: false)] + fetchedResultsController = NSFetchedResultsController(fetchRequest: articleRequest, managedObjectContext: dataStore.viewContext, sectionNameKeyPath: "viewedDateWithoutTime", cacheName: nil) + } + + override func viewDidLoad() { + super.viewDidLoad() + navigationBar.isBarHidingEnabled = false + navigationBar.isShadowHidingEnabled = true + navigationBar.displayType = .largeTitle + + emptyViewType = .noHistory + + title = CommonStrings.historyTabTitle + + deleteAllButtonText = WMFLocalizedString("history-clear-all", value: "Clear", comment: "Text of the button shown at the top of history which deletes all history {{Identical|Clear}}") + deleteAllConfirmationText = WMFLocalizedString("history-clear-confirmation-heading", value: "Are you sure you want to delete all your recent items?", comment: "Heading text of delete all confirmation dialog") + deleteAllCancelText = WMFLocalizedString("history-clear-cancel", value: "Cancel", comment: "Button text for cancelling delete all action {{Identical|Cancel}}") + deleteAllText = WMFLocalizedString("history-clear-delete-all", value: "Yes, delete all", comment: "Button text for confirming delete all action") + isDeleteAllVisible = true + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + collectionViewUpdater.isGranularUpdatingEnabled = true + + // Terrible hack to make back button text appropriate for iOS 14 - need to set the title on `WMFAppViewController`. For all app tabs, this is set in `viewWillAppear`. + (parent as? WMFAppViewController)?.navigationItem.backButtonTitle = title + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + NSUserActivity.wmf_makeActive(NSUserActivity.wmf_recentView()) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + collectionViewUpdater.isGranularUpdatingEnabled = false + } + + override func deleteAll() { + do { + try dataStore.viewContext.clearReadHistory() + } catch let error { + showError(error) + } + } + + override var headerStyle: ColumnarCollectionViewController.HeaderStyle { + return .sections + } + + func titleForHeaderInSection(_ section: Int) -> String? { + guard let sections = fetchedResultsController.sections, sections.count > section else { + return nil + } + let sectionInfo = sections[section] + guard let article = sectionInfo.objects?.first as? WMFArticle, let date = article.viewedDateWithoutTime else { + return nil + } + + return ((date as NSDate).wmf_midnightUTCDateFromLocal as NSDate).wmf_localizedRelativeDateFromMidnightUTCDate() + } + + override func configure(header: CollectionViewHeader, forSectionAt sectionIndex: Int, layoutOnly: Bool) { + header.style = .history + header.title = titleForHeaderInSection(sectionIndex) + header.apply(theme: theme) + header.layoutMargins = layout.itemLayoutMargins + } + + override func collectionViewUpdater(_ updater: CollectionViewUpdater, didUpdate collectionView: UICollectionView) { + super.collectionViewUpdater(updater, didUpdate: collectionView) + updateVisibleHeaders() + } + + func updateVisibleHeaders() { + for indexPath in collectionView.indexPathsForVisibleSupplementaryElements(ofKind: UICollectionView.elementKindSectionHeader) { + guard let headerView = collectionView.supplementaryView(forElementKind: UICollectionView.elementKindSectionHeader, at: indexPath) as? CollectionViewHeader else { + continue + } + headerView.title = titleForHeaderInSection(indexPath.section) + } + } + + override var eventLoggingCategory: EventLoggingCategory { + return .history + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/HorizontalSpacerView.swift b/Apps/Wikipedia/Wikipedia/Code/HorizontalSpacerView.swift new file mode 100644 index 0000000..f925d63 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/HorizontalSpacerView.swift @@ -0,0 +1,31 @@ +import UIKit + +/// A horizontal spacer (conceptually similar to SwiftUI's `Spacer`) +class HorizontalSpacerView: SetupView { + + // MARK: - Properties + + var space: CGFloat = 0 { + didSet { + spaceWidthAnchor?.constant = space + } + } + + fileprivate var spaceWidthAnchor: NSLayoutConstraint? + + // MARK: - Setup + + override func setup() { + spaceWidthAnchor = widthAnchor.constraint(equalToConstant: space) + spaceWidthAnchor?.isActive = true + } + + // MARK: - Factory + + static func spacerWith(space: CGFloat) -> HorizontalSpacerView { + let spacer = HorizontalSpacerView() + spacer.space = space + return spacer + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/IconBarButtonItem.swift b/Apps/Wikipedia/Wikipedia/Code/IconBarButtonItem.swift new file mode 100644 index 0000000..d1d9676 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/IconBarButtonItem.swift @@ -0,0 +1,46 @@ +import UIKit +class IconBarButtonItem: UIBarButtonItem { + + private var theme: Theme? + + @objc convenience init(iconName: String, target: Any?, action: Selector, for controlEvents: UIControl.Event) { + self.init(iconName: iconName, target: target, action: action, for: controlEvents, iconInsets: .zero) + } + + @objc convenience init(iconName: String, target: Any?, action: Selector, for controlEvents: UIControl.Event, iconInsets: UIEdgeInsets) { + let image = UIImage(named: iconName) + let customView = UIButton(type: .system) + customView.setImage(image, for: .normal) + customView.addTarget(target, action: action, for: controlEvents) + customView.adjustsImageWhenDisabled = false + if iconInsets != .zero { customView.imageEdgeInsets = iconInsets } + self.init(customView: customView) + } + + @objc convenience init(systemName: String, target: Any?, action: Selector, for controlEvent: UIControl.Event = .primaryActionTriggered) { + self.init(systemName: systemName, target: target, action: action, for: controlEvent) + } + + override var isEnabled: Bool { + get { + return super.isEnabled + } set { + super.isEnabled = newValue + + if let theme = theme { + apply(theme: theme) + } + } + } +} + +extension IconBarButtonItem: Themeable { + public func apply(theme: Theme) { + self.theme = theme + if let customView = customView as? UIButton { + customView.tintColor = isEnabled ? theme.colors.link : theme.colors.disabledLink + } else { + tintColor = isEnabled ? theme.colors.link : theme.colors.disabledLink + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/IconTitleBadge.swift b/Apps/Wikipedia/Wikipedia/Code/IconTitleBadge.swift new file mode 100644 index 0000000..5b61852 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/IconTitleBadge.swift @@ -0,0 +1,102 @@ +import UIKit + +class IconTitleBadge: SizeThatFitsView { + + enum Icon { + case sfSymbol(name: String) + case custom(name: String) + } + + struct Configuration { + let title: String + let icon: Icon + } + + private var iconImageView: UIImageView? + private let titleLabel = UILabel() + private let configuration: Configuration + private var theme: Theme? + + init(configuration: Configuration, frame: CGRect) { + self.configuration = configuration + super.init(frame: frame) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + + let padding = UIEdgeInsets(top: 3, left: 5, bottom: 3, right: 5) + let maximumWidth = size.width - padding.left - padding.right + + var x = padding.left + var y = padding.top + + let imageTitleSpacing: CGFloat = 5 + + var imageFrame: CGRect? + if let imageView = iconImageView { + let imageOrigin = CGPoint(x: x, y: y) + imageFrame = imageView.wmf_preferredFrame(at: imageOrigin, maximumWidth: maximumWidth, alignedBy: semanticContentAttribute, apply: apply) + x += (imageFrame?.width ?? 0) + imageTitleSpacing + } + + let titleOrigin = CGPoint(x: x, y: y) + let titleFrame = titleLabel.wmf_preferredFrame(at: titleOrigin, maximumWidth: maximumWidth, alignedBy: semanticContentAttribute, apply: apply) + + x += titleFrame.width + padding.right + y += max(titleFrame.height, imageFrame?.height ?? 0) + y += padding.bottom + + return CGSize(width: x, height: y) + } + + override func setup() { + super.setup() + + titleLabel.text = configuration.title + addSubview(titleLabel) + updateFonts(with: traitCollection) + layer.cornerRadius = 3 + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts(with: traitCollection) + } + + override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + + let font = UIFont.wmf_font(.boldSubheadline, compatibleWithTraitCollection: traitCollection) + + let icon: UIImage? + switch configuration.icon { + case .sfSymbol(let symbolName): + let configuration = UIImage.SymbolConfiguration(font: font) + icon = UIImage(systemName: symbolName, withConfiguration: configuration) + case .custom(let iconName): + icon = UIImage(named: iconName) + } + + titleLabel.font = font + if let icon = icon { + iconImageView?.removeFromSuperview() + let imageView = UIImageView(image: icon) + addSubview(imageView) + iconImageView = imageView + } + + } +} + +extension IconTitleBadge: Themeable { + func apply(theme: Theme) { + self.theme = theme + iconImageView?.tintColor = theme.colors.secondaryText + titleLabel.textColor = theme.colors.secondaryText + backgroundColor = theme.colors.baseBackground + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ImageCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/ImageCollectionViewCell.swift new file mode 100644 index 0000000..578c49d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ImageCollectionViewCell.swift @@ -0,0 +1,85 @@ +import UIKit + +class ImageCollectionViewCell: CollectionViewCell { + let imageView: UIImageView = UIImageView() + let gradientView: WMFGradientView = WMFGradientView() + private let captionLabel: UILabel = UILabel() + + var captionIsRTL: Bool = false { + didSet { + captionLabel.textAlignment = captionIsRTL ? .right : .left + } + } + + var caption: String? { + get { + return captionLabel.text + } + set { + captionLabel.text = newValue + setNeedsLayout() + } + } + + override func setup() { + super.setup() + imageView.accessibilityIgnoresInvertColors = true + imageView.contentMode = .scaleAspectFill + addSubview(imageView) + gradientView.setStart(.clear, end: UIColor(white: 0, alpha: 0.8)) + addSubview(gradientView) + captionLabel.numberOfLines = 3 + addSubview(captionLabel) + } + + override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + captionLabel.font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection) + } + + override func reset() { + super.reset() + imageView.wmf_reset() + captionLabel.text = nil + } + + let ratio: CGFloat = 1.02 + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + var size = super.sizeThatFits(size, apply: apply) + if size.width != UIView.noIntrinsicMetric { + size.height = round(ratio * size.width) + } else if size.height != UIView.noIntrinsicMetric { + size.width = round(size.height / ratio) + } + if apply { + let boundsInsetByMargins = CGRect(origin: .zero, size: size).inset(by: layoutMargins) + imageView.frame = CGRect(origin: .zero, size: size) + if captionLabel.wmf_hasAnyNonWhitespaceText { + captionLabel.isHidden = false + gradientView.isHidden = false + var labelFrame = captionLabel.wmf_preferredFrame(at: boundsInsetByMargins.origin, maximumSize: boundsInsetByMargins.size, minimumSize: CGSize(width: boundsInsetByMargins.size.width, height: UIView.noIntrinsicMetric), alignedBy: semanticContentAttribute, apply: false) + let extraBottomPadding: CGFloat = 5.0 + labelFrame.origin = CGPoint(x: labelFrame.origin.x, y: size.height - labelFrame.height - layoutMargins.bottom - extraBottomPadding) + captionLabel.frame = labelFrame + let gradientOriginY = labelFrame.minY - layoutMargins.bottom + gradientView.frame = CGRect(x: 0, y: gradientOriginY, width: size.width, height: size.height - gradientOriginY + extraBottomPadding) + } else { + captionLabel.isHidden = true + gradientView.isHidden = true + } + } + return size + } + + +} + +extension ImageCollectionViewCell: Themeable { + func apply(theme: Theme) { + setBackgroundColors(theme.colors.midBackground, selected: theme.colors.baseBackground) + imageView.backgroundColor = .clear + imageView.alpha = theme.imageOpacity + captionLabel.textColor = .white + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ImageControllerCompletionManager.swift b/Apps/Wikipedia/Wikipedia/Code/ImageControllerCompletionManager.swift new file mode 100644 index 0000000..cef614c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ImageControllerCompletionManager.swift @@ -0,0 +1,121 @@ +import Foundation + +internal struct ImageControllerPermanentCacheCompletion { + let success: () -> Void + let failure: (Error) -> Void +} + +internal struct ImageControllerDataCompletion { + let success: (Data, URLResponse) -> Void + let failure: (Error) -> Void +} + +internal class ImageControllerCompletionManager { + var completions: [String: [String: T]] = [:] + var tasks: [String: [String:URLSessionTask]] = [:] + let queue = DispatchQueue(label: "ImageControllerCompletionManager-" + UUID().uuidString) + + func add(_ completion: T, priority: Float, forGroup group: String, identifier: String, token: String, isFirstCompletion:@escaping (Bool) -> Void) { + queue.async { + var completionsForKey = self.completions[identifier] ?? [:] + let isFirst = completionsForKey.isEmpty + if !isFirst { + self.tasks[group]?[identifier]?.priority = priority + } + completionsForKey[token] = completion + self.completions[identifier] = completionsForKey + isFirstCompletion(isFirst) + } + } + + func add(_ completion: T, priority: Float, forIdentifier identifier: String, token: String, isFirstCompletion:@escaping (Bool) -> Void) { + return add(completion, priority: priority, forGroup: "", identifier: identifier, token: token, isFirstCompletion: isFirstCompletion) + } + + func add(_ task: URLSessionTask, forGroup group: String, identifier: String) { + queue.async { + var groupTasks = self.tasks[group] ?? [:] + groupTasks[identifier] = task + self.tasks[group] = groupTasks + } + } + + func add(_ task: URLSessionTask, forIdentifier identifier: String) { + add(task, forGroup: "", identifier: identifier) + } + + + func cancel(group: String, identifier: String, token: String) { + queue.async { + guard var tasks = self.tasks[group], let task = tasks[identifier], var completions = self.completions[identifier] else { + return + } + completions.removeValue(forKey: token) + if completions.isEmpty { + self.completions.removeValue(forKey: identifier) + task.cancel() + tasks.removeValue(forKey: identifier) + self.tasks[group] = tasks + } else { + self.completions[identifier] = completions + } + } + } + + func cancel(group: String, identifier: String) { + queue.async { + guard var tasks = self.tasks[group], let task = tasks[identifier] else { + return + } + self.completions.removeValue(forKey: identifier) + task.cancel() + tasks.removeValue(forKey: identifier) + self.tasks[group] = tasks + } + } + + func cancel(_ identifier: String) { + cancel(group: "", identifier: identifier) + } + + func cancel(_ identifier: String, token: String) { + cancel(group: "", identifier: identifier, token: token) + } + + func cancel(group: String) { + queue.async { + guard let tasks = self.tasks[group] else { + return + } + for (identifier, task) in tasks { + self.completions.removeValue(forKey: identifier) + task.cancel() + } + } + } + + func cancelAll() { + queue.async { + for group in self.tasks.keys { + self.cancel(group: group) + } + } + } + + func complete(_ group: String, identifier: String, enumerator: @escaping (T) -> Void) { + queue.async { + guard let completionsForKey = self.completions[identifier] else { + return + } + for (_, completion) in completionsForKey { + enumerator(completion) + } + self.completions.removeValue(forKey: identifier) + self.tasks[group]?.removeValue(forKey: identifier) + } + } + + func complete(_ identifier: String, enumerator: @escaping (T) -> Void) { + complete("", identifier: identifier, enumerator: enumerator) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ImageDimmingExampleViewController.swift b/Apps/Wikipedia/Wikipedia/Code/ImageDimmingExampleViewController.swift new file mode 100644 index 0000000..cf14aa9 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ImageDimmingExampleViewController.swift @@ -0,0 +1,13 @@ +import UIKit +import WMF + +class ImageDimmingExampleViewController: UIViewController { + + @IBOutlet weak var exampleImage: UIImageView! + + var isImageDimmed: Bool = false { + didSet { + exampleImage.alpha = isImageDimmed ? Theme.dimmedImageOpacity : 1.0 + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/ImageDimmingExampleViewController.xib b/Apps/Wikipedia/Wikipedia/Code/ImageDimmingExampleViewController.xib new file mode 100644 index 0000000..41d2fd7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ImageDimmingExampleViewController.xib @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/ImageDownload.swift b/Apps/Wikipedia/Wikipedia/Code/ImageDownload.swift new file mode 100644 index 0000000..7246c40 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/ImageDownload.swift @@ -0,0 +1,67 @@ +import Foundation + +public enum ImageOrigin: Int { + case network = 0 + case disk = 1 + case memory = 2 + case unknown = 3 +} + +extension ImageOrigin { + public var debugColor: UIColor { + switch self { + case .network: + return UIColor.red + case .disk: + return UIColor.yellow + case .memory: + return UIColor.green + case .unknown: + return UIColor.black + } + } +} + +public protocol ImageOriginConvertible { + func asImageOrigin() -> ImageOrigin +} + + +public func asImageOrigin(_ c: T) -> ImageOrigin { return c.asImageOrigin() } + +@objc(WMFImage) public class Image: NSObject { + @objc open var staticImage: UIImage + @objc open var animatedImage: FLAnimatedImage? + @objc public init(staticImage: UIImage, animatedImage: FLAnimatedImage?) { + self.staticImage = staticImage + self.animatedImage = animatedImage + } +} + +@objc(WMFImageDownload) public class ImageDownload: NSObject { + // Exposing enums as string constants for ObjC compatibility + @objc public static let imageOriginNetwork = ImageOrigin.network.rawValue + @objc public static let imageOriginDisk = ImageOrigin.disk.rawValue + @objc public static let imageOriginMemory = ImageOrigin.memory.rawValue + @objc public static let imageOriginUnknown = ImageOrigin.unknown.rawValue + + @objc open var url: URL + @objc open var image: Image + open var origin: ImageOrigin + + @objc open var originRawValue: Int { + return origin.rawValue + } + + public init(url: URL, image: Image, origin: ImageOrigin) { + self.url = url + self.image = image + self.origin = origin + } + + public init(url: URL, image: Image, originRawValue: Int) { + self.url = url + self.image = image + self.origin = ImageOrigin(rawValue: originRawValue)! + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InfoBannerView.swift b/Apps/Wikipedia/Wikipedia/Code/InfoBannerView.swift new file mode 100644 index 0000000..9409900 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InfoBannerView.swift @@ -0,0 +1,114 @@ +import UIKit + +class InfoBannerView: SetupView { + + private let iconImageView = UIImageView() + private let titleLabel = UILabel() + private let subtitleLabel = UILabel() + + var isDynamicFont: Bool = true { + didSet { + updateFonts(with: traitCollection) + } + } + + func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + + let semanticContentAttribute: UISemanticContentAttribute = traitCollection.layoutDirection == .rightToLeft ? .forceRightToLeft : .forceLeftToRight + let isRTL = semanticContentAttribute == .forceRightToLeft + + let adjustedMargins = UIEdgeInsets(top: layoutMargins.top, left: layoutMargins.left + 13, bottom: layoutMargins.bottom, right: layoutMargins.right + 13) + + let iconImageSideLength = CGFloat(26) + let iconTextSpacing = CGFloat(10) + let titleSubtitleSpacing = UIStackView.spacingUseSystem + + let titleLabelOrigin = isRTL ? CGPoint(x: adjustedMargins.left, y: adjustedMargins.top) : CGPoint(x: adjustedMargins.left + iconImageSideLength + iconTextSpacing, y: adjustedMargins.top) + let titleLabelWidth = size.width - adjustedMargins.left - adjustedMargins.right - iconImageSideLength - iconTextSpacing + + let titleLabelFrame = titleLabel.wmf_preferredFrame(at: titleLabelOrigin, maximumWidth: titleLabelWidth, minimumWidth: titleLabelWidth, alignedBy: semanticContentAttribute, apply: apply) + + let subtitleLabelOrigin = CGPoint(x: titleLabelOrigin.x, y: titleLabelFrame.maxY + titleSubtitleSpacing) + let subtitleLabelWidth = titleLabelWidth + + let subtitleLabelFrame = subtitleLabel.wmf_preferredFrame(at: subtitleLabelOrigin, maximumWidth: subtitleLabelWidth, minimumWidth: subtitleLabelWidth, alignedBy: semanticContentAttribute, apply: apply) + + let finalHeight = adjustedMargins.top + titleLabelFrame.size.height + subtitleLabelFrame.height + adjustedMargins.bottom + + if apply { + iconImageView.frame = isRTL ? CGRect(x: adjustedMargins.left + titleLabelWidth + iconTextSpacing, y: (finalHeight / 2) - (iconImageSideLength / 2), width: iconImageSideLength, height: iconImageSideLength) : CGRect(x: adjustedMargins.left, y: (finalHeight / 2) - (iconImageSideLength / 2), width: iconImageSideLength, height: iconImageSideLength) + } + + return CGSize(width: size.width, height: finalHeight) + } + + override func sizeThatFits(_ size: CGSize) -> CGSize { + return sizeThatFits(size, apply: false) + } + + func configure(iconName: String, title: String, subtitle: String) { + iconImageView.image = UIImage.init(named: iconName) + titleLabel.text = title + subtitleLabel.text = subtitle + + accessibilityLabel = "\(title)\n\(subtitle)" + } + + // MARK: - Dynamic Type + // Only applies new fonts if the content size category changes + + open override func setNeedsLayout() { + maybeUpdateFonts(with: traitCollection) + super.setNeedsLayout() + } + + override open func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + setNeedsLayout() + } + + var contentSizeCategory: UIContentSizeCategory? + fileprivate func maybeUpdateFonts(with traitCollection: UITraitCollection) { + guard contentSizeCategory == nil || contentSizeCategory != traitCollection.wmf_preferredContentSizeCategory else { + return + } + contentSizeCategory = traitCollection.wmf_preferredContentSizeCategory + updateFonts(with: traitCollection) + } + + func updateFonts(with traitCollection: UITraitCollection) { + if !isDynamicFont { + titleLabel.font = UIFont.systemFont(ofSize: 13.0, weight: .medium) + subtitleLabel.font = UIFont.systemFont(ofSize: 12.0, weight: .regular) + } else { + titleLabel.font = UIFont.wmf_font(.mediumFootnote, compatibleWithTraitCollection: traitCollection) + subtitleLabel.font = UIFont.wmf_font(.caption1, compatibleWithTraitCollection: traitCollection) + } + } + + override func setup() { + autoresizesSubviews = false + titleLabel.numberOfLines = 0 + subtitleLabel.numberOfLines = 0 + addSubview(iconImageView) + addSubview(titleLabel) + addSubview(subtitleLabel) + + titleLabel.isAccessibilityElement = false + subtitleLabel.isAccessibilityElement = false + + isAccessibilityElement = true + + updateFonts(with: traitCollection) + } +} + +// MARK: Themable + +extension InfoBannerView: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.hintBackground + titleLabel.textColor = theme.colors.link + subtitleLabel.textColor = theme.colors.link + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertLinkViewController.swift b/Apps/Wikipedia/Wikipedia/Code/InsertLinkViewController.swift new file mode 100644 index 0000000..6ea41eb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertLinkViewController.swift @@ -0,0 +1,98 @@ +import UIKit + +protocol InsertLinkViewControllerDelegate: AnyObject { + func insertLinkViewController(_ insertLinkViewController: InsertLinkViewController, didTapCloseButton button: UIBarButtonItem) + func insertLinkViewController(_ insertLinkViewController: InsertLinkViewController, didInsertLinkFor page: String, withLabel label: String?) +} + +class InsertLinkViewController: UIViewController { + weak var delegate: InsertLinkViewControllerDelegate? + private var theme = Theme.standard + private let dataStore: MWKDataStore + private let link: Link + private let siteURL: URL? + + init(link: Link, siteURL: URL?, dataStore: MWKDataStore) { + self.link = link + self.siteURL = siteURL + self.dataStore = dataStore + super.init(nibName: nil, bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private lazy var closeButton: UIBarButtonItem = { + let closeButton = UIBarButtonItem.wmf_buttonType(.X, target: self, action: #selector(delegateCloseButtonTap(_:))) + closeButton.accessibilityLabel = CommonStrings.closeButtonAccessibilityLabel + return closeButton + }() + + private lazy var searchViewController: SearchViewController = { + let searchViewController = SearchViewController() + searchViewController.dataStore = dataStore + searchViewController.siteURL = siteURL + searchViewController.searchTerm = link.page + searchViewController.areRecentSearchesEnabled = true + searchViewController.shouldBecomeFirstResponder = true + searchViewController.dataStore = MWKDataStore.shared() + searchViewController.shouldShowCancelButton = false + searchViewController.delegate = self + searchViewController.delegatesSelection = true + searchViewController.showLanguageBar = false + searchViewController.updateRecentlySearchedVisibility(searchText: link.page) + searchViewController.doResultsShowArticlePreviews = false + return searchViewController + }() + + override func viewDidLoad() { + super.viewDidLoad() + title = CommonStrings.insertLinkTitle + navigationItem.leftBarButtonItem = closeButton + let navigationController = WMFThemeableNavigationController(rootViewController: searchViewController) + navigationController.isNavigationBarHidden = true + wmf_add(childController: navigationController, andConstrainToEdgesOfContainerView: view) + apply(theme: theme) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + searchViewController.search() + } + + @objc private func delegateCloseButtonTap(_ sender: UIBarButtonItem) { + delegate?.insertLinkViewController(self, didTapCloseButton: sender) + } + + override func accessibilityPerformEscape() -> Bool { + delegate?.insertLinkViewController(self, didTapCloseButton: closeButton) + return true + } +} + +extension InsertLinkViewController: ArticleCollectionViewControllerDelegate { + func articleCollectionViewController(_ articleCollectionViewController: ArticleCollectionViewController, didSelectArticleWith articleURL: URL, at indexPath: IndexPath) { + guard let title = articleURL.wmf_title else { + return + } + delegate?.insertLinkViewController(self, didInsertLinkFor: title, withLabel: nil) + } +} + +extension InsertLinkViewController: Themeable { + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.inputAccessoryBackground + view.layer.shadowColor = theme.colors.shadow.cgColor + closeButton.tintColor = theme.colors.primaryText + searchViewController.apply(theme: theme) + } +} + +extension InsertLinkViewController: EditingFlowViewController { + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaImageInfoView.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaImageInfoView.swift new file mode 100644 index 0000000..ca93d24 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaImageInfoView.swift @@ -0,0 +1,74 @@ +import UIKit + +final class InsertMediaImageInfoView: UIView { + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var descriptionLabel: UILabel! + @IBOutlet private weak var licenseLabel: UILabel! + @IBOutlet private weak var licenseView: LicenseView! + @IBOutlet private weak var moreInformationButton: UIButton! + + var moreInformationAction: ((URL) -> Void)? + private var keepBackgroundClear = false + + private var moreInformationURL: URL? { + didSet { + moreInformationButton.isHidden = moreInformationURL == nil + } + } + + func configure(with searchResult: InsertMediaSearchResult, showImageDescription: Bool = true, showLicenseName: Bool = true, showMoreInformationButton: Bool = true, keepBackgroundClear: Bool = false, theme: Theme) { + titleLabel.text = searchResult.displayTitle + moreInformationURL = searchResult.imageInfo?.filePageURL + if showImageDescription, let imageDescription = searchResult.imageInfo?.imageDescription { + descriptionLabel.numberOfLines = 5 + descriptionLabel.text = imageDescription + } else { + descriptionLabel.isHidden = true + } + + moreInformationButton.isHidden = !showMoreInformationButton + + licenseView.licenseCodes = searchResult.imageInfo?.license?.code?.split(separator: "-").compactMap { String($0) } ?? [] + licenseView.isHidden = licenseView.licenseCodes.isEmpty + if showLicenseName { + licenseLabel.text = searchResult.imageInfo?.license?.shortDescription + } else { + licenseLabel.isHidden = true + } + updateFonts() + setNeedsLayout() + self.keepBackgroundClear = keepBackgroundClear + apply(theme: theme) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + titleLabel.font = UIFont.wmf_font(.semiboldFootnote, compatibleWithTraitCollection: traitCollection) + descriptionLabel.font = UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + licenseLabel.font = UIFont.wmf_font(.semiboldCaption2, compatibleWithTraitCollection: traitCollection) + } + + @IBAction private func showMoreInformation(_ sender: Any) { + guard let url = moreInformationURL else { + assertionFailure("moreInformationURL should be set by now") + return + } + moreInformationAction?(url) + } +} + +extension InsertMediaImageInfoView: Themeable { + func apply(theme: Theme) { + backgroundColor = keepBackgroundClear ? .clear : theme.colors.paperBackground + titleLabel.textColor = theme.colors.primaryText + descriptionLabel.textColor = theme.colors.primaryText + licenseLabel.textColor = theme.colors.primaryText + moreInformationButton.tintColor = theme.colors.link + moreInformationButton.backgroundColor = backgroundColor + licenseView.tintColor = theme.colors.primaryText + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaImageInfoView.xib b/Apps/Wikipedia/Wikipedia/Code/InsertMediaImageInfoView.xib new file mode 100644 index 0000000..720d2d1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaImageInfoView.xib @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultCollectionViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultCollectionViewCell.swift new file mode 100644 index 0000000..7d2bef7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultCollectionViewCell.swift @@ -0,0 +1,103 @@ +import UIKit + +final class InsertMediaSearchResultCollectionViewCell: CollectionViewCell { + let imageView = UIImageView() + private let captionLabel = UILabel() + + private var imageURL: URL? + + private var spacing: CGFloat = 8 + + private let selectedImageView = UIImageView() + private var selectedImage = UIImage(named: "selected") + + override func setup() { + super.setup() + isAccessibilityElement = true + imageView.accessibilityIgnoresInvertColors = true + imageView.contentMode = .scaleAspectFit + contentView.addSubview(imageView) + selectedImageView.contentMode = .scaleAspectFit + contentView.addSubview(selectedImageView) + captionLabel.numberOfLines = 1 + contentView.addSubview(captionLabel) + captionLabel.isAccessibilityElement = true + } + + override func updateFonts(with traitCollection: UITraitCollection) { + super.updateFonts(with: traitCollection) + captionLabel.font = UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + } + + override func reset() { + super.reset() + imageView.wmf_reset() + captionLabel.text = nil + } + + func configure(imageURL: URL?, caption: String?) { + self.imageURL = imageURL + captionLabel.text = caption + accessibilityValue = caption + setNeedsLayout() + } + + override var isSelected: Bool { + didSet { + updateSelectedOrHighlighted() + selectedImageView.isHidden = !isSelected + } + } + + override func sizeThatFits(_ size: CGSize, apply: Bool) -> CGSize { + guard let imageURL = imageURL else { + return super.sizeThatFits(size, apply: apply) + } + + let imageViewDimension = size.width + let selectedImageViewDimension = min(35, imageViewDimension * 0.2) + + guard let scaledImageURL = WMFParseSizePrefixFromSourceURL(imageURL) < Int(imageViewDimension) ? URL(string: WMFChangeImageSourceURLSizePrefix(imageURL.absoluteString, Int(imageViewDimension))) : imageURL else { + return super.sizeThatFits(size, apply: apply) + } + + let size = super.sizeThatFits(size, apply: apply) + + var origin = CGPoint(x: 0, y: 0) + let minHeight = imageViewDimension + layoutMargins.top + layoutMargins.bottom + let height = max(origin.y, minHeight) + + if apply { + imageView.frame = CGRect(x: origin.x, y: origin.y, width: imageViewDimension, height: imageViewDimension) + imageView.wmf_setImage(with: scaledImageURL, detectFaces: false, onGPU: true, failure: {_ in }, success: { + self.imageView.backgroundColor = .clear + }) + selectedImageView.frame = CGRect(x: imageView.frame.maxX - selectedImageViewDimension, y: imageView.frame.maxY - selectedImageViewDimension, width: selectedImageViewDimension, height: selectedImageViewDimension) + selectedImageView.image = selectedImage + origin.y += imageView.frame.layoutHeight(with: spacing) + } + + selectedImageView.isHidden = !isSelected + + let semanticContentAttribute: UISemanticContentAttribute = traitCollection.layoutDirection == .rightToLeft ? .forceRightToLeft : .forceLeftToRight + + if captionLabel.wmf_hasAnyNonWhitespaceText { + let captionLabelFrame = captionLabel.wmf_preferredFrame(at: origin, maximumWidth: imageViewDimension, alignedBy: semanticContentAttribute, apply: apply) + origin.y += captionLabelFrame.height + } + + captionLabel.isHidden = !captionLabel.wmf_hasAnyNonWhitespaceText + + return CGSize(width: size.width, height: height) + } +} + +extension InsertMediaSearchResultCollectionViewCell: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + imageView.backgroundColor = .clear + selectedImage = theme.isDark ? UIImage(named: "selected-dark") : UIImage(named: "selected") + selectedImageView.tintColor = theme.colors.link + captionLabel.textColor = theme.colors.primaryText + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultPreviewingViewController.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultPreviewingViewController.swift new file mode 100644 index 0000000..5e3451b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultPreviewingViewController.swift @@ -0,0 +1,57 @@ +import UIKit + +final class InsertMediaSearchResultPreviewingViewController: UIViewController { + @IBOutlet private weak var imageView: UIImageView! + @IBOutlet private weak var imageInfoViewContainer: UIView! + @IBOutlet private weak var activityIndicator: UIActivityIndicatorView! + + private lazy var imageInfoView = InsertMediaImageInfoView.wmf_viewFromClassNib()! + + let searchResult: InsertMediaSearchResult + private let imageURL: URL + private var theme = Theme.standard + + var searchResultImageURL: URL? { + return searchResult.imageInfo?.filePageURL + } + + init(imageURL: URL, searchResult: InsertMediaSearchResult) { + self.imageURL = imageURL + self.searchResult = searchResult + super.init(nibName: "InsertMediaSearchResultPreviewingViewController", bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + imageView.accessibilityIgnoresInvertColors = true + imageView.wmf_setImage(with: imageURL, detectFaces: true, onGPU: true, failure: { _ in }) { + self.imageView.backgroundColor = self.view.backgroundColor + self.activityIndicator.stopAnimating() + } + imageInfoView.configure(with: searchResult, showLicenseName: false, showMoreInformationButton: false, theme: theme) + imageInfoView.apply(theme: theme) + imageInfoViewContainer.wmf_addSubviewWithConstraintsToEdges(imageInfoView) + apply(theme: theme) + } +} + +extension InsertMediaSearchResultPreviewingViewController: Themeable { + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.paperBackground + imageView.backgroundColor = view.backgroundColor + activityIndicator.color = theme.isDark ? .white : .gray + imageInfoView.apply(theme: theme) + } +} + +extension InsertMediaSearchResultPreviewingViewController: EditingFlowViewController { + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultPreviewingViewController.xib b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultPreviewingViewController.xib new file mode 100644 index 0000000..6391759 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultPreviewingViewController.xib @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultsCollectionViewController.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultsCollectionViewController.swift new file mode 100644 index 0000000..5a203e7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchResultsCollectionViewController.swift @@ -0,0 +1,290 @@ +import UIKit + +fileprivate class FlowLayout: UICollectionViewFlowLayout { + override init() { + super.init() + minimumInteritemSpacing = 12 + minimumLineSpacing = 38 + sectionInsetReference = .fromContentInset + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext { + let superContext = super.invalidationContext(forBoundsChange: newBounds) + guard let context = superContext as? UICollectionViewFlowLayoutInvalidationContext else { + return superContext + } + context.invalidateFlowLayoutDelegateMetrics = true + return context + } + + override func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) { + defer { + super.invalidateLayout(with: context) + } + guard let collectionView = collectionView else { + return + } + let countOfColumns: CGFloat = 3 + sectionInset = UIEdgeInsets(top: 12, left: minimumInteritemSpacing + collectionView.layoutMargins.left - collectionView.contentInset.left, bottom: 0, right: collectionView.layoutMargins.right - collectionView.contentInset.right + minimumInteritemSpacing) + let availableWidth = collectionView.bounds.width - minimumInteritemSpacing * (countOfColumns - 1) - collectionView.contentInset.left - collectionView.contentInset.right - sectionInset.left - sectionInset.right + let dimension = floor(availableWidth / countOfColumns) + itemSize = CGSize(width: dimension, height: dimension) + } +} + +protocol InsertMediaSearchResultsCollectionViewControllerDelegate: AnyObject { + func insertMediaSearchResultsCollectionViewControllerDidSelect(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, searchResult: InsertMediaSearchResult) +} + +protocol InsertMediaSearchResultsCollectionViewControllerScrollDelegate: AnyObject { + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewDidScroll scrollView: UIScrollView) + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewWillBeginDragging scrollView: UIScrollView) + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewWillEndDragging scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewDidEndDecelerating scrollView: UIScrollView) + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewDidEndScrollingAnimation scrollView: UIScrollView) + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewShouldScrollToTop scrollView: UIScrollView) -> Bool + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewDidScrollToTop scrollView: UIScrollView) +} + +final class InsertMediaSearchResult { + let fileTitle: String + let displayTitle: String + let thumbnailURL: URL + var imageInfo: MWKImageInfo? + + init(fileTitle: String, displayTitle: String, thumbnailURL: URL) { + self.fileTitle = fileTitle + self.displayTitle = displayTitle + self.thumbnailURL = thumbnailURL + } + + func imageURL(for width: CGFloat) -> URL? { + guard width > 0 else { + assertionFailure("width must be greater than 0") + return nil + } + return URL(string: WMFChangeImageSourceURLSizePrefix(thumbnailURL.absoluteString, Int(width))) ?? imageInfo?.canonicalFileURL + } +} + +class InsertMediaSearchResultsCollectionViewController: UICollectionViewController { + private var theme = Theme.standard + private var flowLayout: FlowLayout { + return collectionView.collectionViewLayout as! FlowLayout + } + + weak var delegate: InsertMediaSearchResultsCollectionViewControllerDelegate? + weak var scrollDelegate: InsertMediaSearchResultsCollectionViewControllerScrollDelegate? + + var searchResults = [InsertMediaSearchResult]() { + didSet { + assert(Thread.isMainThread) + reload() + } + } + + var selectedImage: UIImage? { + guard + let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first, + let cell = collectionView.cellForItem(at: selectedIndexPath) as? InsertMediaSearchResultCollectionViewCell + else { + return nil + } + return cell.imageView.image + } + + init() { + super.init(collectionViewLayout: FlowLayout()) + collectionView.contentInsetAdjustmentBehavior = .never + title = CommonStrings.searchTitle + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + collectionView.register(InsertMediaSearchResultCollectionViewCell.self, forCellWithReuseIdentifier: InsertMediaSearchResultCollectionViewCell.identifier) + additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: flowLayout.minimumLineSpacing, right: 0) + apply(theme: theme) + } + + func reload() { + collectionView.reloadData() + updateEmptyState() + } + + private func configure(_ cell: InsertMediaSearchResultCollectionViewCell, at indexPath: IndexPath) { + let result = searchResults[indexPath.item] + cell.configure(imageURL: result.thumbnailURL, caption: result.displayTitle) + cell.apply(theme: theme) + } + + func setImageInfo(_ imageInfo: MWKImageInfo?, for searchResult: InsertMediaSearchResult, at index: Int) { + assert(Thread.isMainThread) + searchResult.imageInfo = imageInfo + } + + // MARK: Themeable + + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.paperBackground + collectionView.backgroundColor = theme.colors.paperBackground + collectionView.reloadData() + } + + // MARK: - Empty State + + var emptyViewType: WMFEmptyViewType = .noSearchResults + + final var isEmpty = true + final var showingEmptyViewType: WMFEmptyViewType? + final func updateEmptyState() { + let sectionCount = numberOfSections(in: collectionView) + + var isCurrentlyEmpty = true + for sectionIndex in 0.. 0 { + isCurrentlyEmpty = false + break + } + } + + guard isCurrentlyEmpty != isEmpty || showingEmptyViewType != emptyViewType else { + return + } + + isEmpty = isCurrentlyEmpty + + isEmptyDidChange() + } + + private var emptyViewFrame: CGRect { + let insets = collectionView?.contentInset ?? UIEdgeInsets.zero + let frame = view.bounds.inset(by: insets) + return frame + } + + open func isEmptyDidChange() { + if isEmpty { + wmf_showEmptyView(of: emptyViewType, theme: theme, frame: emptyViewFrame) + showingEmptyViewType = emptyViewType + } else { + wmf_hideEmptyView() + showingEmptyViewType = nil + } + } + + func scrollViewInsetsDidChange() { + wmf_setEmptyViewFrame(emptyViewFrame) + } + + // MARK: Scroll view delegate + + override func scrollViewDidScroll(_ scrollView: UIScrollView) { + scrollDelegate?.insertMediaSearchResultsCollectionViewController(self, scrollViewDidScroll: scrollView) + } + + override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + scrollDelegate?.insertMediaSearchResultsCollectionViewController(self, scrollViewWillBeginDragging: scrollView) + } + + override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + scrollDelegate?.insertMediaSearchResultsCollectionViewController(self, scrollViewWillEndDragging: scrollView, withVelocity: velocity, targetContentOffset: targetContentOffset) + } + + override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + scrollDelegate?.insertMediaSearchResultsCollectionViewController(self, scrollViewDidEndDecelerating: scrollView) + } + + override func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { + scrollDelegate?.insertMediaSearchResultsCollectionViewController(self, scrollViewDidEndScrollingAnimation: scrollView) + } + + override func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { + return scrollDelegate?.insertMediaSearchResultsCollectionViewController(self, scrollViewShouldScrollToTop: scrollView) ?? true + } + + override func scrollViewDidScrollToTop(_ scrollView: UIScrollView) { + scrollDelegate?.insertMediaSearchResultsCollectionViewController(self, scrollViewDidScrollToTop: scrollView) + } +} + +extension InsertMediaSearchResultsCollectionViewController { + override func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } + + override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return searchResults.count + } + + override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: InsertMediaSearchResultCollectionViewCell.identifier, for: indexPath) + guard let searchResultCell = cell as? InsertMediaSearchResultCollectionViewCell else { + return cell + } + configure(searchResultCell, at: indexPath) + return searchResultCell + } +} + +extension InsertMediaSearchResultsCollectionViewController { + override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let searchResult = searchResults[indexPath.item] + delegate?.insertMediaSearchResultsCollectionViewControllerDidSelect(self, searchResult: searchResult) + } +} + +// MARK: - Context Menu (and preview) +extension InsertMediaSearchResultsCollectionViewController { + func viewController(for indexPath: IndexPath) -> UIViewController? { + guard let searchResult = searchResults[safeIndex: indexPath.item], + let imageURL = searchResult.imageURL(for: view.bounds.width) + else { + return nil + } + let previewingViewController = InsertMediaSearchResultPreviewingViewController(imageURL: imageURL, searchResult: searchResult) + previewingViewController.apply(theme: theme) + return previewingViewController + } + + override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { + guard let vc = viewController(for: indexPath) as? InsertMediaSearchResultPreviewingViewController else { + return nil + } + let previewProvider: () -> UIViewController? = { + return vc + } + return UIContextMenuConfiguration(identifier: nil, previewProvider: previewProvider) { (suggestedActions) -> UIMenu? in + let selectImageAction = UIAction(title: WMFLocalizedString("insert-media-image-preview-select-image-action-title", value: "Select image", comment: "Title for preview action that results in image selection"), handler: { [weak self] (_) in + guard let self = self else { + return + } + self.collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredVertically) + self.delegate?.insertMediaSearchResultsCollectionViewControllerDidSelect(self, searchResult: vc.searchResult) + }) + let moreInformationAction = UIAction(title: WMFLocalizedString("insert-media-image-preview-more-information-action-title", value: "More information", comment: "Title for preview action that results in presenting more information"), handler: { [weak self] (_) in + guard let url = vc.searchResultImageURL else { + return + } + self?.navigate(to: url, useSafari: true) + }) + let cancelAction = UIAction(title: CommonStrings.cancelActionTitle, handler: {(_) in }) + return UIMenu(title: "", image: nil, identifier: nil, options: [], children: [selectImageAction, moreInformationAction, cancelAction]) + } + } + + override func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { + // We do not let this preview commit. + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchViewController.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchViewController.swift new file mode 100644 index 0000000..6e78d89 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSearchViewController.swift @@ -0,0 +1,144 @@ +protocol InsertMediaSearchViewControllerDelegate: InsertMediaViewController { + func insertMediaSearchViewController(_ insertMediaSearchViewController: InsertMediaSearchViewController, didFailWithError error: Error) + func insertMediaSearchViewController(_ insertMediaSearchViewController: InsertMediaSearchViewController, didFind searchResults: [InsertMediaSearchResult]) + func insertMediaSearchViewController(_ insertMediaSearchViewController: InsertMediaSearchViewController, didFind imageInfo: MWKImageInfo, for searchResult: InsertMediaSearchResult, at index: Int) +} + +final class InsertMediaSearchViewController: UIViewController { + let searchBar = UISearchBar() + + private let articleTitle: String? + private let siteURL: URL? + + var progressController: FakeProgressController! + + private let searchFetcher = WMFSearchFetcher() + // SINGLETONTODO + private let imageInfoFetcher = MWKImageInfoFetcher(dataStore: MWKDataStore.shared()) + + weak var delegate: InsertMediaSearchViewControllerDelegate? + weak var searchBarDelegate: UISearchBarDelegate? + + private var theme = Theme.standard + + init(articleTitle: String?, siteURL: URL?) { + self.articleTitle = articleTitle + self.siteURL = siteURL ?? Configuration.current.commonsAPIURLComponents(with: nil).url + super.init(nibName: nil, bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + if let articleTitle = articleTitle { + search(for: articleTitle, isFirstSearch: true) + } + searchBar.placeholder = articleTitle ?? CommonStrings.searchTitle + searchBar.returnKeyType = .done + searchBar.searchBarStyle = .minimal + searchBar.enablesReturnKeyAutomatically = false + searchBar.delegate = searchBarDelegate + view.wmf_addSubview(searchBar, withConstraintsToEdgesWithInsets: UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)) + apply(theme: theme) + } + + func search(for searchTerm: String, isFirstSearch: Bool = false) { + searchFetcher.cancelAllFetches() + imageInfoFetcher.cancelAllFetches() + guard searchTerm.wmf_hasNonWhitespaceText else { + searchForArticleTitle() + return + } + if isFirstSearch { + progressController.delay = 0 + } else { + progressController.delay = 1.0 + } + progressController.start() + let failure = { (error: Error) in + DispatchQueue.main.async { + self.progressController.stop() + self.delegate?.insertMediaSearchViewController(self, didFailWithError: error) + } + } + let searchResults: (WMFSearchResults) -> [InsertMediaSearchResult] = { (results: WMFSearchResults) in + assert(!Thread.isMainThread) + guard let results = results.results else { + return [] + } + return results.compactMap { (result: MWKSearchResult) in + guard + let fileTitle = result.displayTitle, + let thumbnailURL = result.thumbnailURL + else { + return nil + } + let startIndex = fileTitle.index(fileTitle.startIndex, offsetBy: 5) + let endIndex = fileTitle.index(fileTitle.endIndex, offsetBy: -5) + let displayTitle = String(fileTitle[startIndex...endIndex]) + return InsertMediaSearchResult(fileTitle: fileTitle, displayTitle: displayTitle, thumbnailURL: thumbnailURL) + } + } + let success = { (results: WMFSearchResults) in + assert(!Thread.isMainThread) + let searchResults = searchResults(results) + DispatchQueue.main.async { + self.progressController.finish() + self.delegate?.insertMediaSearchViewController(self, didFind: searchResults) + } + for (index, searchResult) in searchResults.enumerated() { + guard searchResult.imageInfo == nil, + let siteURL = self.siteURL else { + continue + } + self.imageInfoFetcher.fetchGalleryInfo(forImage: searchResult.fileTitle, fromSiteURL: siteURL, failure: { error in + }, success: { result in + guard let imageInfo = result as? MWKImageInfo else { + return + } + DispatchQueue.main.async { + self.delegate?.insertMediaSearchViewController(self, didFind: imageInfo, for: searchResult, at: index) + } + }) + } + } + searchFetcher.fetchFiles(forSearchTerm: searchTerm, resultLimit: WMFMaxSearchResultLimit, fullTextSearch: false, appendToPreviousResults: nil, failure: failure) { results in + if let resultsArray = results.results { + if resultsArray.isEmpty { + self.searchFetcher.fetchFiles(forSearchTerm: searchTerm, resultLimit: WMFMaxSearchResultLimit, fullTextSearch: true, appendToPreviousResults: results, failure: failure, success: success) + } else if resultsArray.count < 12 { + let searchResults = searchResults(results) + DispatchQueue.main.async { + self.delegate?.insertMediaSearchViewController(self, didFind: searchResults) + } + self.searchFetcher.fetchFiles(forSearchTerm: searchTerm, resultLimit: WMFMaxSearchResultLimit, fullTextSearch: true, appendToPreviousResults: results, failure: failure, success: success) + } else { + success(results) + } + } else { + self.searchFetcher.fetchFiles(forSearchTerm: searchTerm, resultLimit: WMFMaxSearchResultLimit, fullTextSearch: true, appendToPreviousResults: results, failure: failure, success: success) + } + } + } + + func searchForArticleTitle() { + if let articleTitle = articleTitle { + search(for: articleTitle) + } + } +} + +extension InsertMediaSearchViewController: Themeable { + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.paperBackground + searchBar.apply(theme: theme) + searchBar.tintColor = theme.colors.link + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSelectedImageView.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSelectedImageView.swift new file mode 100644 index 0000000..03f3a61 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSelectedImageView.swift @@ -0,0 +1,65 @@ +import UIKit +import AVFoundation + +final class InsertMediaSelectedImageView: SetupView { + private let imageView = UIImageView() + private let imageInfoView = InsertMediaImageInfoView.wmf_viewFromClassNib()! + private let imageInfoContainerView = UIView() + private var imageInfoContainerViewBottomConstraint: NSLayoutConstraint? + + public var moreInformationAction: ((URL) -> Void)? + + var image: UIImage? { + return imageView.image + } + + var searchResult: InsertMediaSearchResult? + + override func setup() { + super.setup() + imageView.accessibilityIgnoresInvertColors = true + imageView.contentMode = .scaleAspectFit + imageView.translatesAutoresizingMaskIntoConstraints = false + wmf_addSubviewWithConstraintsToEdges(imageView) + + imageInfoContainerView.translatesAutoresizingMaskIntoConstraints = false + addSubview(imageInfoContainerView) + imageInfoContainerView.alpha = 0.8 + let leadingConstraint = imageInfoContainerView.leadingAnchor.constraint(equalTo: leadingAnchor) + let trailingConstraint = imageInfoContainerView.trailingAnchor.constraint(equalTo: trailingAnchor) + let bottomConstraint = imageInfoContainerView.bottomAnchor.constraint(equalTo: bottomAnchor) + let heightConstraint = imageInfoContainerView.heightAnchor.constraint(lessThanOrEqualTo: imageView.heightAnchor, multiplier: 0.5) + imageInfoContainerViewBottomConstraint = bottomConstraint + NSLayoutConstraint.activate([ + leadingConstraint, + trailingConstraint, + bottomConstraint, + heightConstraint + ]) + + imageInfoView.translatesAutoresizingMaskIntoConstraints = false + imageInfoContainerView.backgroundColor = UIColor.white + imageInfoContainerView.wmf_addSubviewWithConstraintsToEdges(imageInfoView) + } + + public func configure(with imageURL: URL, searchResult: InsertMediaSearchResult, theme: Theme, completion: @escaping (Error?) -> Void) { + imageView.image = nil + imageView.wmf_setImage(with: imageURL, detectFaces: false, onGPU: true, failure: { error in + completion(error) + }) { + self.searchResult = searchResult + self.imageView.backgroundColor = .clear + self.imageInfoView.moreInformationAction = self.moreInformationAction + self.imageInfoView.configure(with: searchResult, showImageDescription: false, showLicenseName: true, showMoreInformationButton: true, theme: theme) + completion(nil) + } + } +} + +extension InsertMediaSelectedImageView: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.baseBackground + imageInfoContainerView.backgroundColor = backgroundColor + imageView.backgroundColor = .clear + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSelectedImageViewController.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSelectedImageViewController.swift new file mode 100644 index 0000000..fcfe172 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSelectedImageViewController.swift @@ -0,0 +1,95 @@ +protocol InsertMediaSelectedImageViewControllerDelegate: AnyObject { + func insertMediaSelectedImageViewController(_ insertMediaSelectedImageViewController: InsertMediaSelectedImageViewController, didSetSelectedImage selectedImage: UIImage?, from searchResult: InsertMediaSearchResult) + func insertMediaSelectedImageViewController(_ insertMediaSelectedImageViewController: InsertMediaSelectedImageViewController, willSetSelectedImageFrom searchResult: InsertMediaSearchResult) +} + +final class InsertMediaSelectedImageViewController: UIViewController { + private let selectedView = InsertMediaSelectedImageView() + private let activityIndicator = UIActivityIndicatorView(style: .large) + private var theme = Theme.standard + weak var delegate: InsertMediaSelectedImageViewControllerDelegate? + + var image: UIImage? { + return selectedView.image + } + + var searchResult: InsertMediaSearchResult? { + return selectedView.searchResult + } + + override func viewDidLoad() { + super.viewDidLoad() + apply(theme: theme) + view.addCenteredSubview(activityIndicator) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if image == nil { + wmf_showEmptyView(of: .noSelectedImageToInsert, theme: theme, frame: view.bounds) + } else { + wmf_hideEmptyView() + } + } + + private func startActivityIndicator() { + wmf_hideEmptyView() + cancelPreviousActivityIndicatorSelectors() + selectedView.isHidden = true + perform(#selector(_startActivityIndicator), with: nil, afterDelay: 0.3) + } + + @objc private func _startActivityIndicator() { + activityIndicator.startAnimating() + } + + @objc private func stopActivityIndicator() { + cancelPreviousActivityIndicatorSelectors() + selectedView.isHidden = false + activityIndicator.stopAnimating() + } + + @objc private func cancelPreviousActivityIndicatorSelectors() { + NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(_startActivityIndicator), object: nil) + NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(stopActivityIndicator), object: nil) + } +} + +extension InsertMediaSelectedImageViewController: InsertMediaSearchResultsCollectionViewControllerDelegate { + func insertMediaSearchResultsCollectionViewControllerDidSelect(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, searchResult: InsertMediaSearchResult) { + delegate?.insertMediaSelectedImageViewController(self, willSetSelectedImageFrom: searchResult) + startActivityIndicator() + guard let imageURL = searchResult.imageURL(for: view.bounds.width) else { + stopActivityIndicator() + return + } + if selectedView.moreInformationAction == nil { + selectedView.moreInformationAction = { [weak self] url in + self?.navigate(to: url, useSafari: true) + } + } + selectedView.configure(with: imageURL, searchResult: searchResult, theme: theme) { error in + guard error == nil else { + self.stopActivityIndicator() + return + } + self.stopActivityIndicator() + if self.selectedView.superview == nil { + self.view.wmf_addSubviewWithConstraintsToEdges(self.selectedView) + } + self.delegate?.insertMediaSelectedImageViewController(self, didSetSelectedImage: self.image, from: searchResult) + } + } +} + +extension InsertMediaSelectedImageViewController: Themeable { + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + wmf_applyTheme(toEmptyView: theme) + view.backgroundColor = theme.colors.baseBackground + activityIndicator.color = theme.isDark ? .white : .gray + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsButtonView.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsButtonView.swift new file mode 100644 index 0000000..ca09a32 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsButtonView.swift @@ -0,0 +1,37 @@ +import UIKit + +final class InsertMediaSettingsButtonView: UIView { + @IBOutlet private weak var separatorView: UIView! + @IBOutlet private weak var button: UIButton! + + var buttonTitle: String? { + didSet { + button.setTitle(buttonTitle, for: .normal) + updateFonts() + } + } + + var buttonAction: ((UIButton) -> Void)? + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + button.titleLabel?.font = UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + } + + @IBAction private func delegateButtonAction(_ sender: UIButton) { + buttonAction?(sender) + } +} + +extension InsertMediaSettingsButtonView: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + button.setTitleColor(theme.colors.secondaryText, for: .normal) + button.tintColor = theme.colors.secondaryText + separatorView.backgroundColor = theme.colors.border + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsButtonView.xib b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsButtonView.xib new file mode 100644 index 0000000..a1d54de --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsButtonView.xib @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsImageView.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsImageView.swift new file mode 100644 index 0000000..d0892e3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsImageView.swift @@ -0,0 +1,67 @@ +import UIKit + +final class InsertMediaSettingsImageView: UIView { + @IBOutlet private weak var imageView: UIImageView! + @IBOutlet private weak var headingLabel: UILabel! + @IBOutlet private weak var titleButton: AutoLayoutSafeMultiLineButton! + @IBOutlet private weak var separatorView: UIView! + + var image: UIImage? { + didSet { + imageView.image = image + } + } + + var heading: String? { + didSet { + headingLabel.text = heading + } + } + + var title: String? { + didSet { + titleButton.setTitle(title, for: .normal) + } + } + + var titleURL: URL? + var titleAction: ((URL) -> Void)? + + override func awakeFromNib() { + super.awakeFromNib() + imageView.accessibilityIgnoresInvertColors = true + updateFonts() + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + headingLabel.font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection) + titleButton.titleLabel?.font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + } + + override func layoutSubviews() { + super.layoutSubviews() + headingLabel.preferredMaxLayoutWidth = headingLabel.bounds.width + } + + @IBAction private func performTitleAction(_ sender: UIButton) { + guard let url = titleURL else { + assertionFailure("titleURL should be set by now") + return + } + titleAction?(url) + } +} + +extension InsertMediaSettingsImageView: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + headingLabel.textColor = theme.colors.secondaryText + titleButton.setTitleColor(theme.colors.link, for: .normal) + separatorView.backgroundColor = theme.colors.border + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsImageView.xib b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsImageView.xib new file mode 100644 index 0000000..7120eb5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsImageView.xib @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsTextTableViewCell.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsTextTableViewCell.swift new file mode 100644 index 0000000..eb58850 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsTextTableViewCell.swift @@ -0,0 +1,59 @@ +import UIKit + +class InsertMediaSettingsTextTableViewCell: UITableViewCell { + @IBOutlet private weak var headerLabel: UILabel! + @IBOutlet private weak var footerLabel: UILabel! + @IBOutlet private weak var textView: ThemeableTextView! + + var headerText: String? { + didSet { + headerLabel.text = headerText + } + } + + var footerText: String? { + didSet { + footerLabel.text = footerText + } + } + + func textViewConfigured(with delegate: UITextViewDelegate, placeholder: String?, placeholderDelegate: ThemeableTextViewPlaceholderDelegate, clearDelegate: ThemeableTextViewClearDelegate, tag: Int) -> UITextView { + textView._delegate = delegate + textView.placeholderDelegate = placeholderDelegate + textView.clearDelegate = clearDelegate + textView.showsClearButton = true + textView.placeholder = placeholder + textView.textContainer.lineFragmentPadding = 0 + textView.tag = tag + accessibilityElements = [headerLabel as Any, textView as Any, textView.clearButton as Any, footerLabel as Any] + updateFonts() + return textView + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + headerLabel.font = UIFont.wmf_font(.subheadline, compatibleWithTraitCollection: traitCollection) + footerLabel.font = UIFont.wmf_font(.footnote, compatibleWithTraitCollection: traitCollection) + textView.font = UIFont.wmf_font(.body, compatibleWithTraitCollection: traitCollection) + } + + override func prepareForReuse() { + super.prepareForReuse() + headerLabel.text = nil + footerLabel.text = nil + textView.reset() + } +} + +extension InsertMediaSettingsTextTableViewCell: Themeable { + func apply(theme: Theme) { + backgroundColor = theme.colors.paperBackground + headerLabel.textColor = theme.colors.secondaryText + footerLabel.textColor = theme.colors.secondaryText + textView.apply(theme: theme) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsTextTableViewCell.xib b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsTextTableViewCell.xib new file mode 100644 index 0000000..5aa75d1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsTextTableViewCell.xib @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsViewController.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsViewController.swift new file mode 100644 index 0000000..a193106 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaSettingsViewController.swift @@ -0,0 +1,320 @@ +import UIKit + +typealias InsertMediaSettings = InsertMediaSettingsViewController.Settings + +final class InsertMediaSettingsViewController: ViewController { + private let tableView = UITableView(frame: .zero, style: .grouped) + private let image: UIImage + let searchResult: InsertMediaSearchResult + + private var textViewHeightDelta: (value: CGFloat, row: Int)? + private var textViewsGroupedByType = [TextViewType: UITextView]() + + struct Settings { + let caption: String? + let alternativeText: String? + let advanced: Advanced + + struct Advanced { + let wrapTextAroundImage: Bool + let imagePosition: ImagePosition + let imageType: ImageType + let imageSize: ImageSize + + enum ImagePosition: String { + case right + case left + case center + case none + + var displayTitle: String { + switch self { + case .right: + return WMFLocalizedString("insert-media-image-position-setting-right", value: "Right", comment: "Title for image position setting that positions image on the right") + case .left: + return WMFLocalizedString("insert-media-image-position-setting-left", value: "Left", comment: "Title for image position setting that positions image on the left") + case .center: + return WMFLocalizedString("insert-media-image-position-setting-center", value: "Center", comment: "Title for image position setting that positions image in the center") + case .none: + return WMFLocalizedString("insert-media-image-position-setting-none", value: "None", comment: "Title for image position setting that doesn't set image's position") + } + } + + static var displayTitle: String { + return WMFLocalizedString("insert-media-image-position-settings-title", value: "Image position", comment: "Display ritle for image position setting") + } + } + + enum ImageType: String { + case thumbnail = "thumb" + case frameless + case frame + case basic + + var displayTitle: String { + switch self { + case .thumbnail: + return WMFLocalizedString("insert-media-image-type-setting-thumbnail", value: "Thumbnail", comment: "Title for image type setting that formats image as thumbnail") + case .frameless: + return WMFLocalizedString("insert-media-image-type-setting-frameless", value: "Frameless", comment: "Title for image type setting that formats image as frameless") + case .frame: + return WMFLocalizedString("insert-media-image-type-setting-frame", value: "Frame", comment: "Title for image type setting that formats image as framed") + case .basic: + return WMFLocalizedString("insert-media-image-type-setting-basic", value: "Basic", comment: "Title for image type setting that formats image as basic") + } + } + + static var displayTitle: String { + return WMFLocalizedString("insert-media-image-type-settings-title", value: "Image type", comment: "Display ritle for image type setting") + } + } + + enum ImageSize { + case `default` + case custom(width: Int, height: Int) + + var displayTitle: String { + switch self { + case .default: + return WMFLocalizedString("insert-media-image-size-setting-default", value: "Default", comment: "Title for image size setting that sizes image using default size") + case .custom: + return WMFLocalizedString("insert-media-image-size-setting-custom", value: "Custom", comment: "Title for image size setting that sizes image using custom size") + } + } + + static var displayTitle: String { + return WMFLocalizedString("insert-media-image-size-settings-title", value: "Image size", comment: "Display ritle for image size setting") + } + + var rawValue: String { + switch self { + case .default: + return "\(ImageSize.defaultWidth)x\(ImageSize.defaultHeight)px" + case .custom(let width, let height): + return "\(width)x\(height)px" + } + } + + static var unitName = WMFLocalizedString("insert-media-image-size-settings-px-unit-name", value: "px", comment: "Image size unit name, abbreviation for 'pixels'") + + static var defaultWidth = 220 + static var defaultHeight = 124 + } + + init(wrapTextAroundImage: Bool = false, imagePosition: ImagePosition = .right, imageType: ImageType = .thumbnail, imageSize: ImageSize = .default) { + self.wrapTextAroundImage = wrapTextAroundImage + self.imagePosition = imagePosition + self.imageType = imageType + self.imageSize = imageSize + } + } + + init(caption: String?, alternativeText: String?, advanced: Advanced = Advanced()) { + self.caption = caption + self.alternativeText = alternativeText + self.advanced = advanced + } + } + + var settings: Settings? { + let captionTextView = textViewsGroupedByType[.caption] + let alternativeTextTextView = textViewsGroupedByType[.alternativeText] + let caption = captionTextView?.text.wmf_hasNonWhitespaceText ?? false ? captionTextView?.text : nil + let alternativeText = alternativeTextTextView?.text.wmf_hasNonWhitespaceText ?? false ? alternativeTextTextView?.text : nil + return Settings(caption: caption, alternativeText: alternativeText, advanced: insertMediaAdvancedSettingsViewController.advancedSettings) + } + + private lazy var imageView: InsertMediaSettingsImageView = { + let imageView = InsertMediaSettingsImageView.wmf_viewFromClassNib()! + imageView.image = image + imageView.heading = WMFLocalizedString("insert-media-uploaded-image-title", value: "Uploaded image", comment: "Title that appears next to an image in media settings") + imageView.title = searchResult.displayTitle + imageView.titleURL = searchResult.imageInfo?.filePageURL + imageView.titleAction = { [weak self] url in + self?.navigate(to: url, useSafari: true) + } + imageView.autoresizingMask = [] + return imageView + }() + + private lazy var insertMediaAdvancedSettingsViewController = InsertMediaAdvancedSettingsViewController() + + private lazy var buttonView: InsertMediaSettingsButtonView = { + let buttonView = InsertMediaSettingsButtonView.wmf_viewFromClassNib()! + let isRTL = UIApplication.shared.wmf_isRTL + let buttonTitleWithoutChevron = InsertMediaAdvancedSettingsViewController.title + let buttonTitleWithChevron = isRTL ? "< \(buttonTitleWithoutChevron)" : "\(buttonTitleWithoutChevron) >" + buttonView.buttonTitle = buttonTitleWithChevron + buttonView.buttonAction = { [weak self] _ in + guard let self = self else { + return + } + self.insertMediaAdvancedSettingsViewController.apply(theme: self.theme) + self.navigationController?.pushViewController(self.insertMediaAdvancedSettingsViewController, animated: true) + } + buttonView.autoresizingMask = [] + return buttonView + }() + + private struct TextViewModel { + let type: TextViewType + let headerText: String + let placeholder: String + let footerText: String + + init(type: TextViewType) { + self.type = type + switch type { + case .caption: + headerText = WMFLocalizedString("insert-media-caption-title", value: "Caption", comment: "Title for setting that allows users to add image captions") + placeholder = WMFLocalizedString("insert-media-caption-caption-placeholder", value: "How does this image relate to the article?", comment: "Placeholder text for setting that allows users to add image captions") + footerText = WMFLocalizedString("insert-media-caption-description", value: "Label that shows next to the item for all readers", comment: "Description for setting that allows users to add image captions") + case .alternativeText: + headerText = WMFLocalizedString("insert-media-alternative-text-title", value: "Alternative text", comment: "Title for setting that allows users to add image alternative text") + placeholder = WMFLocalizedString("insert-media-alternative-text-placeholder", value: "Describe this image", comment: "Placeholder text for setting that allows users to add image alternative text") + footerText = WMFLocalizedString("insert-media-alternative-text-description", value: "Text description for readers who cannot see the image", comment: "Description for setting that allows users to add image alternative text") + } + } + } + + private enum TextViewType: Int, Hashable { + case caption + case alternativeText + } + + private lazy var viewModels: [TextViewModel] = { + let captionViewModel = TextViewModel(type: .caption) + let alternativeTextViewModel = TextViewModel(type: .alternativeText) + return [captionViewModel, alternativeTextViewModel] + }() + + init(image: UIImage, searchResult: InsertMediaSearchResult) { + self.image = image + self.searchResult = searchResult + super.init() + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + scrollView = tableView + super.viewDidLoad() + navigationBar.isBarHidingEnabled = false + tableView.dataSource = self + view.wmf_addSubviewWithConstraintsToEdges(tableView) + tableView.register(InsertMediaSettingsTextTableViewCell.wmf_classNib(), forCellReuseIdentifier: InsertMediaSettingsTextTableViewCell.identifier) + tableView.separatorStyle = .none + tableView.tableHeaderView = imageView + tableView.tableFooterView = buttonView + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + guard let headerView = tableView.tableHeaderView else { + return + } + let height = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height + guard headerView.frame.size.height != height else { + return + } + headerView.frame.size.height = height + tableView.tableHeaderView = headerView + } + + override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) { + super.willTransition(to: newCollection, with: coordinator) + guard textViewHeightDelta != nil else { + return + } + UIView.performWithoutAnimation { + self.textViewHeightDelta = nil + self.tableView.beginUpdates() + self.tableView.endUpdates() + } + } + + // MARK: - Themeable + + override func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + view.backgroundColor = theme.colors.paperBackground + tableView.backgroundColor = view.backgroundColor + imageView.apply(theme: theme) + buttonView.apply(theme: theme) + tableView.reloadData() + } +} + +// MARK: - Table view data source + +extension InsertMediaSettingsViewController: UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewModels.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: InsertMediaSettingsTextTableViewCell.identifier, for: indexPath) as? InsertMediaSettingsTextTableViewCell else { + return UITableViewCell() + } + let viewModel = viewModels[indexPath.row] + cell.headerText = viewModel.headerText + textViewsGroupedByType[viewModel.type] = cell.textViewConfigured(with: self, placeholder: viewModel.placeholder, placeholderDelegate: self, clearDelegate: self, tag: indexPath.row) + cell.footerText = viewModel.footerText + cell.selectionStyle = .none + cell.apply(theme: theme) + return cell + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + guard + let cell = tableView.visibleCells[safeIndex: indexPath.row] as? InsertMediaSettingsTextTableViewCell, + let textViewHeightDelta = textViewHeightDelta, + textViewHeightDelta.row == indexPath.row + else { + return UITableView.automaticDimension + } + return cell.frame.size.height + textViewHeightDelta.value + } + +} + +extension InsertMediaSettingsViewController: UITextViewDelegate { + func textViewDidChange(_ textView: UITextView) { + updateTextViewHeight(textView) + } + + private func updateTextViewHeight(_ textView: UITextView) { + let oldHeight = textView.frame.size.height + let newHeight = textView.systemLayoutSizeFitting(textView.frame.size).height + guard oldHeight != newHeight else { + return + } + textViewHeightDelta = (newHeight - oldHeight, textView.tag) + UIView.performWithoutAnimation { + textView.frame.size.height = newHeight + tableView.beginUpdates() + tableView.endUpdates() + } + } +} + +extension InsertMediaSettingsViewController: ThemeableTextViewPlaceholderDelegate { + func themeableTextViewPlaceholderDidHide(_ themeableTextView: UITextView, isPlaceholderHidden: Bool) { + updateTextViewHeight(themeableTextView) + } +} + +extension InsertMediaSettingsViewController: ThemeableTextViewClearDelegate { + func themeableTextViewDidClear(_ themeableTextView: UITextView) { + updateTextViewHeight(themeableTextView) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsertMediaViewController.swift b/Apps/Wikipedia/Wikipedia/Code/InsertMediaViewController.swift new file mode 100644 index 0000000..c7f142f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsertMediaViewController.swift @@ -0,0 +1,343 @@ +protocol InsertMediaViewControllerDelegate: AnyObject { + func insertMediaViewController(_ insertMediaViewController: InsertMediaViewController, didTapCloseButton button: UIBarButtonItem) + func insertMediaViewController(_ insertMediaViewController: InsertMediaViewController, didPrepareWikitextToInsert wikitext: String) +} + +final class InsertMediaViewController: ViewController { + private let selectedImageViewController = InsertMediaSelectedImageViewController() + private let searchViewController: InsertMediaSearchViewController + private let searchResultsCollectionViewController = InsertMediaSearchResultsCollectionViewController() + + weak var delegate: InsertMediaViewControllerDelegate? + + init(articleTitle: String?, siteURL: URL?) { + searchViewController = InsertMediaSearchViewController(articleTitle: articleTitle, siteURL: siteURL) + searchResultsCollectionViewController.delegate = selectedImageViewController + super.init() + selectedImageViewController.delegate = self + searchViewController.progressController = FakeProgressController(progress: navigationBar, delegate: navigationBar) + searchViewController.delegate = self + searchViewController.searchBarDelegate = self + searchResultsCollectionViewController.scrollDelegate = self + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private lazy var closeButton: UIBarButtonItem = { + let closeButton = UIBarButtonItem.wmf_buttonType(.X, target: self, action: #selector(delegateCloseButtonTap(_:))) + closeButton.accessibilityLabel = CommonStrings.closeButtonAccessibilityLabel + return closeButton + }() + + private lazy var nextButton: UIBarButtonItem = { + let nextButton = UIBarButtonItem(title: CommonStrings.nextTitle, style: .done, target: self, action: #selector(goToMediaSettings(_:))) + nextButton.isEnabled = false + return nextButton + }() + + override func viewDidLoad() { + super.viewDidLoad() + navigationController?.isNavigationBarHidden = true + title = CommonStrings.insertMediaTitle + navigationItem.leftBarButtonItem = closeButton + navigationItem.rightBarButtonItem = nextButton + navigationItem.backBarButtonItem = UIBarButtonItem(title: CommonStrings.accessibilityBackTitle, style: .plain, target: nil, action: nil) + navigationBar.displayType = .modal + navigationBar.isBarHidingEnabled = false + navigationBar.isUnderBarViewHidingEnabled = true + navigationBar.isExtendedViewHidingEnabled = true + navigationBar.isTopSpacingHidingEnabled = false + + addChild(selectedImageViewController) + navigationBar.addUnderNavigationBarView(selectedImageViewController.view, shouldIgnoreSafeArea: true) + selectedImageViewController.didMove(toParent: self) + + addChild(searchViewController) + navigationBar.addExtendedNavigationBarView(searchViewController.view) + searchViewController.didMove(toParent: self) + + wmf_add(childController: searchResultsCollectionViewController, andConstrainToEdgesOfContainerView: view) + + additionalSafeAreaInsets = searchResultsCollectionViewController.additionalSafeAreaInsets + scrollView = searchResultsCollectionViewController.collectionView + } + + private var selectedImageViewHeightConstraint: NSLayoutConstraint? + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if selectedImageViewHeightConstraint == nil { + selectedImageViewHeightConstraint = selectedImageViewController.view.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3) + selectedImageViewHeightConstraint?.isActive = true + } + } + + private var isDisappearing = false + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + isDisappearing = true + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + isDisappearing = false + } + + private var isTransitioningToNewCollection = false + + override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) { + isTransitioningToNewCollection = true + super.willTransition(to: newCollection, with: coordinator) + coordinator.animate(alongsideTransition: { _ in + // + }) { _ in + self.isTransitioningToNewCollection = false + } + } + + @objc private func goToMediaSettings(_ sender: UIBarButtonItem) { + guard + let navigationController = navigationController, + let selectedSearchResult = selectedImageViewController.searchResult, + let image = selectedImageViewController.image ?? searchResultsCollectionViewController.selectedImage + else { + assertionFailure("Selected image and search result should be set by now") + return + } + let settingsViewController = InsertMediaSettingsViewController(image: image, searchResult: selectedSearchResult) + settingsViewController.title = WMFLocalizedString("insert-media-media-settings-title", value: "Media settings", comment: "Title for media settings view") + let insertButton = UIBarButtonItem(title: WMFLocalizedString("insert-action-title", value: "Insert", comment: "Title for insert action"), style: .done, target: self, action: #selector(insertMedia(_:))) + insertButton.tintColor = theme.colors.link + settingsViewController.navigationItem.rightBarButtonItem = insertButton + settingsViewController.apply(theme: theme) + navigationController.pushViewController(settingsViewController, animated: true) + } + + @objc private func insertMedia(_ sender: UIBarButtonItem) { + guard let mediaSettingsTableViewController = navigationController?.topViewController as? InsertMediaSettingsViewController else { + assertionFailure() + return + } + let searchResult = mediaSettingsTableViewController.searchResult + let wikitext: String + switch mediaSettingsTableViewController.settings { + case nil: + wikitext = "[[\(searchResult.fileTitle)]]" + case let mediaSettings?: + switch (mediaSettings.caption, mediaSettings.alternativeText) { + case (let caption?, let alternativeText?): + wikitext = """ + [[\(searchResult.fileTitle) | \(mediaSettings.advanced.imageType.rawValue) | \(mediaSettings.advanced.imageSize.rawValue) | \(mediaSettings.advanced.imagePosition.rawValue) | alt= \(alternativeText) | + \(caption)]] + """ + case (let caption?, nil): + wikitext = """ + [[\(searchResult.fileTitle) | \(mediaSettings.advanced.imageType.rawValue) | \(mediaSettings.advanced.imageSize.rawValue) | \(mediaSettings.advanced.imagePosition.rawValue) | \(caption)]] + """ + case (nil, let alternativeText?): + wikitext = """ + [[\(searchResult.fileTitle) | \(mediaSettings.advanced.imageType.rawValue) | \(mediaSettings.advanced.imageSize.rawValue) | \(mediaSettings.advanced.imagePosition.rawValue) | alt= \(alternativeText)]] + """ + default: + wikitext = """ + [[\(searchResult.fileTitle) | \(mediaSettings.advanced.imageType.rawValue) | \(mediaSettings.advanced.imageSize.rawValue) | \(mediaSettings.advanced.imagePosition.rawValue)]] + """ + } + } + delegate?.insertMediaViewController(self, didPrepareWikitextToInsert: wikitext) + } + + @objc private func delegateCloseButtonTap(_ sender: UIBarButtonItem) { + delegate?.insertMediaViewController(self, didTapCloseButton: sender) + } + + override func apply(theme: Theme) { + super.apply(theme: theme) + guard viewIfLoaded != nil else { + return + } + selectedImageViewController.apply(theme: theme) + searchViewController.apply(theme: theme) + searchResultsCollectionViewController.apply(theme: theme) + closeButton.tintColor = theme.colors.primaryText + nextButton.tintColor = theme.colors.link + } + + override func scrollViewInsetsDidChange() { + super.scrollViewInsetsDidChange() + searchResultsCollectionViewController.scrollViewInsetsDidChange() + } + + override func keyboardDidChangeFrame(from oldKeyboardFrame: CGRect?, newKeyboardFrame: CGRect?) { + guard !isAnimatingSearchBarState else { + return + } + super.keyboardDidChangeFrame(from: oldKeyboardFrame, newKeyboardFrame: newKeyboardFrame) + } + + override func accessibilityPerformEscape() -> Bool { + delegate?.insertMediaViewController(self, didTapCloseButton: closeButton) + return true + } + + var isAnimatingSearchBarState: Bool = false + + override var shouldAnimateWhileUpdatingScrollViewInsets: Bool { + return true + } + + func focusSearch(_ focus: Bool, animated: Bool = true, additionalAnimations: (() -> Void)? = nil) { + useNavigationBarVisibleHeightForScrollViewInsets = focus + navigationBar.isAdjustingHidingFromContentInsetChangesEnabled = true + let completion = { (finished: Bool) in + self.isAnimatingSearchBarState = false + self.useNavigationBarVisibleHeightForScrollViewInsets = focus + } + + let animations = { + let underBarViewPercentHidden: CGFloat + let extendedViewPercentHidden: CGFloat + if let scrollView = self.scrollView, scrollView.isAtTop, !focus { + underBarViewPercentHidden = 0 + extendedViewPercentHidden = 0 + } else { + underBarViewPercentHidden = 1 + extendedViewPercentHidden = focus ? 0 : 1 + } + self.navigationBar.setNavigationBarPercentHidden(0, underBarViewPercentHidden: underBarViewPercentHidden, extendedViewPercentHidden: extendedViewPercentHidden, topSpacingPercentHidden: 0, animated: false) + self.searchViewController.searchBar.setShowsCancelButton(focus, animated: animated) + additionalAnimations?() + self.view.layoutIfNeeded() + self.updateScrollViewInsets() + } + guard animated else { + animations() + completion(true) + return + } + isAnimatingSearchBarState = true + self.view.layoutIfNeeded() + UIView.animate(withDuration: 0.3, animations: animations, completion: completion) + } +} + +extension InsertMediaViewController: InsertMediaSearchViewControllerDelegate { + func insertMediaSearchViewController(_ insertMediaSearchViewController: InsertMediaSearchViewController, didFind searchResults: [InsertMediaSearchResult]) { + searchResultsCollectionViewController.emptyViewType = .noSearchResults + searchResultsCollectionViewController.searchResults = searchResults + } + + func insertMediaSearchViewController(_ insertMediaSearchViewController: InsertMediaSearchViewController, didFind imageInfo: MWKImageInfo, for searchResult: InsertMediaSearchResult, at index: Int) { + searchResultsCollectionViewController.setImageInfo(imageInfo, for: searchResult, at: index) + } + + func insertMediaSearchViewController(_ insertMediaSearchViewController: InsertMediaSearchViewController, didFailWithError error: Error) { + let emptyViewType: WMFEmptyViewType + let nserror = error as NSError + if nserror.wmf_isNetworkConnectionError() { + emptyViewType = .noInternetConnection + } else if nserror.domain == NSURLErrorDomain, nserror.code == NSURLErrorCancelled { + emptyViewType = .none + } else { + emptyViewType = .noSearchResults + } + searchResultsCollectionViewController.emptyViewType = emptyViewType + searchResultsCollectionViewController.searchResults = [] + } +} + +extension InsertMediaViewController: InsertMediaSelectedImageViewControllerDelegate { + func insertMediaSelectedImageViewController(_ insertMediaSelectedImageViewController: InsertMediaSelectedImageViewController, willSetSelectedImageFrom searchResult: InsertMediaSearchResult) { + nextButton.isEnabled = false + } + + func insertMediaSelectedImageViewController(_ insertMediaSelectedImageViewController: InsertMediaSelectedImageViewController, didSetSelectedImage selectedImage: UIImage?, from searchResult: InsertMediaSearchResult) { + nextButton.isEnabled = true + } +} + +extension InsertMediaViewController: InsertMediaSearchResultsCollectionViewControllerScrollDelegate { + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewDidScroll scrollView: UIScrollView) { + guard !isAnimatingSearchBarState else { + return + } + scrollViewDidScroll(scrollView) + } + + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewWillBeginDragging scrollView: UIScrollView) { + scrollViewWillBeginDragging(scrollView) + } + + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewWillEndDragging scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + scrollViewWillEndDragging(scrollView, withVelocity: velocity, targetContentOffset: targetContentOffset) + } + + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewDidEndDecelerating scrollView: UIScrollView) { + scrollViewDidEndDecelerating(scrollView) + } + + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewDidEndScrollingAnimation scrollView: UIScrollView) { + scrollViewDidEndScrollingAnimation(scrollView) + } + + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewShouldScrollToTop scrollView: UIScrollView) -> Bool { + return scrollViewShouldScrollToTop(scrollView) + } + + func insertMediaSearchResultsCollectionViewController(_ insertMediaSearchResultsCollectionViewController: InsertMediaSearchResultsCollectionViewController, scrollViewDidScrollToTop scrollView: UIScrollView) { + scrollViewDidScrollToTop(scrollView) + } +} + +extension InsertMediaViewController: UISearchBarDelegate { + func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + searchViewController.search(for: searchText) + } + + func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { + unfocusSearch { + searchBar.endEditing(true) + } + } + + func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { + navigationBar.isUnderBarViewHidingEnabled = false + navigationBar.isExtendedViewHidingEnabled = false + } + + func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { + guard !isAnimatingSearchBarState else { + return + } + unfocusSearch { + searchBar.endEditing(true) + searchBar.text = nil + } + } + + func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { + navigationBar.isUnderBarViewHidingEnabled = true + navigationBar.isExtendedViewHidingEnabled = true + } + + func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool { + guard !isAnimatingSearchBarState else { + return false + } + focusSearch(true) + return true + } + + private func unfocusSearch(additionalAnimations: (() -> Void)? = nil) { + navigationBar.isUnderBarViewHidingEnabled = true + navigationBar.isExtendedViewHidingEnabled = true + focusSearch(false, additionalAnimations: additionalAnimations) + } +} + +extension InsertMediaViewController: EditingFlowViewController { + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InsetLabelView.swift b/Apps/Wikipedia/Wikipedia/Code/InsetLabelView.swift new file mode 100644 index 0000000..0a74596 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InsetLabelView.swift @@ -0,0 +1,41 @@ +import UIKit + +/// A `UILabel` embedded within a `UIView` to allow easier styling +class InsetLabelView: SetupView { + + // MARK: - Properties + + let label: UILabel = UILabel() + + var insets: NSDirectionalEdgeInsets = .zero { + didSet { + labelLeadingAnchor?.constant = insets.leading + labelTrailingAnchor?.constant = insets.trailing + labelTopAnchor?.constant = insets.top + labelBottomAnchor?.constant = insets.bottom + } + } + + fileprivate var labelLeadingAnchor: NSLayoutConstraint? + fileprivate var labelTrailingAnchor: NSLayoutConstraint? + fileprivate var labelTopAnchor: NSLayoutConstraint? + fileprivate var labelBottomAnchor: NSLayoutConstraint? + + // MARK: - Setup + + override func setup() { + label.translatesAutoresizingMaskIntoConstraints = false + + addSubview(label) + + labelLeadingAnchor = label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: insets.leading) + labelTrailingAnchor = label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: insets.trailing) + labelTopAnchor = label.topAnchor.constraint(equalTo: topAnchor, constant: insets.top) + labelBottomAnchor = label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: insets.bottom) + + NSLayoutConstraint.activate( + [labelLeadingAnchor, labelTrailingAnchor, labelTopAnchor, labelBottomAnchor].compactMap { $0 } + ) + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/InternalLinkPreviewing.swift b/Apps/Wikipedia/Wikipedia/Code/InternalLinkPreviewing.swift new file mode 100644 index 0000000..f4d20f7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/InternalLinkPreviewing.swift @@ -0,0 +1,32 @@ +import Foundation + +protocol InternalLinkPreviewing: ViewController { } + +extension InternalLinkPreviewing { + func showInternalLink(url: URL) { + let exists: Bool + if let query = url.query { + exists = !query.contains("redlink=1") + } else { + exists = true + } + if !exists { + showRedLinkInAlert() + return + } + let dataStore = MWKDataStore.shared() + let internalLinkViewController = EditPreviewInternalLinkViewController(articleURL: url, dataStore: dataStore) + internalLinkViewController.modalPresentationStyle = .overCurrentContext + internalLinkViewController.modalTransitionStyle = .crossDissolve + internalLinkViewController.apply(theme: theme) + present(internalLinkViewController, animated: true, completion: nil) + } + + func showRedLinkInAlert() { + let title = WMFLocalizedString("wikitext-preview-link-not-found-preview-title", value: "No internal link found", comment: "Title for nonexistent link preview popup") + let message = WMFLocalizedString("wikitext-preview-link-not-found-preview-description", value: "Wikipedia does not have an article with this exact name", comment: "Description for nonexistent link preview popup") + let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: CommonStrings.okTitle, style: .default)) + present(alertController, animated: true) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/KeychainCredentialsManager.swift b/Apps/Wikipedia/Wikipedia/Code/KeychainCredentialsManager.swift new file mode 100644 index 0000000..9fd140f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/KeychainCredentialsManager.swift @@ -0,0 +1,20 @@ +public class KeychainCredentialsManager: NSObject { + private var keychainCredentials = WMFKeychainCredentials() + @objc public static let shared = KeychainCredentialsManager() + + public var username: String? { + get { + return keychainCredentials.userName + } set { + keychainCredentials.userName = newValue + } + } + + public var password: String? { + get { + return keychainCredentials.password + } set { + keychainCredentials.password = newValue + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Launch Screen.storyboard b/Apps/Wikipedia/Wikipedia/Code/Launch Screen.storyboard new file mode 100644 index 0000000..959b5e5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Launch Screen.storyboard @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/LegacyArticle.swift b/Apps/Wikipedia/Wikipedia/Code/LegacyArticle.swift new file mode 100644 index 0000000..5357801 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/LegacyArticle.swift @@ -0,0 +1,56 @@ +public struct LegacyArticle { + struct Section { + let info: [String: Any] + let html: String + init?(sectionFolderURL: URL) { + let sectionHTMLFileURL = sectionFolderURL.appendingPathComponent("Section.html") + let sectionPlistFileURL = sectionFolderURL.appendingPathComponent("Section.plist") + guard let htmlData = try? Data(contentsOf: sectionHTMLFileURL) else { + return nil + } + guard let html = String(data: htmlData, encoding: .utf8) else { + return nil + } + self.html = html + guard let sectionInfo = NSDictionary(contentsOf: sectionPlistFileURL) as? [String: Any] else { + return nil + } + self.info = sectionInfo + } + } + let info: [String: Any] + let sections: [Section] + public init?(articleFolderURL: URL) { + let articlePlistFileURL = articleFolderURL.appendingPathComponent("Article.plist") + guard let articleInfo = NSDictionary(contentsOf: articlePlistFileURL) as? [String: Any] else { + return nil + } + self.info = articleInfo + let articleSectionsFolderURL = articleFolderURL.appendingPathComponent("sections") + guard let sectionEnumerator = FileManager.default.enumerator(at: articleSectionsFolderURL, includingPropertiesForKeys: nil, options: .skipsSubdirectoryDescendants, errorHandler: { (fileURL, error) -> Bool in + return true + }) else { + return nil + } + var sections: [Section] = [] + for item in sectionEnumerator { + guard + let sectionFolderURL = item as? URL, + let section = Section(sectionFolderURL: sectionFolderURL) + else { + continue + } + sections.append(section) + } + sections.sort { (section1, section2) -> Bool in + guard let id1 = section1.info["id"] as? Int else { + return true + } + guard let id2 = section2.info["id"] as? Int else { + return false + } + return id1 < id2 + } + self.sections = sections + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/LibrariesUsed.storyboard b/Apps/Wikipedia/Wikipedia/Code/LibrariesUsed.storyboard new file mode 100644 index 0000000..8efdb9f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/LibrariesUsed.storyboard @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/LibrariesUsed.swift b/Apps/Wikipedia/Wikipedia/Code/LibrariesUsed.swift new file mode 100644 index 0000000..22e4a52 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/LibrariesUsed.swift @@ -0,0 +1,224 @@ +import WMF + +public struct LibraryUsed { + let title:String + let licenseName:String + let licenseText:String +} + +class LibrariesUsedViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { + var libraries:[LibraryUsed] = [] + @IBOutlet weak var tableView: UITableView! + + @objc public static let storyboardName = "LibrariesUsed" + + private static let cellReuseIdentifier = "org.wikimedia.libraries.used.cell" + private static let dataFileName = "LibrariesUsed.plist" + + private static let plistLibrariesUsedKey = "LibrariesUsed" + private static let plistTitleKey = "Title" + private static let plistLicenseNameKey = "LicenseName" + private static let plistLicenseTextKey = "LicenseText" + + fileprivate var theme = Theme.standard + + @objc func closeButtonPushed(_ : UIBarButtonItem) { + dismiss(animated: true, completion: nil) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named:"close"), style: .plain, target:self, action:#selector(closeButtonPushed(_:))) + navigationItem.leftBarButtonItem?.accessibilityLabel = CommonStrings.closeButtonAccessibilityLabel + } + + lazy private var tableHeaderView: UIView = { + let headerFrame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 56) + let headerView = UIView.init(frame: headerFrame) + let labelFrame = headerView.frame.insetBy(dx: 10, dy: 10) + let label = UILabel.init(frame: labelFrame) + label.adjustsFontForContentSizeCategory = true + label.font = UIFont.preferredFont(forTextStyle: .footnote) + label.textColor = self.theme.colors.primaryText + label.textAlignment = .center + label.numberOfLines = 0 + label.lineBreakMode = .byWordWrapping + label.text = String.localizedStringWithFormat(WMFLocalizedString("about-libraries-licenses-title", value:"We love open source software %1$@", comment:"Title for list of library licenses. %1$@ will be replaced with an emoji expressing our love for open source software"), "💖") + label.autoresizingMask = [.flexibleWidth, .flexibleHeight] + headerView.addSubview(label) + return headerView + }() + + override func viewDidLoad() { + super.viewDidLoad() + + self.apply(theme: self.theme) + view.backgroundColor = .gray400 + tableView.register(UITableViewCell.self, forCellReuseIdentifier: LibrariesUsedViewController.cellReuseIdentifier) + tableView.estimatedRowHeight = 41 + tableView.rowHeight = UITableView.automaticDimension + tableView.tableHeaderView = tableHeaderView + tableView.semanticContentAttribute = .forceLeftToRight + navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target:nil, action:nil) + + title = WMFLocalizedString("about-libraries", value:"Libraries used", comment:"Header text for libraries section (as in a collection of subprograms used to develop software) of the about page. Is not capitalised for aesthetic reasons, but could be capitalised in translations.") + + let fileName = LibrariesUsedViewController.dataFileName + guard + let plistPath = Bundle.main.path(forResource: fileName.wmf_substring(before: "."), ofType: fileName.wmf_substring(after: ".")) + else { + assertionFailure("Could find '\(fileName)' resource.") + return + } + libraries = librariesUsed(from: plistPath) + } + + private func librariesUsed(from plistPath: String) -> [LibraryUsed] { + guard + let dict = NSDictionary(contentsOfFile: plistPath) as? [String: Any], + let librariesUsedDataArray = dict[LibrariesUsedViewController.plistLibrariesUsedKey] as? [[String: Any]] + else { + assertionFailure("\n\nUnexpected items found in '\(plistPath)' or its '\(LibrariesUsedViewController.plistLibrariesUsedKey)' array.\n\n") + return [] + } + return librariesUsedDataArray + .compactMap {library -> LibraryUsed? in + guard + let title = library[LibrariesUsedViewController.plistTitleKey] as? String, + let licenseName = library[LibrariesUsedViewController.plistLicenseNameKey] as? String, + let licenseText = library[LibrariesUsedViewController.plistLicenseTextKey] as? String + else { + assertionFailure("\n\nOne of the following required keys not found in '\(LibrariesUsedViewController.plistLibrariesUsedKey)' array in '\(LibrariesUsedViewController.dataFileName)': '\(LibrariesUsedViewController.plistTitleKey)', '\(LibrariesUsedViewController.plistLicenseNameKey)', '\(LibrariesUsedViewController.plistLicenseTextKey)'\n\n") + return nil + } + return LibraryUsed.init(title: title.wmf_stringByCapitalizingFirstCharacter(usingWikipediaLanguageCode: "en"), licenseName: licenseName, licenseText: licenseText) + } + .sorted(by: { + $0.title < $1.title + }) + } + + func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return libraries.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: LibrariesUsedViewController.cellReuseIdentifier, for: indexPath) + cell.contentView.semanticContentAttribute = .forceLeftToRight + cell.textLabel?.semanticContentAttribute = .forceLeftToRight + cell.textLabel?.textAlignment = .left + + cell.backgroundColor = theme.colors.paperBackground + cell.textLabel?.textColor = theme.colors.primaryText + + cell.selectionStyle = .default + cell.selectedBackgroundView = UIView() + cell.selectedBackgroundView?.backgroundColor = theme.colors.midBackground + + let library:LibraryUsed = self.libraries[indexPath.row] + cell.textLabel?.text = library.title + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + let libraryVC = LibraryUsedViewController.wmf_viewControllerFromStoryboardNamed(LibrariesUsedViewController.storyboardName) + libraryVC.apply(theme: self.theme) + let library = self.libraries[indexPath.row] + libraryVC.library = library + libraryVC.title = library.title + navigationController?.pushViewController(libraryVC, animated: true) + } +} + +class LibraryUsedViewController: UIViewController { + @IBOutlet weak var textView: UITextView! + public var library: LibraryUsed? + + fileprivate var theme = Theme.standard + + override func viewDidLoad() { + super.viewDidLoad() + + self.apply(theme: self.theme) + + textView.adjustsFontForContentSizeCategory = true + textView.textContainerInset = UIEdgeInsets.init(top: 10, left: 10, bottom: 10, right: 10) + guard let licenseText = library?.licenseText else { return } + textView.text = normalizeWhitespaceForBetterReadability(from: licenseText) + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + textView.setContentOffset(.zero, animated: false) + } + + private var newlineOptionalWhitespaceNewlineRegex: NSRegularExpression? = { + do { + return try NSRegularExpression(pattern: "\\R\\s*\\R", options: []) + } catch { + assertionFailure("regex failed to compile") + } + return nil + }() + + private var oneOrMoreWhitespaceCharactersRegex: NSRegularExpression? = { + do { + return try NSRegularExpression(pattern: "\\s+", options: []) + } catch { + assertionFailure("regex failed to compile") + } + return nil + }() + + // Minimal cleanups on license text. + // - consecutive line breaks reduce to 2 line breaks + // - non-consecutive line breaks converted to spaces (similar to HTML) + // Imperfect but *vast* improvement in readability especially with line wrapping. + private func normalizeWhitespaceForBetterReadability(from licenseString: String) -> String { + guard + let multiNewlineRegex = newlineOptionalWhitespaceNewlineRegex, + let whitespaceRegex = oneOrMoreWhitespaceCharactersRegex + else { + assertionFailure("regex(s) failed to compile") + return licenseString + } + var string = licenseString + let placeholder = "#temporary_placeholder#" + string = multiNewlineRegex.stringByReplacingMatches(in: string, options: [], range: string.fullRange, withTemplate: placeholder) + string = string.components(separatedBy: .newlines).joined(separator: " ") + string = whitespaceRegex.stringByReplacingMatches(in: string, options: [], range: string.fullRange, withTemplate: " ") + string = string.replacingOccurrences(of: placeholder, with: "\n\n") + return string + } +} + +extension LibrariesUsedViewController: Themeable { + public func apply(theme: Theme) { + self.theme = theme + + guard viewIfLoaded != nil else { + return + } + tableView.backgroundColor = theme.colors.baseBackground + tableView.separatorColor = theme.colors.chromeBackground + tableView.reloadData() + } +} + +extension LibraryUsedViewController: Themeable { + public func apply(theme: Theme) { + self.theme = theme + + guard viewIfLoaded != nil else { + return + } + self.view.backgroundColor = theme.colors.baseBackground + self.textView.backgroundColor = theme.colors.baseBackground + self.textView.textColor = theme.colors.primaryText + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/LicenseView.swift b/Apps/Wikipedia/Wikipedia/Code/LicenseView.swift new file mode 100644 index 0000000..10e7027 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/LicenseView.swift @@ -0,0 +1,24 @@ +import Foundation + +@objc (WMFLicenseView) +class LicenseView: UIStackView { + @objc public var licenseCodes: [String] = [] { + didSet { + axis = .horizontal + alignment = .center + spacing = 5 + for view in arrangedSubviews { + view.removeFromSuperview() + } + for license in licenseCodes { + guard let image = UIImage(named: "license-" + license) else { + continue + } + let imageView = UIImageView(image: image) + imageView.contentMode = .center + imageView.setContentCompressionResistancePriority(.required, for: .horizontal) + addArrangedSubview(imageView) + } + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Link.swift b/Apps/Wikipedia/Wikipedia/Code/Link.swift new file mode 100644 index 0000000..fa96dee --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Link.swift @@ -0,0 +1,33 @@ +import Foundation + +struct Link { + let page: String + let label: String? + let exists: Bool + + init?(page: String?, label: String?, exists: Bool?) { + guard let page = page else { + assertionFailure("Attempting to create a Link without a page") + return nil + } + guard let exists = exists else { + assertionFailure("Attempting to create a Link without information about whether it's an existing link") + return nil + } + self.page = page + self.label = label + self.exists = exists + } + + var hasLabel: Bool { + return label != nil + } + + func articleURL(for siteURL: URL) -> URL? { + guard exists else { + return nil + } + return siteURL.wmf_URL(withTitle: page) + } +} + diff --git a/Apps/Wikipedia/Wikipedia/Code/LinkOnlyTextView.swift b/Apps/Wikipedia/Wikipedia/Code/LinkOnlyTextView.swift new file mode 100644 index 0000000..d9aae68 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/LinkOnlyTextView.swift @@ -0,0 +1,26 @@ +// https://stackoverflow.com/a/47913329 + +import Foundation + +class LinkOnlyTextView: UITextView { + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + guard !UIAccessibility.isVoiceOverRunning else { + return super.point(inside: point, with: event) + } + + let glyphIndex = self.layoutManager.glyphIndex(for: point, in: self.textContainer) + + // Ensure the glyphIndex actually matches the point and isn't just the closest glyph to the point + let glyphRect = self.layoutManager.boundingRect(forGlyphRange: NSRange(location: glyphIndex, length: 1), in: self.textContainer) + + if glyphIndex < self.textStorage.length, + glyphRect.contains(point), + self.textStorage.attribute(NSAttributedString.Key.link, at: glyphIndex, effectiveRange: nil) != nil { + + return true + } else { + return false + } + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/Loadable.swift b/Apps/Wikipedia/Wikipedia/Code/Loadable.swift new file mode 100644 index 0000000..1023515 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/Loadable.swift @@ -0,0 +1,6 @@ +import Foundation + +protocol Loadable { + func startLoading() + func stopLoading() +} diff --git a/Apps/Wikipedia/Wikipedia/Code/LoadingAnimationViewController.swift b/Apps/Wikipedia/Wikipedia/Code/LoadingAnimationViewController.swift new file mode 100644 index 0000000..73c27a2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/LoadingAnimationViewController.swift @@ -0,0 +1,100 @@ +import UIKit + +class LoadingAnimationViewController: UIViewController { + + var theme: Theme = Theme.standard + + var cancelBlock: (() -> Void)? + @IBOutlet private var satelliteImageView: UIImageView! + @IBOutlet private var backgroundView: UIView! + @IBOutlet private var cancelButton: UIButton! + @IBOutlet private var backgroundImageView: UIImageView! + @IBOutlet private var statusLabel: UILabel! + + override func viewDidLoad() { + super.viewDidLoad() + + apply(theme: theme) + + cancelButton.setTitle(CommonStrings.cancelActionTitle, for: .normal) + statusLabel.text = WMFLocalizedString("data-migration-status", value: "Updating...", comment: "Message displayed during a long running data migration.") + view.accessibilityViewIsModal = true + + updateFonts() + } + + @objc func applicationDidBecomeActive(_ notification: Notification) { + satelliteImageView.startRotating() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + satelliteImageView.startRotating() + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: statusLabel) + NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + satelliteImageView.stopRotating() + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: nil) + NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil) + } + + @IBAction func tappedCancel(_ sender: UIButton) { + cancelBlock?() + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + updateFonts() + } + + private func updateFonts() { + cancelButton.titleLabel?.font = UIFont.wmf_font(.semiboldTitle3, compatibleWithTraitCollection: traitCollection) + statusLabel.font = UIFont.wmf_font(.boldHeadline, compatibleWithTraitCollection: traitCollection) + } +} + +private extension UIView { + + var rotationKey: String { + return "rotation" + } + + func startRotating(duration: Double = 1) { + let kAnimationKey = rotationKey + + if layer.animation(forKey: kAnimationKey) == nil { + let animate = CABasicAnimation(keyPath: "transform.rotation") + animate.duration = duration + animate.repeatCount = Float.infinity + animate.fromValue = 0.0 + animate.toValue = Float(.pi * 2.0) + layer.add(animate, forKey: kAnimationKey) + } + } + func stopRotating() { + let kAnimationKey = rotationKey + + if self.layer.animation(forKey: kAnimationKey) != nil { + self.layer.removeAnimation(forKey: kAnimationKey) + } + } +} + +extension LoadingAnimationViewController: Themeable { + func apply(theme: Theme) { + self.theme = theme + guard viewIfLoaded != nil else { + return + } + + backgroundView.backgroundColor = theme.colors.paperBackground + cancelButton.setTitleColor(theme.colors.link, for: .normal) + backgroundImageView.tintColor = theme.colors.animationBackground + statusLabel.textColor = theme.colors.primaryText + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/LoadingAnimationViewController.xib b/Apps/Wikipedia/Wikipedia/Code/LoadingAnimationViewController.xib new file mode 100644 index 0000000..554ef69 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/LoadingAnimationViewController.xib @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/Code/LoggingDefaults.swift b/Apps/Wikipedia/Wikipedia/Code/LoggingDefaults.swift new file mode 100644 index 0000000..a9ee629 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/LoggingDefaults.swift @@ -0,0 +1,7 @@ +import Foundation +import CocoaLumberjackSwift +extension DDLog { + @objc public class func wmf_setSwiftDefaultLogLevel(_ level: UInt) { + dynamicLogLevel = DDLogLevel(rawValue: level)! + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/LoginFunnel.swift b/Apps/Wikipedia/Wikipedia/Code/LoginFunnel.swift new file mode 100644 index 0000000..7c1e317 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/LoginFunnel.swift @@ -0,0 +1,81 @@ +// https://meta.wikimedia.org/wiki/Schema:MobileWikiAppiOSLoginAction + +@objc final class LoginFunnel: EventLoggingFunnel, EventLoggingStandardEventProviding { + @objc public static let shared = LoginFunnel() + + private override init() { + super.init(schema: "MobileWikiAppiOSLoginAction", version: 18121305) + } + + private enum Action: String { + case impression + case loginStart = "login_start" + case logout + case loginSuccess = "login_success" + case createAccountStart = "createaccount_start" + case createAccountSuccess = "createaccount_success" + } + + private func event(category: EventLoggingCategory, label: EventLoggingLabel?, action: Action, measure: Double? = nil) -> [String: Any] { + let category = category.rawValue + let action = action.rawValue + + var event: [String: Any] = ["category": category, "action": action, "primary_language": primaryLanguage(), "is_anon": isAnon] + if let label = label?.rawValue { + event["label"] = label + } + if let measure = measure { + event["measure_time"] = Int(round(measure)) + } + return event + } + + override func preprocessData(_ eventData: [AnyHashable: Any]) -> [AnyHashable: Any] { + return wholeEvent(with: eventData) + } + + // MARK: - Feed + + @objc public func logLoginImpressionInFeed() { + log(event(category: .feed, label: .syncEducation, action: .impression)) + } + + @objc public func logLoginStartInFeed() { + log(event(category: .feed, label: .syncEducation, action: .loginStart)) + } + + // MARK: - Login screen + + public func logSuccess(timeElapsed: Double?) { + log(event(category: .login, label: nil, action: .loginSuccess, measure: timeElapsed)) + } + + @objc public func logCreateAccountAttempt() { + log(event(category: .login, label: nil, action: .createAccountStart)) + } + + public func logCreateAccountSuccess(timeElapsed: Double?) { + log(event(category: .login, label: nil, action: .createAccountSuccess, measure: timeElapsed)) + } + + // MARK: - Settings + + @objc public func logLoginStartInSettings() { + log(event(category: .setting, label: .login, action: .loginStart)) + } + + @objc public func logLogoutInSettings() { + log(event(category: .setting, label: .login, action: .logout)) + } + + // MARK: - Sync popovers + + public func logLoginImpressionInSyncPopover() { + log(event(category: .loginToSyncPopover, label: nil, action: .impression)) + } + + public func logLoginStartInSyncPopover() { + log(event(category: .loginToSyncPopover, label: nil, action: .loginStart)) + } + +} diff --git a/Apps/Wikipedia/Wikipedia/Code/MKCoordinateRegion+Dimensions.swift b/Apps/Wikipedia/Wikipedia/Code/MKCoordinateRegion+Dimensions.swift new file mode 100644 index 0000000..3a8808a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MKCoordinateRegion+Dimensions.swift @@ -0,0 +1,19 @@ +import MapKit + +extension MKCoordinateRegion { + var width: CLLocationDistance { + let halfLongitudeDelta = span.longitudeDelta * 0.5 + let left = CLLocation(latitude: center.latitude, longitude: center.longitude - halfLongitudeDelta) + let right = CLLocation(latitude: center.latitude, longitude: center.longitude + halfLongitudeDelta) + let width = right.distance(from: left) + return width + } + + var height: CLLocationDistance { + let halfLatitudeDelta = span.latitudeDelta * 0.5 + let top = CLLocation(latitude: center.latitude + halfLatitudeDelta, longitude: center.longitude) + let bottom = CLLocation(latitude: center.latitude - halfLatitudeDelta, longitude: center.longitude) + let height = top.distance(from: bottom) + return height + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/MTLValueTransformer+WMFNumericValueTransformer.h b/Apps/Wikipedia/Wikipedia/Code/MTLValueTransformer+WMFNumericValueTransformer.h new file mode 100644 index 0000000..0f0718b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MTLValueTransformer+WMFNumericValueTransformer.h @@ -0,0 +1,10 @@ +@import WMF; + +/** + * Transformer which can handle both numbers or strings on input, and produces numbers on ouput. + */ +@interface MTLValueTransformer (WMFNumericValueTransformer) + ++ (instancetype)wmf_numericValueTransformer; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MTLValueTransformer+WMFNumericValueTransformer.m b/Apps/Wikipedia/Wikipedia/Code/MTLValueTransformer+WMFNumericValueTransformer.m new file mode 100644 index 0000000..10c8d51 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MTLValueTransformer+WMFNumericValueTransformer.m @@ -0,0 +1,35 @@ +#import "MTLValueTransformer+WMFNumericValueTransformer.h" +#import "NSValueTransformer+MTLPredefinedTransformerAdditions.h" +@import WMF.WMFOutParamUtils; + +static NSString *const WMFNumericTransformerErrorDomain = @"WMFNumericTransformerErrorDomain"; + +typedef NS_ENUM(NSInteger, WMFNumericTransformerErrorCode) { + WMFNumericTransformerErrorDomainInvalidString +}; + +@implementation MTLValueTransformer (WMFNumericValueTransformer) + ++ (instancetype)wmf_numericValueTransformer { + NSValueTransformer *validatingNumberTransformer = + [MTLValueTransformer mtl_validatingTransformerForClass:[NSNumber class]]; + return [MTLValueTransformer transformerUsingForwardBlock:^id(id stringOrNumber, BOOL *success, NSError *__autoreleasing *error) { + if ([stringOrNumber isKindOfClass:[NSString class]]) { + double value = 0.0; + if ([[NSScanner scannerWithString:stringOrNumber] scanDouble:&value]) { + return @(value); + } else { + *success = NO; + WMFSafeAssign(error, + [NSError errorWithDomain:WMFNumericTransformerErrorDomain + code:WMFNumericTransformerErrorDomainInvalidString + userInfo:nil]); + return nil; + } + } else { + return [validatingNumberTransformer transformedValue:stringOrNumber success:success error:error]; + } + }]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKDataObject.h b/Apps/Wikipedia/Wikipedia/Code/MWKDataObject.h new file mode 100644 index 0000000..164dde3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKDataObject.h @@ -0,0 +1,23 @@ +#import + +@interface MWKDataObject : WMFMTLModel + +- (id)dataExport; + +- (NSString *)optionalString:(NSString *)key dict:(NSDictionary *)dict; +- (NSString *)requiredString:(NSString *)key dict:(NSDictionary *)dict; +- (NSString *)requiredString:(NSString *)key dict:(NSDictionary *)dict allowEmpty:(BOOL)allowEmpty; + +- (NSNumber *)optionalNumber:(NSString *)key dict:(NSDictionary *)dict; +- (NSNumber *)requiredNumber:(NSString *)key dict:(NSDictionary *)dict; +- (NSNumber *)numberWithString:(NSString *)str; + +- (NSDate *)optionalDate:(NSString *)key dict:(NSDictionary *)dict; +- (NSDate *)requiredDate:(NSString *)key dict:(NSDictionary *)dict; +- (NSDate *)getDateFromIso8601DateString:(NSString *)string; +- (NSString *)iso8601DateString:(NSDate *)date; + +- (NSDictionary *)optionalDictionary:(NSString *)key dict:(NSDictionary *)dict; +- (NSDictionary *)requiredDictionary:(NSString *)key dict:(NSDictionary *)dict; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKDataObject.m b/Apps/Wikipedia/Wikipedia/Code/MWKDataObject.m new file mode 100644 index 0000000..aec06ca --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKDataObject.m @@ -0,0 +1,143 @@ +#import +#import + +@implementation MWKDataObject + +- (id)dataExport; +{ + @throw [NSException exceptionWithName:@"MWKDataObjectException" + reason:@"dataExport not implemented" + userInfo:@{}]; +} + +#pragma mark - string methods + +- (NSString *)optionalString:(NSString *)key dict:(NSDictionary *)dict { + id obj = dict[key]; + if (obj == nil) { + return nil; + } else if ([obj isKindOfClass:[NSString class]]) { + return (NSString *)obj; + } else { + @throw [NSException exceptionWithName:@"MWKDataObjectException" + reason:@"expected string, got something else" + userInfo:@{@"key": key}]; + } +} + +- (NSString *)requiredString:(NSString *)key dict:(NSDictionary *)dict { + return [self requiredString:key dict:dict allowEmpty:YES]; +} + +- (NSString *)requiredString:(NSString *)key dict:(NSDictionary *)dict allowEmpty:(BOOL)allowEmpty { + NSString *str = [self optionalString:key dict:dict]; + if (str == nil || (str.length == 0 && !allowEmpty)) { + @throw [NSException exceptionWithName:@"MWKDataObjectException" + reason:@"expected string, got nothing" + userInfo:@{@"key": key}]; + } else { + return str; + } +} + +#pragma mark - number methods + +- (NSNumber *)optionalNumber:(NSString *)key dict:(NSDictionary *)dict { + id obj = dict[key]; + if (obj == nil) { + return nil; + } else if ([obj isKindOfClass:[NSNumber class]]) { + return (NSNumber *)obj; + } else if ([obj isKindOfClass:[NSString class]]) { + // PHP is often fuzzy and sometimes gives us strings when we wanted integers. + return [self numberWithString:(NSString *)obj]; + } else { + @throw [NSException exceptionWithName:@"MWKDataObjectException" reason:@"expected string or nothing, got something else" userInfo:nil]; + } +} + +- (NSNumber *)requiredNumber:(NSString *)key dict:(NSDictionary *)dict { + NSNumber *num = [self optionalNumber:key dict:dict]; + if (num == nil) { + @throw [NSException exceptionWithName:@"MWKDataObjectException" + reason:@"missing required number field" + userInfo:@{@"key": key}]; + } else { + return num; + } +} + +- (NSNumber *)numberWithString:(NSString *)str { + if ([str rangeOfString:@"."].location != NSNotFound || + [str rangeOfString:@"e"].location != NSNotFound) { + double val = [str doubleValue]; + return [NSNumber numberWithDouble:val]; + } else { + int val = [str intValue]; + return [NSNumber numberWithInt:val]; + } +} + +#pragma mark - date methods + +- (NSDate *)optionalDate:(NSString *)key dict:(NSDictionary *)dict { + NSString *str = [self optionalString:key dict:dict]; + if (str == nil) { + return nil; + } else { + return [self getDateFromIso8601DateString:str]; + } +} + +- (NSDate *)requiredDate:(NSString *)key dict:(NSDictionary *)dict { + NSDate *date = [self optionalDate:key dict:dict]; + if (date == nil) { + @throw [NSException exceptionWithName:@"MWKDataObjectException" + reason:@"missing required date field" + userInfo:@{@"key": key}]; + } else { + return date; + } +} + +#pragma mark - date methods + +- (NSDate *)getDateFromIso8601DateString:(NSString *)string { + return [[NSDateFormatter wmf_iso8601Formatter] dateFromString:string]; +} + +- (NSString *)iso8601DateString:(NSDate *)date { + return [[NSDateFormatter wmf_iso8601Formatter] stringFromDate:date]; +} + +#pragma mark - dictionary methods + +- (NSDictionary *)optionalDictionary:(NSString *)key dict:(NSDictionary *)dict { + id obj = dict[key]; + if (obj == nil) { + return nil; + } else if ([obj isKindOfClass:[NSArray class]]) { + // PHP likes to output empty associative arrays as empty JSON arrays, + // which become empty NSArrays. + return @{}; + } else if ([obj isKindOfClass:[NSDictionary class]]) { + return (NSDictionary *)obj; + } else { + @throw [NSException exceptionWithName:@"MWKDataObjectException" + reason:@"expected dictionary, got something else" + userInfo:@{@"key": key}]; + } +} + +- (NSDictionary *)requiredDictionary:(NSString *)key dict:(NSDictionary *)dict { + NSDictionary *obj = [self optionalDictionary:key dict:dict]; + if (obj == nil) { + @throw [NSException exceptionWithName:@"MWKDataObjectException" + reason:@"missing required dictionary field" + userInfo:@{@"key": key}]; + } else { + return obj; + } +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKDataStore+LegacyMobileview.swift b/Apps/Wikipedia/Wikipedia/Code/MWKDataStore+LegacyMobileview.swift new file mode 100644 index 0000000..ff4ca1c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKDataStore+LegacyMobileview.swift @@ -0,0 +1,97 @@ +import CocoaLumberjackSwift + +enum MigrateMobileviewToMobileHTMLIfNecessaryError: Error { + case noArticleURL + case noArticleCacheController + case noLegacyArticleData + case noMobileHTML +} + +extension MWKDataStore { + // TODO: use this method's completion block when loading articles (in case a mobileview conversion hasn't happened yet for that article's saved data for any reason) + func migrateMobileviewToMobileHTMLIfNecessary(article: WMFArticle, completionHandler: @escaping ((Error?) -> Void)) { + guard article.isConversionFromMobileViewNeeded == true else { + // If conversion was previously attempted don't try again. + completionHandler(nil) + return + } + guard let articleURL = article.url else { + assertionFailure("Could not get article url") + completionHandler(MigrateMobileviewToMobileHTMLIfNecessaryError.noArticleURL) + return + } + + let articleCacheController = cacheController.articleCache + let articleFolderURL = URL(fileURLWithPath: path(forArticleURL: articleURL)) + guard let legacyArticle = LegacyArticle(articleFolderURL: articleFolderURL) else { + completionHandler(MigrateMobileviewToMobileHTMLIfNecessaryError.noLegacyArticleData) + return + } + + mobileviewConverter.convertMobileviewSavedDataToMobileHTML(articleURL: articleURL, article: legacyArticle) { (result, error) in + let removeArticleMobileviewSavedDataFolder = { + // Remove old mobileview saved data folder for this article + do { + try FileManager.default.removeItem(atPath: self.path(forArticleURL: articleURL)) + } catch { + DDLogError("Could not remove mobileview folder for articleURL: \(articleURL)") + } + } + + let handleConversionFailure = { + // No need to keep mobileview section html if conversion failed, so ok to remove section data + // because we're setting `isDownloaded` next so saved article fetching will re-download from + // new mobilehtml endpoint. + removeArticleMobileviewSavedDataFolder() + + // If conversion failed above for any reason set "article.isDownloaded" to false so normal fetching logic picks it up + DispatchQueue.main.async { + do { + article.isDownloaded = false + article.isConversionFromMobileViewNeeded = false + try self.save() + } catch let error { + DDLogError("Error updating article: \(error)") + } + } + } + + guard error == nil, let result = result else { + handleConversionFailure() + completionHandler(error) + return + } + guard let mobileHTML = result as? String else { + handleConversionFailure() + completionHandler(MigrateMobileviewToMobileHTMLIfNecessaryError.noMobileHTML) + return + } + + articleCacheController.cacheFromMigration(desktopArticleURL: articleURL, content: mobileHTML) { error in + // Conversion succeeded so can safely blast old mobileview folder. + removeArticleMobileviewSavedDataFolder() + DispatchQueue.main.async { + do { + article.isConversionFromMobileViewNeeded = false + try self.save() + } catch let error { + completionHandler(error) + DDLogError("Error updating article: \(error)") + return + } + + completionHandler(nil) + } + } + } + } + + func removeAllLegacyArticleData() { + let fileURL = URL(fileURLWithPath: basePath) + let titlesToRemoveFileURL = fileURL.appendingPathComponent("TitlesToRemove.plist") + let sitesFolderURL = fileURL.appendingPathComponent("sites") + let fm = FileManager.default + try? fm.removeItem(at: titlesToRemoveFileURL) + try? fm.removeItem(at: sitesFolderURL) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKDataStore.h b/Apps/Wikipedia/Wikipedia/Code/MWKDataStore.h new file mode 100644 index 0000000..73eb3e9 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKDataStore.h @@ -0,0 +1,155 @@ +@import Foundation; +#import + +@class MWKSavedPageList; +@class MWKRecentSearchList; +@class WMFArticle; +@class WMFExploreFeedContentController; +@class WMFReadingListsController; +@class RemoteNotificationsController; +@class WMFArticleSummaryController; +@class MobileviewToMobileHTMLConverter; +@class MWKLanguageLinkController; +@class WMFSession; +@class WMFConfiguration; +@class WMFPermanentCacheController; +@class WMFNotificationsController; +@class WMFAuthenticationManager; +@class WMFABTestsController; + +@protocol ABTestsPersisting; + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXPORT NSString *const MWKDataStoreValidImageSitePrefix; + +/** + * Creates an image URL by appending @c path to @c MWKDataStoreValidImageSitePrefix. + * @param path The relative path to an image without the leading slash. For example, + * @"File.jpg/440px-File.jpg". + */ +extern NSString *MWKCreateImageURLWithPath(NSString *path); + +/** + * Subscribe to get notifications when a WMFArticle is + * added to saved pages, history, etc… + */ +extern NSString *const WMFArticleUpdatedNotification; +extern NSString *const WMFArticleDeletedNotification; +extern NSString *const WMFArticleDeletedNotificationUserInfoArticleKeyKey; // User info key for the article key +extern NSString *const WMFBackgroundContextDidSave; +extern NSString *const WMFFeedImportContextDidSave; +extern NSString *const WMFViewContextDidSave; +extern NSString *const WMFViewContextDidResetNotification; + +typedef NS_OPTIONS(NSUInteger, RemoteConfigOption) { + RemoteConfigOptionReadingLists = 1 << 0, + RemoteConfigOptionGeneric = 1 << 1 +}; + +@interface MWKDataStore : NSObject + +/// The current library version as used in migrations +@property (class, nonatomic, readonly) NSInteger currentLibraryVersion; + +/** + * Initialize with sharedInstance database and legacyDataBasePath + * + * @return A data store + */ +- (instancetype)init; + +- (instancetype)initWithContainerURL:(NSURL *)containerURL NS_DESIGNATED_INITIALIZER; + +/// Call to cancel any async tasks and wait for completion +- (void)teardown:(nullable dispatch_block_t)completion; + +@property (readonly, strong, nonatomic) NSURL *containerURL; +@property (readonly, strong, nonatomic) WMFSession *session; +@property (readonly, strong, nonatomic) WMFConfiguration *configuration; +@property (readonly, strong, nonatomic) WMFPermanentCacheController *cacheController; + +- (void)performLibraryUpdates:(dispatch_block_t)completion needsMigrateBlock:(dispatch_block_t)needsMigrateBlock; +- (void)performInitialLibrarySetup; +#if TEST +- (void)performTestLibrarySetup; +#endif + +- (void)updateLocalConfigurationFromRemoteConfigurationWithCompletion:(nullable void (^)(NSError *nullable))completion; +@property (readwrite, nonatomic) BOOL isLocalConfigUpdateAllowed; +@property (readonly, nonatomic) RemoteConfigOption remoteConfigsThatFailedUpdate; + +@property (readonly, strong, nonatomic) WMFAuthenticationManager *authenticationManager; +@property (readonly, strong, nonatomic) MWKSavedPageList *savedPageList; +@property (readonly, strong, nonatomic) MWKRecentSearchList *recentSearchList; +@property (readonly, strong, nonatomic) WMFReadingListsController *readingListsController; +@property (readonly, strong, nonatomic) RemoteNotificationsController *remoteNotificationsController; +@property (readonly, strong, nonatomic) WMFArticleSummaryController *articleSummaryController; +@property (readonly, strong, nonatomic) MWKLanguageLinkController *languageLinkController; +@property (readonly, strong, nonatomic) WMFNotificationsController *notificationsController; + +@property (nonatomic, strong, readonly) NSManagedObjectContext *viewContext; +@property (nonatomic, strong, readonly) NSManagedObjectContext *feedImportContext; + +/** + * Returns the siteURL of the user's first preferred language. + */ +@property (readonly, copy, nonatomic, nullable) NSURL *primarySiteURL; + +#pragma mark - Caching + +@property (readonly, strong, nonatomic) MobileviewToMobileHTMLConverter *mobileviewConverter; + +- (void)performBackgroundCoreDataOperationOnATemporaryContext:(nonnull void (^)(NSManagedObjectContext *moc))mocBlock; + +@property (nonatomic, strong, readonly) WMFExploreFeedContentController *feedContentController; + +- (void)teardownFeedImportContext; + +- (void)prefetchArticles; // fill the article cache to speed up initial feed load + +- (nullable WMFArticle *)fetchArticleWithURL:(NSURL *)URL inManagedObjectContext:(NSManagedObjectContext *)moc; +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(NSURL *)URL inManagedObjectContext:(NSManagedObjectContext *)moc; +- (nullable WMFArticle *)fetchArticleWithKey:(NSString *)key variant:(nullable NSString *)variant inManagedObjectContext:(NSManagedObjectContext *)moc; + +- (nullable WMFArticle *)fetchArticleWithURL:(NSURL *)URL; //uses the view context +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(NSURL *)URL; //uses the view context +- (nullable WMFArticle *)fetchArticleWithKey:(NSString *)key variant:(nullable NSString *)variant; //uses the view context +- (nullable WMFArticle *)fetchArticleWithKey:(NSString *)key; // Temporary shim for areas like reading lists that are not yet variant-aware + +- (nullable WMFArticle *)fetchArticleWithWikidataID:(NSString *)wikidataID; //uses the view context + +- (BOOL)save:(NSError **)error; + +- (void)clearMemoryCache; + +/// Clears both the memory cache and the URLSession cache +- (void)clearTemporaryCache; + +#pragma mark - Legacy Datastore methods + +@property (readonly, copy, nonatomic) NSString *basePath; + +/// Deprecated: Use dependency injection ++ (MWKDataStore *)shared; + +/// Deprecated: Used only for mobile-html conversion +- (NSString *)pathForArticleURL:(NSURL *)url; + +- (BOOL)saveRecentSearchList:(MWKRecentSearchList *)list error:(NSError **)error; + +- (NSArray *)recentSearchListData; + +// Storage helper methods + +- (NSError *)removeFolderAtBasePath; + +#pragma mark - ABTestsController + +@property (readonly, strong, nonatomic) WMFABTestsController *abTestsController; + +- (void)setupAbTestsControllerWithPersistenceService: (id)persistenceService; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKDataStore.m b/Apps/Wikipedia/Wikipedia/Code/MWKDataStore.m new file mode 100644 index 0000000..8589f61 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKDataStore.m @@ -0,0 +1,1025 @@ +#import +#import +#import "WMFAnnouncement.h" + +@import CoreData; + +// Emitted when article state changes. Can be used for things such as being notified when article 'saved' state changes. +NSString *const WMFArticleUpdatedNotification = @"WMFArticleUpdatedNotification"; +NSString *const WMFArticleDeletedNotification = @"WMFArticleDeletedNotification"; +NSString *const WMFArticleDeletedNotificationUserInfoArticleKeyKey = @"WMFArticleDeletedNotificationUserInfoArticleKeyKey"; +NSString *const WMFBackgroundContextDidSave = @"WMFBackgroundContextDidSave"; +NSString *const WMFFeedImportContextDidSave = @"WMFFeedImportContextDidSave"; +NSString *const WMFViewContextDidSave = @"WMFViewContextDidSave"; +NSString *const WMFViewContextDidResetNotification = @"WMFViewContextDidResetNotification"; + +NSString *const WMFLibraryVersionKey = @"WMFLibraryVersion"; +static const NSInteger WMFCurrentLibraryVersion = 14; + +NSString *const MWKDataStoreValidImageSitePrefix = @"//upload.wikimedia.org/"; + +NSString *const WMFCoreDataSynchronizerInfoFileName = @"Wikipedia.info"; + +NSString *const WMFMainContextCrossProcessNotificiationChannelNameKey = @"CrossProcessNotificiationChannelName"; +NSString *const WMFMainContextCrossProcessNotificationChannelNamePrefix = @"org.wikimedia.wikipedia.cd-cpn-"; + +NSString *const WMFCacheContextCrossProcessNotificiationChannelNameKey = @"CacheContextCrossProcessNotificiationChannelName"; +NSString *const WMFCacheContextCrossProcessNotificiationChannelNamePrefix = @"org.wikimedia.wikipedia.cache-cd-cpn-"; + +NSString *MWKCreateImageURLWithPath(NSString *path) { + return [MWKDataStoreValidImageSitePrefix stringByAppendingString:path]; +} + +@interface MWKDataStore () + +@property (nonatomic, strong) WMFSession *session; +@property (nonatomic, strong) WMFConfiguration *configuration; +@property (nonatomic, strong) WMFAuthenticationManager *authenticationManager; + +@property (readwrite, strong, nonatomic) MWKSavedPageList *savedPageList; +@property (readwrite, strong, nonatomic) MWKRecentSearchList *recentSearchList; + +@property (nonatomic, strong) WMFReadingListsController *readingListsController; +@property (nonatomic, strong) WMFExploreFeedContentController *feedContentController; +@property (nonatomic, strong) RemoteNotificationsController *remoteNotificationsController; +@property (nonatomic, strong) WMFArticleSummaryController *articleSummaryController; +@property (nonatomic, strong) MWKLanguageLinkController *languageLinkController; +@property (nonatomic, strong) WMFNotificationsController *notificationsController; + +@property (nonatomic, strong) MobileviewToMobileHTMLConverter *mobileviewConverter; + +@property (readwrite, copy, nonatomic) NSString *basePath; +@property (readwrite, strong, nonatomic) NSCache *articleCache; + +@property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator; +@property (nonatomic, strong) NSManagedObjectContext *viewContext; +@property (nonatomic, strong) NSManagedObjectContext *feedImportContext; + +@property (nonatomic, strong) WMFPermanentCacheController *cacheController; + +@property (nonatomic, strong) WMFCrossProcessCoreDataSynchronizer *librarySynchronizer; +@property (nonatomic, strong) WMFCrossProcessCoreDataSynchronizer *cacheSynchronizer; + +@property (nonatomic, strong) NSURL *containerURL; + +@property (readwrite, nonatomic) RemoteConfigOption remoteConfigsThatFailedUpdate; + +@property (readwrite, strong, nonatomic) WMFABTestsController *abTestsController; + +@end + +@implementation MWKDataStore + ++ (MWKDataStore *)shared { + static dispatch_once_t onceToken; + static MWKDataStore *sharedInstance; + dispatch_once(&onceToken, ^{ + sharedInstance = [self new]; + }); + return sharedInstance; +} + ++ (NSInteger)currentLibraryVersion { + return WMFCurrentLibraryVersion; +} + +#pragma mark - NSObject + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [self stopCoreDataSynchronizers]; +} + +- (instancetype)init { + self = [self initWithContainerURL:[[NSFileManager defaultManager] wmf_containerURL]]; + return self; +} + +- (instancetype)initWithContainerURL:(NSURL *)containerURL { + self = [super init]; + if (self) { + WMFConfiguration *configuration = [WMFConfiguration current]; + WMFSession *session = [[WMFSession alloc] initWithConfiguration:configuration]; + session.authenticationDelegate = self; + WMFAuthenticationManager *authenticationManager = [[WMFAuthenticationManager alloc] initWithSession:session configuration:configuration]; + authenticationManager.delegate = self; + self.session = session; + self.configuration = configuration; + self.authenticationManager = authenticationManager; + self.containerURL = containerURL; + self.basePath = [self.containerURL URLByAppendingPathComponent:@"Data" isDirectory:YES].path; + [self setupLegacyDataStore]; + [self setupCoreDataStackWithContainerURL:containerURL]; + [self setupCoreDataSynchronizersWithContainerURL:containerURL]; + [self startSynchronizingLibraryContexts]; + [self setupHistoryAndSavedPageLists]; + self.languageLinkController = [[MWKLanguageLinkController alloc] initWithManagedObjectContext:self.viewContext]; + self.feedContentController = [[WMFExploreFeedContentController alloc] initWithDataStore:self]; + [self.feedContentController updateContentSources]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarningWithNotification:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + self.remoteNotificationsController = [[RemoteNotificationsController alloc] initWithSession:session configuration:configuration languageLinkController:self.languageLinkController authManager:authenticationManager]; + self.notificationsController = [[WMFNotificationsController alloc] initWithDataStore:self languageLinkController:self.languageLinkController]; + self.articleSummaryController = [[WMFArticleSummaryController alloc] initWithSession:session configuration:configuration dataStore:self]; + self.mobileviewConverter = [[MobileviewToMobileHTMLConverter alloc] init]; + } + return self; +} + +- (void)teardown:(nullable dispatch_block_t)completion { + [self stopCoreDataSynchronizers]; + [self.session teardown]; + if (self.cacheController) { + [self.cacheController teardown:^{ + if (completion) { + completion(); + } + }]; + } else if (completion) { + completion(); + } +} + +/// Generates a globally unique cross process notification channel name with the given prefix +- (NSString *)uniqueCrossProcessNotificationChannelNameWithPrefix:(NSString *)prefix { + NSUUID *uuid = [NSUUID new]; + NSString *uuidString = uuid.UUIDString; + return [NSString stringWithFormat:@"%@%@", prefix, uuidString].lowercaseString; +} + +- (void)setupCoreDataSynchronizersWithContainerURL:(NSURL *)containerURL { + NSURL *infoDictionaryURL = [containerURL URLByAppendingPathComponent:WMFCoreDataSynchronizerInfoFileName isDirectory:NO]; + NSError *unarchiveError = nil; + NSMutableDictionary *infoDictionary = [[self unarchivedDictionaryFromFileURL:infoDictionaryURL error:&unarchiveError] mutableCopy] ?: [NSMutableDictionary new]; + if (unarchiveError) { + DDLogError(@"Error unarchiving shared info dictionary: %@", unarchiveError); + } + + BOOL needsWrite = false; + + // The main cross process notification channel is for the main core data stack (articles, feed, library values, etc) + NSString *key = WMFMainContextCrossProcessNotificiationChannelNameKey; + if (!infoDictionary[key]) { + infoDictionary[key] = [self uniqueCrossProcessNotificationChannelNameWithPrefix:WMFMainContextCrossProcessNotificationChannelNamePrefix]; + needsWrite = YES; + } + self.librarySynchronizer = [[WMFCrossProcessCoreDataSynchronizer alloc] initWithIdentifier:infoDictionary[key] storageDirectory:containerURL]; + + // The cache context cross process notification channel is for the cache's core data stack (cached images, offline article content) + key = WMFCacheContextCrossProcessNotificiationChannelNameKey; + if (!infoDictionary[key]) { + infoDictionary[key] = [self uniqueCrossProcessNotificationChannelNameWithPrefix:WMFCacheContextCrossProcessNotificiationChannelNamePrefix]; + needsWrite = YES; + } + self.cacheSynchronizer = [[WMFCrossProcessCoreDataSynchronizer alloc] initWithIdentifier:infoDictionary[key] storageDirectory:containerURL]; + + if (needsWrite) { + NSError *archiveError = nil; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:infoDictionary requiringSecureCoding:false error:&archiveError]; + if (archiveError) { + DDLogError(@"Error archiving shared info dictionary: %@", archiveError); + } + [data writeToURL:infoDictionaryURL atomically:YES]; + } +} + +- (void)startSynchronizingLibraryContexts { + [self.librarySynchronizer startSynchronizingContexts:@[self.viewContext]]; +} + +- (void)startSynchronizingCacheContext:(NSManagedObjectContext *)moc { + [self.cacheSynchronizer startSynchronizingContexts:@[moc]]; +} + +- (void)stopCoreDataSynchronizers { + [self.librarySynchronizer stop]; + [self.cacheSynchronizer stop]; +} + +- (NSDictionary *)unarchivedDictionaryFromFileURL:(NSURL *)fileURL error:(NSError **)error { + NSData *data = [NSData dataWithContentsOfURL:fileURL]; + NSSet *allowedClasses = [NSSet setWithArray:[NSSecureUnarchiveFromDataTransformer allowedTopLevelClasses]]; + return [NSKeyedUnarchiver unarchivedObjectOfClasses:allowedClasses fromData:data error:error]; +} + +- (void)setupCoreDataStackWithContainerURL:(NSURL *)containerURL { + NSURL *modelURL = [[NSBundle wmf] URLForResource:@"Wikipedia" withExtension:@"momd"]; + NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; + NSString *coreDataDBName = @"Wikipedia.sqlite"; + + NSURL *coreDataDBURL = [containerURL URLByAppendingPathComponent:coreDataDBName isDirectory:NO]; + NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; + NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption: @YES, + NSInferMappingModelAutomaticallyOption: @YES}; + NSError *persistentStoreError = nil; + if (nil == [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:coreDataDBURL options:options error:&persistentStoreError]) { + // TODO: Metrics + DDLogError(@"Error adding persistent store: %@", persistentStoreError); + NSError *moveError = nil; + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *uuid = [[NSUUID UUID] UUIDString]; + NSURL *moveURL = [[containerURL URLByAppendingPathComponent:uuid] URLByAppendingPathExtension:@"sqlite"]; + [fileManager moveItemAtURL:coreDataDBURL toURL:moveURL error:&moveError]; + if (moveError) { + // TODO: Metrics + [fileManager removeItemAtURL:coreDataDBURL error:nil]; + } + persistentStoreError = nil; + if (nil == [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:coreDataDBURL options:options error:&persistentStoreError]) { + // TODO: Metrics + DDLogError(@"Second error after adding persistent store: %@", persistentStoreError); + } + } + + self.persistentStoreCoordinator = persistentStoreCoordinator; + self.viewContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; + self.viewContext.persistentStoreCoordinator = persistentStoreCoordinator; + self.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy; + self.viewContext.automaticallyMergesChangesFromParent = YES; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(managedObjectContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.viewContext]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewContextDidChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:self.viewContext]; +} + +- (void)viewContextDidChange:(NSNotification *)note { + NSDictionary *userInfo = note.userInfo; + NSArray *keys = @[NSInsertedObjectsKey, NSUpdatedObjectsKey, NSDeletedObjectsKey, NSRefreshedObjectsKey, NSInvalidatedObjectsKey]; + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + // NSInvalidatedAllObjectsKey is present when NSManagedObjectContext.reset() is called + if (userInfo[NSInvalidatedAllObjectsKey]) { + [self clearMemoryCache]; + [nc postNotificationName:WMFViewContextDidResetNotification object: nil]; + } + for (NSString *key in keys) { + NSSet *changedObjects = userInfo[key]; + for (NSManagedObject *object in changedObjects) { + if ([object isKindOfClass:[WMFArticle class]]) { + WMFArticle *article = (WMFArticle *)object; + WMFInMemoryURLKey *articleKey = article.inMemoryKey; + NSURL *articleURL = article.URL; + if (!articleKey || !articleURL) { + continue; + } + [self.articleCache removeObjectForKey:articleKey]; + if ([key isEqualToString:NSDeletedObjectsKey]) { // Could change WMFArticleUpdatedNotification to use UserInfo for consistency but want to keep change set minimal at this point + [nc postNotificationName:WMFArticleDeletedNotification object:[note object] userInfo:@{WMFArticleDeletedNotificationUserInfoArticleKeyKey: articleKey}]; + } else { + [nc postNotificationName:WMFArticleUpdatedNotification object:article]; + } + } + } + } +} + +#pragma mark - Background Contexts + +- (void)managedObjectContextDidSave:(NSNotification *)note { + NSManagedObjectContext *moc = note.object; + NSNotificationName notificationName; + if (moc == _viewContext) { + notificationName = WMFViewContextDidSave; + } else if (moc == _feedImportContext) { + notificationName = WMFFeedImportContextDidSave; + } else { + notificationName = WMFBackgroundContextDidSave; + } + [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:note.object userInfo:note.userInfo]; +} + +- (void)performBackgroundCoreDataOperationOnATemporaryContext:(nonnull void (^)(NSManagedObjectContext *moc))mocBlock { + WMFAssertMainThread(@"Background Core Data operations must be started from the main thread."); + NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; + backgroundContext.persistentStoreCoordinator = _persistentStoreCoordinator; + backgroundContext.automaticallyMergesChangesFromParent = YES; + backgroundContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy; + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(managedObjectContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:backgroundContext]; + [backgroundContext performBlock:^{ + mocBlock(backgroundContext); + [nc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:backgroundContext]; + }]; +} + +- (NSManagedObjectContext *)feedImportContext { + WMFAssertMainThread(@"feedImportContext must be created on the main thread"); + if (!_feedImportContext) { + _feedImportContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; + _feedImportContext.persistentStoreCoordinator = _persistentStoreCoordinator; + _feedImportContext.automaticallyMergesChangesFromParent = YES; + _feedImportContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(managedObjectContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:_feedImportContext]; + } + return _feedImportContext; +} + +- (void)teardownFeedImportContext { + WMFAssertMainThread(@"feedImportContext must be torn down on the main thread"); + if (_feedImportContext) { + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:_feedImportContext]; + _feedImportContext = nil; + } +} + +#pragma mark - Migrations + +- (BOOL)migrateToReadingListsInManagedObjectContext:(NSManagedObjectContext *)moc error:(NSError **)migrationError { + ReadingList *defaultReadingList = [moc wmf_fetchOrCreateDefaultReadingList]; + if (!defaultReadingList) { + defaultReadingList = [[ReadingList alloc] initWithContext:moc]; + defaultReadingList.canonicalName = [ReadingList defaultListCanonicalName]; + defaultReadingList.isDefault = YES; + } + + for (ReadingListEntry *entry in defaultReadingList.entries) { + entry.isUpdatedLocally = YES; + } + + if ([moc hasChanges] && ![moc save:migrationError]) { + return NO; + } + + NSFetchRequest *request = [WMFArticle fetchRequest]; + request.fetchLimit = 500; + request.predicate = [NSPredicate predicateWithFormat:@"savedDate != NULL && readingLists.@count == 0", defaultReadingList]; + + NSArray *results = [moc executeFetchRequest:request error:migrationError]; + if (!results) { + return NO; + } + + NSError *addError = nil; + + while (results.count > 0) { + for (WMFArticle *article in results) { + [self.readingListsController addArticleToDefaultReadingList:article error:&addError]; + if (addError) { + break; + } + } + if (addError) { + break; + } + if (![moc save:migrationError]) { + return NO; + } + [moc reset]; + defaultReadingList = [moc wmf_fetchOrCreateDefaultReadingList]; // needs to re-fetch after reset + results = [moc executeFetchRequest:request error:migrationError]; + if (!defaultReadingList || !results) { + return NO; + } + } + if (addError) { + DDLogError(@"Error adding to default reading list: %@", addError); + } else { + [moc wmf_setValue:@(5) forKey:WMFLibraryVersionKey]; + } + + return [moc save:migrationError]; +} + +- (BOOL)migrateMainPageContentGroupInManagedObjectContext:(NSManagedObjectContext *)moc error:(NSError **)migrationError { + NSArray *mainPages = [moc contentGroupsOfKind:WMFContentGroupKindMainPage]; + for (WMFContentGroup *mainPage in mainPages) { + [moc deleteObject:mainPage]; + } + [moc wmf_setValue:@(6) forKey:WMFLibraryVersionKey]; + return [moc save:migrationError]; +} + +- (void)performUpdatesFromLibraryVersion:(NSUInteger)currentLibraryVersion inManagedObjectContext:(NSManagedObjectContext *)moc { + NSError *migrationError = nil; + + if (currentLibraryVersion < 5) { + if (![self migrateToReadingListsInManagedObjectContext:moc error:&migrationError]) { + DDLogError(@"Error during migration: %@", migrationError); + return; + } + } + + if (currentLibraryVersion < 6) { + if (![self migrateMainPageContentGroupInManagedObjectContext:moc error:&migrationError]) { + DDLogError(@"Error during migration: %@", migrationError); + return; + } + } + + if (currentLibraryVersion < 8) { + NSUserDefaults *ud = [[NSUserDefaults alloc] initWithSuiteName:WMFApplicationGroupIdentifier]; + [ud removeObjectForKey:@"WMFOpenArticleURLKey"]; + [ud removeObjectForKey:@"WMFOpenArticleTitleKey"]; + [ud synchronize]; + [moc wmf_setValue:@(8) forKey:WMFLibraryVersionKey]; + if ([moc hasChanges] && ![moc save:&migrationError]) { + DDLogError(@"Error saving during migration: %@", migrationError); + return; + } + } + + if (currentLibraryVersion < 9) { + [self markAllDownloadedArticlesInManagedObjectContextAsNeedingConversionFromMobileview:moc]; + [moc wmf_setValue:@(9) forKey:WMFLibraryVersionKey]; + if ([moc hasChanges] && ![moc save:&migrationError]) { + DDLogError(@"Error saving during migration: %@", migrationError); + return; + } + } + + if (currentLibraryVersion < 10) { + [self migrateToStandardUserDefaults]; + [moc wmf_setValue:@(10) forKey:WMFLibraryVersionKey]; + if ([moc hasChanges] && ![moc save:&migrationError]) { + DDLogError(@"Error saving during migration: %@", migrationError); + return; + } + + if (![self moveImageControllerCacheFolderWithError:&migrationError]) { + DDLogError(@"Error saving during migration: %@", migrationError); + return; + } + } + + if (currentLibraryVersion < 11) { + [MWKLanguageLinkController migratePreferredLanguagesToManagedObjectContext:moc]; + [moc wmf_setValue:@(11) forKey:WMFLibraryVersionKey]; + if ([moc hasChanges] && ![moc save:&migrationError]) { + DDLogError(@"Error saving during migration: %@", migrationError); + return; + } + } + + if (currentLibraryVersion < 12) { + [[WMFEventLoggingService sharedInstance] migrateShareUsageAndInstallIDToUserDefaults]; + [self migrateToLanguageVariantsForLibraryVersion:12 inManagedObjectContext:moc]; + [moc wmf_setValue:@(12) forKey:WMFLibraryVersionKey]; + if ([moc hasChanges] && ![moc save:&migrationError]) { + DDLogError(@"Error saving during migration: %@", migrationError); + return; + } + } + + if (currentLibraryVersion < 13) { + [self.languageLinkController migrateToUniquedPreferredLanguagesInManagedObjectContext:moc]; + [moc wmf_setValue:@(13) forKey:WMFLibraryVersionKey]; + if ([moc hasChanges] && ![moc save:&migrationError]) { + DDLogError(@"Error saving during migration: %@", migrationError); + return; + } + } + + if (currentLibraryVersion < 14) { + [self.remoteNotificationsController deleteLegacyDatabaseFilesAndReturnError:nil]; + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + [moc removeAllContentGroupsOfKind:WMFContentGroupKindNotification]; + userDefaults.wmf_shouldShowNotificationsExploreFeedCard = YES; + [NSHTTPCookieStorage migrateCookiesToSharedStorage]; + [moc wmf_setValue:@(14) forKey:WMFLibraryVersionKey]; + if ([moc hasChanges] && ![moc save:&migrationError]) { + DDLogError(@"Error saving during migration: %@", migrationError); + return; + } + } + + // IMPORTANT: When adding a new library version and migration, update WMFCurrentLibraryVersion to the latest version number +} + +/// Library updates are separate from Core Data migration and can be used to orchestrate migrations that are more complex than automatic Core Data migration allows. +/// They can also be used to perform migrations when the underlying Core Data model has not changed version but the apps' logic has changed in a way that requires data migration. +- (void)performLibraryUpdates:(dispatch_block_t)completion needsMigrateBlock:(dispatch_block_t)needsMigrateBlock { + dispatch_block_t combinedCompletion = ^{ + [WMFPermanentCacheController setupCoreDataStack:^(NSManagedObjectContext *_Nullable moc, NSError *_Nullable error) { + if (error) { + DDLogError(@"Error during cache controller migration: %@", error); + } + WMFPermanentCacheController *permanentCacheController = [[WMFPermanentCacheController alloc] initWithMoc:moc session:self.session configuration:self.configuration preferredLanguageDelegate:self.languageLinkController]; + self.cacheController = permanentCacheController; + [self startSynchronizingCacheContext:moc]; + if (completion) { + completion(); + } + }]; + }; + NSNumber *libraryVersionNumber = [self.viewContext wmf_numberValueForKey:WMFLibraryVersionKey]; + // If the library value doesn't exist, it's a new library and can be set to the latest version + if (!libraryVersionNumber) { + [self performInitialLibrarySetup]; + combinedCompletion(); + return; + } + NSInteger currentUserLibraryVersion = [libraryVersionNumber integerValue]; + // If the library version is >= the current version, we can skip the migration + if (currentUserLibraryVersion >= WMFCurrentLibraryVersion) { + combinedCompletion(); + return; + } + + needsMigrateBlock(); + [self performBackgroundCoreDataOperationOnATemporaryContext:^(NSManagedObjectContext *moc) { + [self performUpdatesFromLibraryVersion:currentUserLibraryVersion inManagedObjectContext:moc]; + combinedCompletion(); + }]; +} + +- (void)performInitialLibrarySetup { + [self.viewContext wmf_fetchOrCreateDefaultReadingList]; + [self.viewContext wmf_setValue:@(WMFCurrentLibraryVersion) forKey:WMFLibraryVersionKey]; + NSError *setupError = nil; + if (![self.viewContext save:&setupError]) { + DDLogError(@"Error performing initial library setup: %@", setupError); + } +} + +#if TEST +- (void)performTestLibrarySetup { + WMFPermanentCacheController *permanentCacheController = [WMFPermanentCacheController testControllerWith:self.containerURL dataStore:self]; + self.cacheController = permanentCacheController; +} +#endif + +- (void)markAllDownloadedArticlesInManagedObjectContextAsNeedingConversionFromMobileview:(NSManagedObjectContext *)moc { + NSFetchRequest *request = [WMFArticle fetchRequest]; + request.predicate = [NSPredicate predicateWithFormat:@"isDownloaded == YES && isConversionFromMobileViewNeeded == NO"]; + request.fetchLimit = 500; + request.propertiesToFetch = @[]; + NSError *fetchError = nil; + NSArray *downloadedArticles = [moc executeFetchRequest:request error:&fetchError]; + if (fetchError) { + DDLogError(@"Error fetching downloaded articles: %@", fetchError); + return; + } + while (downloadedArticles.count > 0) { + @autoreleasepool { + for (WMFArticle *article in downloadedArticles) { + article.isConversionFromMobileViewNeeded = YES; + } + if ([moc hasChanges]) { + NSError *saveError = nil; + [moc save:&saveError]; + if (saveError) { + DDLogError(@"Error saving downloaded articles: %@", fetchError); + return; + } + [moc reset]; + } + } + downloadedArticles = [moc executeFetchRequest:request error:&fetchError]; + if (fetchError) { + DDLogError(@"Error fetching downloaded articles: %@", fetchError); + return; + } + } +} + +- (void)migrateToStandardUserDefaults { + NSUserDefaults *wmfDefaults = [[NSUserDefaults alloc] initWithSuiteName:WMFApplicationGroupIdentifier]; + if (!wmfDefaults) { + return; + } + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + NSDictionary *wmfDefaultsDictionary = [wmfDefaults dictionaryRepresentation]; + NSArray *keys = [wmfDefaultsDictionary allKeys]; + for (NSString *key in keys) { + id value = [wmfDefaultsDictionary objectForKey:key]; + [userDefaults setObject:value forKey:key]; + [wmfDefaults removeObjectForKey:value]; + } +} + +- (BOOL)moveImageControllerCacheFolderWithError:(NSError **)error { + + NSURL *legacyDirectory = [[[NSFileManager defaultManager] wmf_containerURL] URLByAppendingPathComponent:@"Permanent Image Cache" isDirectory:YES]; + NSURL *newDirectory = [[[NSFileManager defaultManager] wmf_containerURL] URLByAppendingPathComponent:@"Permanent Cache" isDirectory:YES]; + + // move legacy image cache to new non-image path name + return [[NSFileManager defaultManager] moveItemAtURL:legacyDirectory toURL:newDirectory error:error]; +} + +- (void)markAllDownloadedArticlesInManagedObjectContextAsUndownloaded:(NSManagedObjectContext *)moc { + NSFetchRequest *request = [WMFArticle fetchRequest]; + request.predicate = [NSPredicate predicateWithFormat:@"isDownloaded == YES"]; + request.fetchLimit = 500; + NSError *fetchError = nil; + NSArray *downloadedArticles = [moc executeFetchRequest:request error:&fetchError]; + if (fetchError) { + DDLogError(@"Error fetching downloaded articles: %@", fetchError); + return; + } + + while (downloadedArticles.count > 0) { + @autoreleasepool { + for (WMFArticle *article in downloadedArticles) { + article.isDownloaded = NO; + } + + if ([moc hasChanges]) { + NSError *saveError = nil; + [moc save:&saveError]; + if (saveError) { + DDLogError(@"Error saving downloaded articles: %@", fetchError); + return; + } + [moc reset]; + } + } + + downloadedArticles = [moc executeFetchRequest:request error:&fetchError]; + if (fetchError) { + DDLogError(@"Error fetching downloaded articles: %@", fetchError); + return; + } + } +} + +#pragma mark - Memory + +- (void)didReceiveMemoryWarningWithNotification:(NSNotification *)note { + [self clearMemoryCache]; +} + +#pragma mark - Accessors + +- (MWKRecentSearchList *)recentSearchList { + if (!_recentSearchList) { + _recentSearchList = [[MWKRecentSearchList alloc] initWithDataStore:self]; + } + return _recentSearchList; +} + +- (nullable NSURL *)primarySiteURL { + return self.languageLinkController.appLanguage.siteURL; +} + +#pragma mark - History and Saved Page List + +- (void)setupHistoryAndSavedPageLists { + WMFAssertMainThread(@"History and saved page lists must be setup on the main thread"); + self.savedPageList = [[MWKSavedPageList alloc] initWithDataStore:self]; + self.readingListsController = [[WMFReadingListsController alloc] initWithDataStore:self]; +} + +#pragma mark - Legacy DataStore + ++ (NSString *)mainDataStorePath { + NSString *documentsFolder = [[NSFileManager defaultManager] wmf_containerPath]; + return [documentsFolder stringByAppendingPathComponent:@"Data"]; +} + ++ (NSString *)appSpecificMainDataStorePath { // deprecated, use the group folder from mainDataStorePath + NSString *documentsFolder = + [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; + return [documentsFolder stringByAppendingPathComponent:@"Data"]; +} + +- (void)setupLegacyDataStore { + NSString *pathToExclude = [self pathForSites]; + NSError *directoryCreationError = nil; + if (![[NSFileManager defaultManager] createDirectoryAtPath:pathToExclude withIntermediateDirectories:YES attributes:nil error:&directoryCreationError]) { + DDLogError(@"Error creating MWKDataStore path: %@", directoryCreationError); + } + NSURL *directoryURL = [NSURL fileURLWithPath:pathToExclude isDirectory:YES]; + NSError *excludeBackupError = nil; + if (![directoryURL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:&excludeBackupError]) { + DDLogError(@"Error excluding MWKDataStore path from backup: %@", excludeBackupError); + } + self.articleCache = [[NSCache alloc] init]; + self.articleCache.countLimit = 1000; +} + +#pragma mark - path methods + +- (NSString *)joinWithBasePath:(NSString *)path { + return [self.basePath stringByAppendingPathComponent:path]; +} + +- (NSString *)pathForSites { + return [self joinWithBasePath:@"sites"]; +} + +- (NSString *)pathForDomainInURL:(NSURL *)url { + NSString *sitesPath = [self pathForSites]; + NSString *domainPath = [sitesPath stringByAppendingPathComponent:url.wmf_domain]; + return [domainPath stringByAppendingPathComponent:url.wmf_languageCode]; +} + +- (NSString *)pathForArticlesInDomainFromURL:(NSURL *)url { + NSString *sitePath = [self pathForDomainInURL:url]; + return [sitePath stringByAppendingPathComponent:@"articles"]; +} + +/// Returns the folder where data for the correspnoding title is stored. +- (NSString *)pathForArticleURL:(NSURL *)url { + NSString *articlesPath = [self pathForArticlesInDomainFromURL:url]; + NSString *encTitle = [self safeFilenameWithString:url.wmf_titleWithUnderscores]; + return [articlesPath stringByAppendingPathComponent:encTitle]; +} + +- (NSString *)safeFilenameWithString:(NSString *)str { + // Escape only % and / with percent style for readability + NSString *encodedStr = [str stringByReplacingOccurrencesOfString:@"%" withString:@"%25"]; + encodedStr = [encodedStr stringByReplacingOccurrencesOfString:@"/" withString:@"%2F"]; + + return encodedStr; +} + +#pragma mark - save methods + +- (BOOL)ensurePathExists:(NSString *)path error:(NSError **)error { + return [[NSFileManager defaultManager] createDirectoryAtPath:path + withIntermediateDirectories:YES + attributes:nil + error:error]; +} + +- (void)ensurePathExists:(NSString *)path { + [self ensurePathExists:path error:NULL]; +} + +- (BOOL)saveData:(NSData *)data toFile:(NSString *)filename atPath:(NSString *)path error:(NSError **)error { + NSAssert([filename length] > 0, @"No file path given for saving data"); + if (!filename) { + return NO; + } + [self ensurePathExists:path error:error]; + NSString *absolutePath = [path stringByAppendingPathComponent:filename]; + return [data writeToFile:absolutePath options:NSDataWritingAtomic error:error]; +} + +- (void)saveData:(NSData *)data path:(NSString *)path name:(NSString *)name { + NSError *error = nil; + [self saveData:data toFile:name atPath:path error:&error]; + NSAssert(error == nil, @"Error saving image to data store: %@", error); +} + +- (BOOL)saveArray:(NSArray *)array path:(NSString *)path name:(NSString *)name error:(NSError **)error { + NSData *data = [NSPropertyListSerialization dataWithPropertyList:array format:NSPropertyListXMLFormat_v1_0 options:0 error:error]; + return [self saveData:data toFile:name atPath:path error:error]; +} + +- (void)saveArray:(NSArray *)array path:(NSString *)path name:(NSString *)name { + [self saveArray:array path:path name:name error:NULL]; +} + +- (BOOL)saveDictionary:(NSDictionary *)dict path:(NSString *)path name:(NSString *)name error:(NSError **)error { + NSData *data = [NSPropertyListSerialization dataWithPropertyList:dict format:NSPropertyListXMLFormat_v1_0 options:0 error:error]; + return [self saveData:data toFile:name atPath:path error:error]; +} + +- (BOOL)saveString:(NSString *)string path:(NSString *)path name:(NSString *)name error:(NSError **)error { + return [self saveData:[string dataUsingEncoding:NSUTF8StringEncoding] toFile:name atPath:path error:error]; +} + +- (BOOL)saveRecentSearchList:(MWKRecentSearchList *)list error:(NSError **)error { + NSString *path = self.basePath; + NSDictionary *export = @{@"entries": [list dataExport]}; + return [self saveDictionary:export path:path name:@"RecentSearches.plist" error:error]; +} + +- (NSArray *)recentSearchListData { + NSString *path = self.basePath; + NSString *filePath = [path stringByAppendingPathComponent:@"RecentSearches.plist"]; + NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath]; + return dict[@"entries"]; +} + +#pragma mark - helper methods + +- (NSInteger)sitesDirectorySize { + NSURL *sitesURL = [NSURL fileURLWithPath:[self pathForSites]]; + return (NSInteger)[[NSFileManager defaultManager] sizeOfDirectoryAt:sitesURL]; +} + +#pragma mark - Deletion + +- (NSError *)removeFolderAtBasePath { + NSError *err; + [[NSFileManager defaultManager] removeItemAtPath:self.basePath error:&err]; + return err; +} + +#pragma mark - Cache + +- (void)prefetchArticles { + NSFetchRequest *request = [WMFArticle fetchRequest]; + request.fetchLimit = 1000; + NSManagedObjectContext *moc = self.viewContext; + NSArray *prefetchedArticles = [moc executeFetchRequest:request error:nil]; + for (WMFArticle *article in prefetchedArticles) { + NSString *key = article.key; + if (!key) { + continue; + } + NSString *variant = article.variant; + WMFInMemoryURLKey *cacheKey = [[WMFInMemoryURLKey alloc] initWithDatabaseKey:key languageVariantCode:variant]; + [self.articleCache setObject:article forKey:cacheKey]; + } +} + +- (void)clearMemoryCache { + @synchronized(self.articleCache) { + [self.articleCache removeAllObjects]; + } +} + +- (void)clearTemporaryCache { + [self clearMemoryCache]; + [self.session clearTemporaryCache]; + NSSet *typesToClear = [NSSet setWithObjects:WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache, nil]; + [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:typesToClear + modifiedSince:[NSDate distantPast] + completionHandler:^{ + }]; +} + +#pragma mark - Remote Configuration + +- (void)updateLocalConfigurationFromRemoteConfigurationWithCompletion:(nullable void (^)(NSError *nullable))completion { + void (^combinedCompletion)(NSError *) = ^(NSError *error) { + if (completion) { + completion(error); + } + }; + + __block NSError *updateError = nil; + WMFTaskGroup *taskGroup = [[WMFTaskGroup alloc] init]; + + // Site info + NSURL *siteURL = [NSURL URLWithString:@"//meta.wikimedia.org"]; // Only the host of the URL is needed + NSURL *URL = [self.configuration mediaWikiAPIURLForURL:siteURL withQueryParameters:WikipediaSiteInfo.defaultRequestParameters]; + [taskGroup enter]; + [self.session getJSONDictionaryFromURL:URL + ignoreCache:YES + completionHandler:^(NSDictionary *_Nullable siteInfo, NSURLResponse *_Nullable response, NSError *_Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (error) { + updateError = error; + [taskGroup leave]; + return; + } + NSDictionary *generalProps = [siteInfo valueForKeyPath:@"query.general"]; + NSDictionary *readingListsConfig = generalProps[@"readinglists-config"]; + if (self.isLocalConfigUpdateAllowed) { + [self updateReadingListsLimits:readingListsConfig]; + self.remoteConfigsThatFailedUpdate &= ~RemoteConfigOptionReadingLists; + } else { + self.remoteConfigsThatFailedUpdate |= RemoteConfigOptionReadingLists; + } + [taskGroup leave]; + }); + }]; + // Remote config + NSURL *remoteConfigURL = [NSURL URLWithString:@"https://meta.wikimedia.org/w/extensions/MobileApp/config/ios.json"]; + [taskGroup enter]; + [self.session getJSONDictionaryFromURL:remoteConfigURL + ignoreCache:YES + completionHandler:^(NSDictionary *_Nullable remoteConfigurationDictionary, NSURLResponse *_Nullable response, NSError *_Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (error) { + updateError = error; + [taskGroup leave]; + return; + } + if (self.isLocalConfigUpdateAllowed) { + [self updateLocalConfigurationFromRemoteConfiguration:remoteConfigurationDictionary]; + self.remoteConfigsThatFailedUpdate &= ~RemoteConfigOptionGeneric; + } else { + self.remoteConfigsThatFailedUpdate |= RemoteConfigOptionGeneric; + } + [taskGroup leave]; + }); + }]; + + [taskGroup waitInBackgroundWithCompletion:^{ + combinedCompletion(updateError); + }]; +} + +- (void)updateLocalConfigurationFromRemoteConfiguration:(NSDictionary *)remoteConfigurationDictionary { + NSNumber *disableReadingListSyncNumber = remoteConfigurationDictionary[@"disableReadingListSync"]; + BOOL shouldDisableReadingListSync = [disableReadingListSyncNumber boolValue]; + self.readingListsController.isSyncRemotelyEnabled = !shouldDisableReadingListSync; +} + +- (void)updateReadingListsLimits:(NSDictionary *)readingListsConfig { + NSNumber *maxEntriesPerList = readingListsConfig[@"maxEntriesPerList"]; + NSNumber *maxListsPerUser = readingListsConfig[@"maxListsPerUser"]; + self.readingListsController.maxEntriesPerList = maxEntriesPerList; + self.readingListsController.maxListsPerUser = [maxListsPerUser intValue]; +} + +#pragma mark - Core Data + +#if DEBUG +- (NSManagedObjectContext *)viewContext { + NSAssert([NSThread isMainThread], @"View context must only be accessed on the main thread"); + return _viewContext; +} +#endif + +- (BOOL)save:(NSError **)error { + if (![self.viewContext hasChanges]) { + return YES; + } + return [self.viewContext save:error]; +} + +- (nullable WMFArticle *)fetchArticleWithURL:(NSURL *)URL inManagedObjectContext:(nonnull NSManagedObjectContext *)moc { + return [self fetchArticleWithKey:URL.wmf_databaseKey variant:URL.wmf_languageVariantCode inManagedObjectContext:moc]; +} + +- (nullable WMFArticle *)fetchArticleWithKey:(NSString *)key variant:(nullable NSString *)variant inManagedObjectContext:(nonnull NSManagedObjectContext *)moc { + WMFArticle *article = nil; + if (moc == _viewContext) { // use ivar to avoid main thread check + WMFInMemoryURLKey *cacheKey = [[WMFInMemoryURLKey alloc] initWithDatabaseKey:key languageVariantCode:variant]; + article = [self.articleCache objectForKey:cacheKey]; + if (article) { + return article; + } + } + article = [moc fetchArticleWithKey:key variant:variant]; + if (article && moc == _viewContext) { // use ivar to avoid main thread check + WMFInMemoryURLKey *cacheKey = [[WMFInMemoryURLKey alloc] initWithDatabaseKey:key languageVariantCode:variant]; + [self.articleCache setObject:article forKey:cacheKey]; + } + return article; +} + +- (nullable WMFArticle *)fetchArticleWithWikidataID:(NSString *)wikidataID { + return [_viewContext fetchArticleWithWikidataID:wikidataID]; +} + +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(NSURL *)URL inManagedObjectContext:(NSManagedObjectContext *)moc { + NSString *language = URL.wmf_languageCode; + NSString *title = URL.wmf_title; + NSString *key = URL.wmf_databaseKey; + if (!language || !title || !key) { + return nil; + } + NSString *variant = URL.wmf_languageVariantCode; + WMFArticle *article = [self fetchArticleWithKey:key variant:variant inManagedObjectContext:moc]; + if (!article) { + article = [moc createArticleWithKey:key variant:variant]; + article.displayTitleHTML = article.displayTitle; + if (moc == self.viewContext) { + WMFInMemoryURLKey *cacheKey = [[WMFInMemoryURLKey alloc] initWithDatabaseKey:key languageVariantCode:variant]; + [self.articleCache setObject:article forKey:cacheKey]; + } + } + return article; +} + +- (nullable WMFArticle *)fetchArticleWithURL:(NSURL *)URL { + return [self fetchArticleWithKey:URL.wmf_databaseKey variant:URL.wmf_languageVariantCode]; +} + +- (nullable WMFArticle *)fetchArticleWithKey:(NSString *)key { + return [self fetchArticleWithKey:key variant:nil]; +} + +- (nullable WMFArticle *)fetchArticleWithKey:(NSString *)key variant:(nullable NSString *)variant { + WMFAssertMainThread(@"Article fetch must be performed on the main thread."); + return [self fetchArticleWithKey:key variant:variant inManagedObjectContext:self.viewContext]; +} + +- (nullable WMFArticle *)fetchOrCreateArticleWithURL:(NSURL *)URL { + WMFAssertMainThread(@"Article fetch must be performed on the main thread."); + return [self fetchOrCreateArticleWithURL:URL inManagedObjectContext:self.viewContext]; +} + +#pragma mark - WMFAuthenticationManagerDelegate + +- (nullable NSURL *)loginSiteURL { + return self.primarySiteURL; +} + +- (void)authenticationManagerWillLogOutWithCompletionHandler:(void (^)(void))completionHandler { + [self.notificationsController authenticationManagerWillLogOut:completionHandler]; +} + +- (void)authenticationManagerDidLogin { + [self clearMemoryCache]; +} + +- (void)authenticationManagerDidReset { + [self clearMemoryCache]; + [self.readingListsController setSyncEnabled:NO shouldDeleteLocalLists:NO shouldDeleteRemoteLists:NO]; +} + +#pragma mark - WMFSessionAuthenticationDelegate + +- (void)attemptReauthentication { + [self.authenticationManager attemptLoginWithLogoutOnFailureInitiatedBy:LogoutInitiatorServer + completion:^{ + }]; +} + +- (void)deauthenticate { + [self.authenticationManager logoutInitiatedBy:LogoutInitiatorServer + completion:^{ + }]; +} + +#pragma mark - ABTestsManaging + +- (void)setupAbTestsControllerWithPersistenceService:(id)persistenceService { + self.abTestsController = [[WMFABTestsController alloc] initWithPersistanceService:persistenceService]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKDataStoreList.h b/Apps/Wikipedia/Wikipedia/Code/MWKDataStoreList.h new file mode 100644 index 0000000..4409e0a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKDataStoreList.h @@ -0,0 +1,18 @@ +@class MWKDataStore; + +@protocol MWKDataStoreList + +/** + * Initialize the receiver from a data store. + * + * The receiver should read any data which resides in the store, and subsequent saves should update it. + * + * @param dataStore The store to read data from. + * + * @return A new list. + */ +- (instancetype)initWithDataStore:(MWKDataStore *)dataStore; + +@property (nonatomic, weak, readonly) MWKDataStore *dataStore; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKImageInfo.h b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfo.h new file mode 100644 index 0000000..49b9999 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfo.h @@ -0,0 +1,62 @@ +#import +@import CoreGraphics; +@class MWKLicense; + +NS_ASSUME_NONNULL_BEGIN +@interface MWKImageInfo : MWKDataObject + +/// Name of the canonical file associated with this image, in the format @c "File:Some_file_name.extension". +@property (nullable, nonatomic, readonly, copy) NSString *canonicalPageTitle; + +/// Name of the file associated with this image, without the "File:" prefix; +- (nullable NSString *)canonicalPageName; + +/// URL pointing at the canonical file associated with this gallery item, e.g. @c "//site/.../Some_file_name.extension". +/// @warning These images can be *very* large. Special handling may be needed when downloading or displaying. +@property (nullable, nonatomic, readonly, copy) NSURL *canonicalFileURL; + +/// Short description of the image contents (e.g. "John Smith posing for a picture"). +@property (nullable, nonatomic, readonly, copy) NSString *imageDescription; + +@property (nonatomic, assign, readonly) BOOL imageDescriptionIsRTL; + +@property (nullable, nonatomic, readonly, strong) MWKLicense *license; + +/// URL pointing to the corresponding file page for the receiver. +@property (nullable, nonatomic, readonly, copy) NSURL *filePageURL; + +/// URL pointing at a thumbnail version of the image at @c imageURL. +@property (nullable, nonatomic, readonly, copy) NSURL *imageThumbURL; + +/// Size of the original image. +@property (nonatomic, readonly, assign) CGSize imageSize; + +/// Size of the thumbnail at @c imageThumbURL. +@property (nonatomic, readonly, assign) CGSize thumbSize; + +/// Name of the entity owning this image. +@property (nullable, nonatomic, readonly, copy) NSString *owner; + +/// Value which can be used to associate the receiver with a @c MWKImage. +@property (nullable, nonatomic, readonly, strong) id imageAssociationValue; +NS_ASSUME_NONNULL_END + +/// Factory method for creating an instance from the output of @c exportData. ++ (nullable instancetype)imageInfoWithExportedData:(nullable NSDictionary *)exportedData; + +NS_ASSUME_NONNULL_BEGIN +- (instancetype)initWithCanonicalPageTitle:(nullable NSString *)canonicalPageTitle + canonicalFileURL:(nullable NSURL *)canonicalFileURL + imageDescription:(nullable NSString *)imageDescription + imageDescriptionIsRTL:(BOOL)imageDescriptionIsRTL + license:(nullable MWKLicense *)license + filePageURL:(nullable NSURL *)filePageURL + imageThumbURL:(nullable NSURL *)imageThumbURL + owner:(nullable NSString *)owner + imageSize:(CGSize)imageSize + thumbSize:(CGSize)thumbSize; + +- (nullable NSURL *)imageURLForTargetWidth:(NSInteger)width; + +@end +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKImageInfo.m b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfo.m new file mode 100644 index 0000000..0d7af49 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfo.m @@ -0,0 +1,177 @@ +#import +#import +#import +@import UIKit; +#import +#import +#import + +// !!!: don't change key constants w/o writing conversion code to pull values from the old keys +// Model Version 1.0.0 +NSString *const mWKImageInfoModelVersionKey = @"modelVersion"; +NSUInteger const MWKImageInfoModelVersion_1 = 1; + +NSString *const MWKImageInfoCanonicalPageTitleKey = @"canonicalPageTitle"; +NSString *const MWKImageInfoCanonicalFileURLKey = @"canonicalFileURL"; +NSString *const MWKImageInfoImageDescriptionKey = @"imageDescription"; +NSString *const MWKImageInfoImageDescriptionIsRTLKey = @"imageDescriptionIsRTL"; +NSString *const MWKImageInfoFilePageURLKey = @"filePageURL"; +NSString *const MWKImageInfoImageThumbURLKey = @"imageThumbURL"; +NSString *const MWKImageInfoOwnerKey = @"owner"; +NSString *const MWKImageInfoLicenseKey = @"license"; +NSString *const MWKImageInfoImageSize = @"imageSize"; +NSString *const MWKImageInfoThumbSize = @"thumbSize"; + +@interface MWKImageInfo () + +@property (nonatomic, readwrite, copy) NSString *canonicalPageTitle; +@property (nonatomic, readwrite, copy) NSURL *canonicalFileURL; +@property (nonatomic, readwrite, copy) NSString *imageDescription; +@property (nonatomic, assign, readwrite) BOOL imageDescriptionIsRTL; +@property (nonatomic, readwrite, strong) MWKLicense *license; +@property (nonatomic, readwrite, copy) NSURL *filePageURL; +@property (nonatomic, readwrite, copy) NSURL *imageThumbURL; +@property (nonatomic, readwrite, assign) CGSize imageSize; +@property (nonatomic, readwrite, assign) CGSize thumbSize; +@property (nonatomic, readwrite, copy) NSString *owner; +@property (nonatomic, readwrite, strong) id imageAssociationValue; + +@end + +@implementation MWKImageInfo + +- (instancetype)initWithCanonicalPageTitle:(NSString *)canonicalPageTitle + canonicalFileURL:(NSURL *)canonicalFileURL + imageDescription:(NSString *)imageDescription + imageDescriptionIsRTL:(BOOL)imageDescriptionIsRTL + license:(MWKLicense *)license + filePageURL:(NSURL *)filePageURL + imageThumbURL:(NSURL *)imageThumbURL + owner:(NSString *)owner + imageSize:(CGSize)imageSize + thumbSize:(CGSize)thumbSize { + // !!!: not sure what's guaranteed by the API + // NSParameterAssert(canonicalPageTitle.length); + // NSParameterAssert(canonicalFileURL.absoluteString.length); + self = [super init]; + if (self) { + self.canonicalPageTitle = canonicalPageTitle; + self.imageDescription = imageDescription; + self.imageDescriptionIsRTL = imageDescriptionIsRTL; + self.owner = owner; + self.license = license; + self.canonicalFileURL = canonicalFileURL; + self.filePageURL = filePageURL; + self.imageThumbURL = imageThumbURL; + self.imageSize = imageSize; + self.thumbSize = thumbSize; + } + return self; +} + ++ (instancetype)imageInfoWithExportedData:(NSDictionary *)exportedData { + if (!exportedData) { + return nil; + } + // assume all model versions are 1.0.0 + BOOL isRTL = NO; + id isRTLNumber = exportedData[MWKImageInfoImageDescriptionIsRTLKey]; + if (isRTLNumber && [isRTLNumber isKindOfClass:[NSNumber class]]) { + isRTL = [isRTLNumber boolValue]; + } + return [[MWKImageInfo alloc] + initWithCanonicalPageTitle:exportedData[MWKImageInfoCanonicalPageTitleKey] + canonicalFileURL:[NSURL URLWithString:exportedData[MWKImageInfoCanonicalFileURLKey]] + imageDescription:exportedData[MWKImageInfoImageDescriptionKey] + imageDescriptionIsRTL:isRTL + license:[MWKLicense licenseWithExportedData:exportedData[MWKImageInfoLicenseKey]] + filePageURL:[NSURL URLWithString:exportedData[MWKImageInfoFilePageURLKey]] + imageThumbURL:[NSURL URLWithString:exportedData[MWKImageInfoImageThumbURLKey]] + owner:exportedData[MWKImageInfoOwnerKey] + imageSize:CGSizeFromString(exportedData[MWKImageInfoImageSize]) + thumbSize:CGSizeFromString(exportedData[MWKImageInfoThumbSize])]; +} + +- (NSDictionary *)dataExport { + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:6]; + + dict[mWKImageInfoModelVersionKey] = @(MWKImageInfoModelVersion_1); + + [dict setValue:self.canonicalPageTitle forKey:MWKImageInfoCanonicalPageTitleKey]; + [dict setValue:self.canonicalFileURL.absoluteString forKey:MWKImageInfoCanonicalFileURLKey]; + [dict setValue:self.imageDescription forKey:MWKImageInfoImageDescriptionKey]; + [dict setValue:self.filePageURL.absoluteString forKey:MWKImageInfoFilePageURLKey]; + [dict setValue:self.imageThumbURL.absoluteString forKey:MWKImageInfoImageThumbURLKey]; + [dict setValue:self.owner forKey:MWKImageInfoOwnerKey]; + + [dict setValue:[self.license dataExport] forKey:MWKImageInfoLicenseKey]; + + dict[MWKImageInfoImageSize] = NSStringFromCGSize(self.imageSize); + dict[MWKImageInfoThumbSize] = NSStringFromCGSize(self.thumbSize); + + return [dict copy]; +} + +- (nullable NSURL *)imageURLForTargetWidth:(NSInteger)targetWidth { + NSInteger width = (NSInteger)self.imageSize.width; + if (width <= 0) { + return self.imageThumbURL; + } + + BOOL isCanonicalFileSvg = [self.canonicalFileURL.pathExtension isEqualToString:@"svg"]; + if (width <= targetWidth && !isCanonicalFileSvg) { + return self.canonicalFileURL; + } + + NSString *source = self.canonicalFileURL.absoluteString; + + NSString *scaledImageURLString = WMFChangeImageSourceURLSizePrefix(source, targetWidth); + if (!scaledImageURLString) { + return self.imageThumbURL; + } + + NSURL *scaledImageURL = [NSURL URLWithString:scaledImageURLString]; + if (!scaledImageURL) { + return self.imageThumbURL; + } + + return scaledImageURL; +} + +- (BOOL)isEqual:(id)obj { + if (obj == self) { + return YES; + } else if ([obj isKindOfClass:[MWKImageInfo class]]) { + return [self isEqualToImageInfo:obj]; + } else { + return NO; + } +} + +- (BOOL)isEqualToImageInfo:(MWKImageInfo *)other { + return WMF_EQUAL(self.canonicalPageTitle, isEqualToString:, other.canonicalPageTitle) && WMF_IS_EQUAL(self.canonicalFileURL, other.canonicalFileURL) && WMF_EQUAL(self.imageDescription, isEqualToString:, other.imageDescription) && WMF_EQUAL(self.license, isEqualToLicense:, other.license) && WMF_IS_EQUAL(self.filePageURL, other.filePageURL) && WMF_IS_EQUAL(self.imageThumbURL, other.imageThumbURL) && WMF_EQUAL(self.owner, isEqualToString:, other.owner) && CGSizeEqualToSize(self.imageSize, other.imageSize) && CGSizeEqualToSize(self.thumbSize, other.thumbSize); +} + +- (NSUInteger)hash { + return self.canonicalPageTitle.hash ^ flipBitsWithAdditionalRotation(self.canonicalFileURL.hash, 1); +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %@ %@", + [super description], self.canonicalPageTitle, self.canonicalFileURL]; +} + +#pragma mark - Calculated properties + +- (id)imageAssociationValue { + if (!_imageAssociationValue) { + _imageAssociationValue = WMFParseImageNameFromSourceURL(self.canonicalFileURL); + } + return _imageAssociationValue; +} + +- (NSString *)canonicalPageName { + return [self.canonicalPageTitle wmf_safeSubstringFromIndex:@"File:".length]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher+PicOfTheDayInfo.h b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher+PicOfTheDayInfo.h new file mode 100644 index 0000000..cb06321 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher+PicOfTheDayInfo.h @@ -0,0 +1,21 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MWKImageInfoFetcher (PicOfTheDayInfo) + +/** + * Fetch one @c MWKImageInfo object for the given date's Commons Picture of the Day. + * + * The info must be fetched individually, since there's no reliable way to correlate the responses with their matching + * date. + * + * @param date The dates to fetch POTD info for. + * @param metadataLanguage The language metadata should be request in. Defaults to current locale's language code if @c nil. + * + */ +- (void)fetchPicOfTheDayGalleryInfoForDate:(NSDate *)date metadataLanguage:(nullable NSString *)metadataLanguage failure:(WMFErrorHandler)failure success:(WMFSuccessIdHandler)success; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher+PicOfTheDayInfo.m b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher+PicOfTheDayInfo.m new file mode 100644 index 0000000..6416948 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher+PicOfTheDayInfo.m @@ -0,0 +1,87 @@ +#import "MWKImageInfoFetcher+PicOfTheDayInfo.h" +#import "NSDate+WMFPOTDTitle.h" +@import WMF; + +NS_ASSUME_NONNULL_BEGIN + +static NSString *const MWKPOTDImageInfoErrorDomain = @"MWKPOTDImageInfoErrorDomain"; + +typedef NS_ENUM(NSInteger, MWKPOTDImageInfoErrorCode) { + MWKPOTDImageInfoErrorCodeEmptyInfo +}; + +@interface NSError (MWKPOTDImageInfoErrorDomain) + ++ (instancetype)wmf_emptyPOTDErrorForDate:(NSDate *)date; + +@end + +typedef id (^MWKImageInfoResolve)(id _Nullable); + +static MWKImageInfoResolve selectFirstImageInfo(NSDate *date) { + return ^id(NSArray *infoObjects) { + if (infoObjects.count < 1) { + return [NSError wmf_emptyPOTDErrorForDate:date]; + } else { + return infoObjects.firstObject; + } + }; +} + +static MWKImageInfoResolve addPictureOfTheDayToDescriptionForDate(NSDate *date) { + return ^id(MWKImageInfo *info) { + NSString *dateString = [[NSDateFormatter wmf_mediumDateFormatterWithoutTime] stringFromDate:date]; + NSMutableString *potdDescription = + [[NSString localizedStringWithFormat:WMFLocalizedStringWithDefaultValue(@"potd-description-prefix", nil, nil, @"Picture of the day for %1$@", @"Prefix to picture of the day description which states it was the picture of the day for a specific date. The %1$@ token is subtituted for the date."), dateString] mutableCopy]; + if (info.imageDescription.length) { + [potdDescription appendString:@"\n\n"]; + [potdDescription appendString:info.imageDescription]; + } + return [[MWKImageInfo alloc] initWithCanonicalPageTitle:info.canonicalPageTitle + canonicalFileURL:info.canonicalFileURL + imageDescription:potdDescription + imageDescriptionIsRTL:info.imageDescriptionIsRTL + license:info.license + filePageURL:info.filePageURL + imageThumbURL:info.imageThumbURL + owner:info.owner + imageSize:info.imageSize + thumbSize:info.thumbSize]; + }; +} + +@implementation MWKImageInfoFetcher (PicOfTheDayInfo) + +- (void)fetchPicOfTheDayGalleryInfoForDate:(NSDate *)date + metadataLanguage:(nullable NSString *)metadataLanguage + failure:(WMFErrorHandler)failure + success:(WMFSuccessIdHandler)success { + [self fetchGalleryInfoForImagesOnPages:@[[date wmf_picOfTheDayPageTitle]] + fromSiteURL:[NSURL wmf_wikimediaCommonsURL] + metadataLanguage:metadataLanguage + failure:failure + success:^(id _Nonnull object) { + if ([selectFirstImageInfo(date)(object) isKindOfClass:[NSError class]]) { + failure(selectFirstImageInfo(date)(object)); + } else { + success(addPictureOfTheDayToDescriptionForDate(date)(selectFirstImageInfo(date)(object))); + } + }]; +} + +@end + +@implementation NSError (MWKPOTDImageInfoErrorDomain) + ++ (instancetype)wmf_emptyPOTDErrorForDate:(NSDate *)date { + return [[NSError alloc] initWithDomain:MWKPOTDImageInfoErrorDomain + code:MWKPOTDImageInfoErrorCodeEmptyInfo + userInfo:@{ + NSLocalizedDescriptionKey: + [NSString localizedStringWithFormat:WMFLocalizedStringWithDefaultValue(@"potd-empty-error-description", nil, nil, @"Failed to retrieve picture of the day for %1$@", @"Error message when app fails to download Commons Picture of the Day. %1$@ will be substitued with the date which the app attempted to retrieve."), [[NSDateFormatter wmf_mediumDateFormatterWithoutTime] stringFromDate:date]] + }]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher.h b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher.h new file mode 100644 index 0000000..4ce1344 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher.h @@ -0,0 +1,66 @@ +@import Foundation; +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class NSURLSessionDataTask; +@class MWKDataStore; + +@protocol MWKImageInfoRequest + +- (void)cancel; + +@end + +@interface MWKImageInfoFetcher : WMFLegacyFetcher + +- (instancetype)initWithDataStore:(MWKDataStore *)dataStore; + +/** + * Fetch the imageinfo for the given image page titles. + * @param imageTitles One or more page titles to get imageinfo for (e.g. @c "File:My_Image_Title.jpg). + * @param site A site object for the MW site to target. + * @return An operation which can be used to set success and failure handling or cancel the originating request. + */ +- (id)fetchGalleryInfoForImageFiles:(NSArray *)imageTitles + fromSiteURL:(NSURL *)siteURL + success:(void (^)(NSArray *infoObjects))success + failure:(void (^)(NSError *error))failure; + +- (void)fetchGalleryInfoForImage:(NSString *)canonicalPageTitle fromSiteURL:(NSURL *)siteURL failure:(WMFErrorHandler)failure success:(WMFSuccessIdHandler)success; + +/** + * Fetch @c MWKImageInfo populated with enough suitable for display in a modal gallery. + * + * Used in contexts where image URL, description, artist, etc. are needed. + * + * @param pageTitles One or more page titles to retrieve images from, and then fetch info for. + * @param site A site object for the MW site to target. + * @param metadataLanguage The langauge to attempt to retrieve image metadata in. Falls back to English if the specified + * langauge isn't available. Defaults to the current locale's language if @c nil. + * + * @param failure is passed an error on failure + * @param success is passed @c MWKImageInfo containing info the images found on the specified pages. + */ + +- (void)fetchGalleryInfoForImagesOnPages:(NSArray *)pageTitles + fromSiteURL:(NSURL *)siteURL + metadataLanguage:(NSString *)metadataLanguage + failure:(WMFErrorHandler)failure + success:(WMFSuccessIdHandler)success; + +- (void)fetchImageInfoForCommonsFiles:(NSArray *)filenames + failure:(WMFErrorHandler)failure + success:(WMFSuccessIdHandler)success; + +- (nullable NSURL *)galleryInfoURLForImageTitles: (NSArray *)imageTitles fromSiteURL: (NSURL *)siteURL; + +- (nullable NSURLRequest *)urlRequestForFromURL: (NSURL *)url; + +@property (weak, nonatomic) id preferredLanguageDelegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher.m b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher.m new file mode 100644 index 0000000..c3f1456 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKImageInfoFetcher.m @@ -0,0 +1,273 @@ +#import +#import +#import +#import + +/// Required extmetadata keys, don't forget to add new ones to +requiredExtMetadataKeys! +static NSString *const ExtMetadataImageDescriptionKey = @"ImageDescription"; +static NSString *const ExtMetadataArtistKey = @"Artist"; +static NSString *const ExtMetadataLicenseUrlKey = @"LicenseUrl"; +static NSString *const ExtMetadataLicenseShortNameKey = @"LicenseShortName"; +static NSString *const ExtMetadataLicenseKey = @"License"; + +static CGSize MWKImageInfoSizeFromJSON(NSDictionary *json, NSString *widthKey, NSString *heightKey) { + NSNumber *width = json[widthKey]; + NSNumber *height = json[heightKey]; + if (width && height) { + // both NSNumber & NSString respond to `floatValue` + return CGSizeMake([width floatValue], [height floatValue]); + } else { + return CGSizeZero; + } +} + +@implementation MWKImageInfoFetcher + +- (id)initWithDataStore:(MWKDataStore *)dataStore { + self = [super initWithSession:dataStore.session configuration:dataStore.configuration]; + if (self) { + self.preferredLanguageDelegate = dataStore.languageLinkController; + } + return self; +} + +- (void)fetchGalleryInfoForImage:(NSString *)canonicalPageTitle fromSiteURL:(NSURL *)siteURL failure:(WMFErrorHandler)failure success:(WMFSuccessIdHandler)success { + [self fetchGalleryInfoForImageFiles:@[canonicalPageTitle] + fromSiteURL:siteURL + success:^(NSArray *infoObjects) { + success(infoObjects.firstObject); + } + failure:failure]; +} + +- (void)fetchGalleryInfoForImagesOnPages:(NSArray *)pageTitles + fromSiteURL:(NSURL *)siteURL + metadataLanguage:(NSString *)metadataLanguage + failure:(WMFErrorHandler)failure + success:(WMFSuccessIdHandler)success { + [self fetchInfoForTitles:pageTitles + fromSiteURL:siteURL + thumbnailWidth:[NSNumber numberWithInteger:[[UIScreen mainScreen] wmf_articleImageWidthForScale]] + extmetadataKeys:[MWKImageInfoFetcher galleryExtMetadataKeys] + metadataLanguage:metadataLanguage + useGenerator:YES + success:success + failure:failure]; +} + +- (id)fetchGalleryInfoForImageFiles:(NSArray *)imageTitles + fromSiteURL:(NSURL *)siteURL + success:(void (^)(NSArray *infoObjects))success + failure:(void (^)(NSError *error))failure { + return [self fetchInfoForTitles:imageTitles + fromSiteURL:siteURL + thumbnailWidth:[NSNumber numberWithInteger:[[UIScreen mainScreen] wmf_articleImageWidthForScale]] + extmetadataKeys:[MWKImageInfoFetcher galleryExtMetadataKeys] + metadataLanguage:siteURL.wmf_languageCode + useGenerator:NO + success:success + failure:failure]; +} + ++ (NSArray *)galleryExtMetadataKeys { + return @[ExtMetadataLicenseKey, + ExtMetadataLicenseUrlKey, + ExtMetadataLicenseShortNameKey, + ExtMetadataImageDescriptionKey, + ExtMetadataArtistKey]; +} + +- (id)responseObjectForJSON:(NSDictionary *)json preferredLanguageCodes:(NSArray *)preferredLanguageCodes error:(NSError *__autoreleasing *)error { + if (!json) { + if (error) { + *error = [WMFFetcher invalidParametersError]; + } + return nil; + } + NSDictionary *indexedImages = json[@"query"][@"pages"]; + + if (!indexedImages) { + return @[]; + } + + NSMutableArray *itemListBuilder = [NSMutableArray arrayWithCapacity:[[indexedImages allKeys] count]]; + + [indexedImages enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSDictionary *image, BOOL *stop) { + NSDictionary *imageInfo = [image[@"imageinfo"] firstObject]; + NSDictionary *extMetadata = imageInfo[@"extmetadata"]; + // !!!: workaround for a nasty bug in JSON serialization in the back-end + if (![extMetadata isKindOfClass:[NSDictionary class]]) { + extMetadata = nil; + } + MWKLicense *license = + [[MWKLicense alloc] initWithCode:extMetadata[ExtMetadataLicenseKey][@"value"] + shortDescription:extMetadata[ExtMetadataLicenseShortNameKey][@"value"] + URL:[NSURL wmf_optionalURLWithString:extMetadata[ExtMetadataLicenseUrlKey][@"value"]]]; + + NSString *description = nil; + BOOL descriptionIsRTL = NO; + NSString *descriptionLangCode = nil; + + id imageDescriptionValue = extMetadata[ExtMetadataImageDescriptionKey][@"value"]; + if ([imageDescriptionValue isKindOfClass:[NSDictionary class]]) { + NSDictionary *availableDescriptionsByLangCode = imageDescriptionValue; + descriptionLangCode = [self descriptionLangCodeToUseFromAvailableDescriptionsByLangCode:availableDescriptionsByLangCode forPreferredLangCodes:preferredLanguageCodes]; + if (descriptionLangCode) { + // The image description service returns languages but not language variant codes. + // The language variant in the request is currenty not taken into account in the response. + // So using the language code is correct in this case. + description = availableDescriptionsByLangCode[descriptionLangCode]; + descriptionIsRTL = [MWKLanguageLinkController isLanguageRTLForContentLanguageCode:descriptionLangCode]; + } + } else if ([imageDescriptionValue isKindOfClass:[NSString class]]) { + description = imageDescriptionValue; + } + + NSString *artist = nil; + id artistValue = extMetadata[ExtMetadataArtistKey][@"value"]; + if ([artistValue isKindOfClass:[NSDictionary class]]) { + artist = artistValue[descriptionLangCode]; + } else if ([artistValue isKindOfClass:[NSString class]]) { + artist = artistValue; + } + + MWKImageInfo *item = + [[MWKImageInfo alloc] + initWithCanonicalPageTitle:image[@"title"] + canonicalFileURL:[NSURL wmf_optionalURLWithString:imageInfo[@"url"]] + imageDescription:[[description wmf_stringByRemovingHTML] wmf_getCollapsedWhitespaceStringAdjustedForTerminalPunctuation] + imageDescriptionIsRTL:descriptionIsRTL + license:license + filePageURL:[NSURL wmf_optionalURLWithString:imageInfo[@"descriptionurl"]] + imageThumbURL:[NSURL wmf_optionalURLWithString:imageInfo[@"thumburl"]] + owner:[[artist wmf_stringByRemovingHTML] wmf_getCollapsedWhitespaceStringAdjustedForTerminalPunctuation] + imageSize:MWKImageInfoSizeFromJSON(imageInfo, @"width", @"height") + thumbSize:MWKImageInfoSizeFromJSON(imageInfo, @"thumbwidth", @"thumbheight")]; + [itemListBuilder addObject:item]; + }]; + return itemListBuilder; +} + +- (NSString *)descriptionLangCodeToUseFromAvailableDescriptionsByLangCode:(NSDictionary *)availableDescriptionsByLangCode forPreferredLangCodes:(NSArray *)preferredLangCodes { + // use first of user's preferred lang codes for which we have a translation + for (NSString *langCode in preferredLangCodes) { + if ([availableDescriptionsByLangCode objectForKey:langCode]) { + return langCode; + } + } + // else use "en" description if available + if ([availableDescriptionsByLangCode objectForKey:@"en"]) { + return @"en"; + } + // else use first description lang code + for (NSString *langCode in availableDescriptionsByLangCode.allKeys) { + if (![langCode hasPrefix:@"_"]) { // there's a weird "_type" key in the results for some reason + return langCode; + } + } + // else no luck + return nil; +} + +- (nullable NSURL *)galleryInfoURLForImageTitles: (NSArray *)imageTitles + fromSiteURL: (NSURL *)siteURL { + + NSDictionary *params = [self queryParametersForTitles:imageTitles fromSiteURL:siteURL thumbnailWidth:[NSNumber numberWithInteger:[[UIScreen mainScreen] wmf_articleImageWidthForScale]] extmetadataKeys:[MWKImageInfoFetcher galleryExtMetadataKeys] metadataLanguage:siteURL.wmf_languageCode useGenerator:NO]; + + if (siteURL.host) { + return [self.configuration mediaWikiAPIURLForURL:siteURL withQueryParameters:params]; + } + + return nil; +} + +- (NSDictionary *)queryParametersForTitles:(NSArray *)titles + fromSiteURL:(NSURL *)siteURL + thumbnailWidth:(NSNumber *)thumbnailWidth + extmetadataKeys:(NSArray *)extMetadataKeys +metadataLanguage:(nullable NSString *)metadataLanguage + useGenerator:(BOOL)useGenerator { + if (!titles) { + return @{}; + } + + NSMutableDictionary *params = + [@{@"format": @"json", + @"action": @"query", + @"titles": [titles componentsJoinedByString:@"|"], + // suppress continue warning + @"rawcontinue": @"", + @"prop": @"imageinfo", + @"iiprop": [@[@"url", @"extmetadata", @"dimensions"] componentsJoinedByString:@"|"], + @"iiextmetadatafilter": [extMetadataKeys ?: @[] componentsJoinedByString:@"|"], + @"iiextmetadatamultilang": @1, + @"iiurlwidth": thumbnailWidth} mutableCopy]; + + if (useGenerator) { + params[@"generator"] = @"images"; + } + + if (metadataLanguage) { + params[@"iiextmetadatalanguage"] = metadataLanguage; + } + + return [params copy]; +} + +- (nullable NSURLRequest *)urlRequestForFromURL: (NSURL *)url { + + return [self.session imageInfoURLRequestFromPersistenceWith: url]; +} + +- (id)fetchInfoForTitles:(NSArray *)titles + fromSiteURL:(NSURL *)siteURL + thumbnailWidth:(NSNumber *)thumbnailWidth + extmetadataKeys:(NSArray *)extMetadataKeys + metadataLanguage:(nullable NSString *)metadataLanguage + useGenerator:(BOOL)useGenerator + success:(void (^)(NSArray *))success + failure:(void (^)(NSError *))failure { + NSParameterAssert([titles count]); + NSAssert([titles count] <= 50, @"Only 50 titles can be queried at a time."); + NSParameterAssert(siteURL); + + NSDictionary *params = [self queryParametersForTitles:titles fromSiteURL:siteURL thumbnailWidth:thumbnailWidth extmetadataKeys:extMetadataKeys metadataLanguage:metadataLanguage useGenerator:useGenerator]; + + NSURL *url = [self.fetcher.configuration mediaWikiAPIURLForURL:siteURL withQueryParameters:params]; + + NSURLRequest *urlRequest = [self urlRequestForFromURL:url]; + + return (id)[self performMediaWikiAPIGETForURLRequest:urlRequest completionHandler:^(NSDictionary *_Nullable result, NSHTTPURLResponse *_Nullable response, NSError *_Nullable error) { + if (error) { + failure(error); + return; + } + [self getPreferredContentLanguageCodes:^(NSArray *preferredLanguageCodes) { + NSError *serializerError = nil; + NSArray *galleryItems = [self responseObjectForJSON:result preferredLanguageCodes:preferredLanguageCodes error:&serializerError]; + if (serializerError) { + failure(serializerError); + return; + } + success(galleryItems); + }]; + }]; +} + +- (void)getPreferredContentLanguageCodes:(void (^)(NSArray *))completion { + if (!self.preferredLanguageDelegate) { + NSAssert(false, @"Preferred language delegate should be set"); + completion(@[@"en"]); + return; + } + [self.preferredLanguageDelegate getPreferredContentLanguageCodes:completion]; +} + +- (void)fetchImageInfoForCommonsFiles:(NSArray *)filenames + failure:(WMFErrorHandler)failure + success:(WMFSuccessIdHandler)success { + NSURL *commonsURL = [self.configuration commonsAPIURLComponentsWithQueryParameters:nil].URL; + [self fetchGalleryInfoForImageFiles:filenames fromSiteURL:commonsURL success:success failure:failure]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLanguageFilter.h b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageFilter.h new file mode 100644 index 0000000..b4e1482 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageFilter.h @@ -0,0 +1,63 @@ +@import Foundation; + +@class MWKLanguageLink; + +NS_ASSUME_NONNULL_BEGIN + +// Notification sent by an MWKLanguageFilterDataSource when language array values change +extern NSString *const MWKLanguageFilterDataSourceLanguagesDidChangeNotification; + +@protocol MWKLanguageFilterDataSource + +@property (readonly, copy, nonatomic) NSArray *allLanguages; +@property (readonly, copy, nonatomic) NSArray *preferredLanguages; +@property (readonly, copy, nonatomic) NSArray *otherLanguages; + +@end + +@interface MWKLanguageFilter : NSObject + +- (instancetype)initWithLanguageDataSource:(id)dataSource; + +@property (nonatomic, strong, readonly) id dataSource; + +/** + * String used to filter languages by their @c languageCode or @c languageName. + * + * Setting this property to @c nil will disable filtering. + * + * @return The string to filter by, or @c nil if disabled. + */ +@property (copy, nullable, nonatomic) NSString *languageFilter; + +/** + * Returns all languages of the languageController. If the data source preferred languages contains one or more + * language variants, the variants for those languages will appear first in the array. + * + * Note that this property is currently only used in the 'Add Langugages' configuration of WMFLanguagesViewController. + * That view controller requires this particular sorting scheme. + * + * If additional clients in the future require a different sorting scheme, a per-instance configuration property + * that specifies a sorting style would probably be the best approach. + * + * The languages returned by this property will be filtered if @c languageFilter is non-nil. + */ +@property (nonatomic, copy, readonly) NSArray *filteredLanguages; + +/** + * The user's preferred languages. + * + * The languages returned by this property will be filtered if @c languageFilter is non-nil. + */ +@property (nonatomic, copy, readonly) NSArray *filteredPreferredLanguages; + +/** + * All the languages in the languageController minus @c filteredPreferredLanguages. + * + * The languages returned by this property will be filtered if @c languageFilter is non-nil. + */ +@property (nonatomic, copy, readonly) NSArray *filteredOtherLanguages; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLanguageFilter.m b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageFilter.m new file mode 100644 index 0000000..24ab06c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageFilter.m @@ -0,0 +1,127 @@ +#import +#import +#import +#import +#import + +NSString *const MWKLanguageFilterDataSourceLanguagesDidChangeNotification = @"MWKLanguageFilterDataSourceLanguagesDidChangeNotification"; + +@interface MWKLanguageFilter () + +@property (nonatomic, strong, readwrite) id dataSource; +@property (nonatomic, copy, readwrite) NSArray *filteredLanguages; +@property (nonatomic, copy, readwrite) NSArray *filteredPreferredLanguages; +@property (nonatomic, copy, readwrite) NSArray *filteredOtherLanguages; + +@end + +/* Note that multiple MWKLanguageFilter instances can be active at the same time. + * For instance, when adding a language in settings, the WMFPreferredLanguagesViewController + * and the language-choosing WMFLanguagesViewController each have an instance of MWKLanguageFilter. + * Multiple active instances need to be notified of changes in the data source, + * so a notification is used instead of a delegate. +*/ +@implementation MWKLanguageFilter + +- (instancetype)initWithLanguageDataSource:(id)dataSource { + self = [super init]; + if (self) { + self.dataSource = dataSource; + [self updateFilteredLanguages]; + } + return self; +} + +- (void)dealloc { + self.dataSource = nil; +} + +- (void)setDataSource:(id)dataSource { + if (_dataSource == dataSource) { + return; + } + if (_dataSource) { + [[NSNotificationCenter defaultCenter] removeObserver:self name:MWKLanguageFilterDataSourceLanguagesDidChangeNotification object:_dataSource]; + } + _dataSource = dataSource; + if (_dataSource) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataSourceLanguagesDidChange:) name:MWKLanguageFilterDataSourceLanguagesDidChangeNotification object:_dataSource]; + } +} + +- (void)setLanguageFilter:(NSString *__nullable)filterString { + if (WMF_EQUAL(self.languageFilter, isEqualToString:, filterString)) { + return; + } + _languageFilter = [filterString copy]; + [self updateFilteredLanguages]; +} + +/* This method will sort the provided array of languages in the following way: + * If the data source preferred languages contains a language variant, + * all variants for that language are moved to the beginning of resulting array. + * + * If language variants of multiple languages are present in the data source preferred langugages, + * all variants for each language are moved to the beginning of the resulting array in the same + * relative order as the variants appear in the original array. + */ +- (NSArray *)languagesSortedWithPreferredLanguageVariantLanguagesFirst:(NSArray *)unsortedLanguages { + + NSMutableArray *temporaryLanguages = [unsortedLanguages mutableCopy]; + + // Gather all the preferred languages that support variants + // Maintain the preference order so variants are presented using same order + NSMutableArray *preferredVariantAwareLanguageCodes = [NSMutableArray array]; + for (MWKLanguageLink *language in self.dataSource.preferredLanguages) { + if (language.languageVariantCode && ![preferredVariantAwareLanguageCodes containsObject:language.languageCode]) { + [preferredVariantAwareLanguageCodes addObject:language.languageCode]; + } + } + + // Process language codes in reverse order so the earlier items are placed earlier in the resulting array + for (NSString *languageCode in preferredVariantAwareLanguageCodes.reverseObjectEnumerator) { + NSIndexSet *foundIndexes = [temporaryLanguages indexesOfObjectsPassingTest:^BOOL(MWKLanguageLink * _Nonnull language, NSUInteger idx, BOOL * _Nonnull stop) { + return [language.languageCode isEqualToString: languageCode]; + }]; + NSArray *foundLanguages = [temporaryLanguages objectsAtIndexes:foundIndexes]; + [temporaryLanguages removeObjectsAtIndexes:foundIndexes]; + [temporaryLanguages insertObjects:foundLanguages atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, foundLanguages.count)]]; + } + + return [temporaryLanguages copy]; +} + +- (void)updateFilteredLanguages { + NSArray *unsortedFilteredLanguages = nil; + if ([self.languageFilter length] == 0) { + unsortedFilteredLanguages = self.dataSource.allLanguages; + self.filteredPreferredLanguages = self.dataSource.preferredLanguages; + self.filteredOtherLanguages = self.dataSource.otherLanguages; + } else { + unsortedFilteredLanguages = [self.dataSource.allLanguages wmf_select:^BOOL(MWKLanguageLink *langLink) { + return [self langLinkMatchesFilter:langLink]; + }]; + self.filteredPreferredLanguages = [self.dataSource.preferredLanguages wmf_select:^BOOL(MWKLanguageLink *langLink) { + return [self langLinkMatchesFilter:langLink]; + }]; + self.filteredOtherLanguages = [self.dataSource.otherLanguages wmf_select:^BOOL(MWKLanguageLink *langLink) { + return [self langLinkMatchesFilter:langLink]; + }]; + } + + self.filteredLanguages = [self languagesSortedWithPreferredLanguageVariantLanguagesFirst:unsortedFilteredLanguages]; +} + +- (BOOL)langLinkMatchesFilter:(MWKLanguageLink *) langLink { + return [langLink.name wmf_caseInsensitiveContainsString:self.languageFilter] || + [langLink.localizedName wmf_caseInsensitiveContainsString:self.languageFilter] || + [langLink.languageCode wmf_caseInsensitiveContainsString:self.languageFilter] || + // Farsi/Perisan hack: To fix https://phabricator.wikimedia.org/T107530, explicitly checking for Farsi in search box. + ([@"Farsi" wmf_caseInsensitiveContainsString:self.languageFilter] && [langLink.languageCode wmf_isEqualToStringIgnoringCase:@"fa"]); +} + +- (void)dataSourceLanguagesDidChange:(NSNotification *)note { + [self updateFilteredLanguages]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLink.h b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLink.h new file mode 100644 index 0000000..ad9ef3a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLink.h @@ -0,0 +1,59 @@ +@import Foundation; + +NS_ASSUME_NONNULL_BEGIN + +@interface MWKLanguageLink : NSObject + +/// Language code for the site where @c pageTitleText is located. +@property (readonly, copy, nonatomic, nonnull) NSString *languageCode; + +/// Title text for the page linked to by the receiver. +@property (readonly, copy, nonatomic, nonnull) NSString *pageTitleText; + +/// User-readable name for @c languageCode in the language specified in the current device language. +@property (readonly, copy, nonatomic, nonnull) NSString *localizedName; + +/// User-readable name for @c languageCode in the language specified by @c languageCode. +@property (readonly, copy, nonatomic, nonnull) NSString *name; + +/// If representing a language variant, the language variant code. Otherwise nil. +@property (readonly, copy, nonatomic, nullable) NSString *languageVariantCode; + +/// Alternative ISO code that is useful in some instances where the languageCode lookup fails. So far this is only set and in use for Norwegian (Bokmål) ("no" languageCode, "nb" altISOCode) +@property (readonly, copy, nonatomic, nullable) NSString *altISOCode; + +- (instancetype)initWithLanguageCode:(nonnull NSString *)languageCode + pageTitleText:(nonnull NSString *)pageTitleText + name:(nonnull NSString *)name + localizedName:(nonnull NSString *)localizedName + languageVariantCode:(nullable NSString *)languageVariantCode + altISOCode:(nullable NSString *)altISOCode NS_DESIGNATED_INITIALIZER; + +/// +/// @name Comparison +/// + +- (BOOL)isEqualToLanguageLink:(MWKLanguageLink *)rhs; + +/// Comparison is based on @c contentLanguageCode +- (NSComparisonResult)compare:(MWKLanguageLink *)other; + +/// +/// @name Computed Properties +/// + +/// Returns @c languageVariantCode if non-nil and non-empty string, @c lanagugeCode otherwise +@property (readonly, copy, nonatomic, nonnull) NSString *contentLanguageCode; + +/// A url with the default Wikipedia domain and the receiver's @c languageCode. The receiver's @c languageVariantCode is set as the URL's wmf_languageVariantCode. +@property (readonly, copy, nonatomic, nonnull) NSURL *siteURL; + +/// A url whose domain & path are derived from the receiver's @c languageCode and @c pageTitleText. The receiver's @c languageVariantCode is set as the URL's wmf_languageVariantCode. +@property (readonly, copy, nonatomic, nonnull) NSURL *articleURL; + +/// Returns a MWKLanguageLink with the provided pageTitleText, with all other properties the same as the receiver. +- (MWKLanguageLink *)languageLinkWithPageTitleText:(NSString *)pageTitleText; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLink.m b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLink.m new file mode 100644 index 0000000..1e9d2cf --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLink.m @@ -0,0 +1,95 @@ +#import +#import +#import +#import + +@interface MWKLanguageLink () + +@property (readwrite, copy, nonatomic, nonnull) NSString *languageCode; +@property (readwrite, copy, nonatomic, nonnull) NSString *pageTitleText; +@property (readwrite, copy, nonatomic, nonnull) NSString *localizedName; +@property (readwrite, copy, nonatomic, nonnull) NSString *name; +@property (readwrite, copy, nonatomic, nullable) NSString *languageVariantCode; +@property (readwrite, copy, nonatomic, nullable) NSString *altISOCode; + +@end + +NS_ASSUME_NONNULL_BEGIN + +@implementation MWKLanguageLink + +- (instancetype)initWithLanguageCode:(nonnull NSString *)languageCode + pageTitleText:(nonnull NSString *)pageTitleText + name:(nonnull NSString *)name + localizedName:(nonnull NSString *)localizedName + languageVariantCode:(nullable NSString *)languageVariantCode + altISOCode:(nullable NSString *)altISOCode { + self = [super init]; + if (self) { + self.languageCode = languageCode; + self.pageTitleText = pageTitleText; + self.name = name; + self.localizedName = localizedName; + self.languageVariantCode = languageVariantCode; + self.altISOCode = altISOCode; + } + return self; +} + +WMF_SYNTHESIZE_IS_EQUAL(MWKLanguageLink, isEqualToLanguageLink:) + +- (BOOL)isEqualToLanguageLink:(MWKLanguageLink *)rhs { + return WMF_RHS_PROP_EQUAL(languageCode, isEqualToString:) && WMF_RHS_PROP_EQUAL(pageTitleText, isEqualToString:) && WMF_RHS_PROP_EQUAL(name, isEqualToString:) && WMF_RHS_PROP_EQUAL(localizedName, isEqualToString:) && WMF_RHS_PROP_EQUAL(languageVariantCode, isEqualToString:) && WMF_RHS_PROP_EQUAL(altISOCode, isEqualToString:); +} + +- (NSComparisonResult)compare:(MWKLanguageLink *)other { + return [self.contentLanguageCode compare:other.contentLanguageCode]; +} + +- (NSUInteger)hash { + return self.languageCode.hash ^ flipBitsWithAdditionalRotation(self.pageTitleText.hash, 1) ^ flipBitsWithAdditionalRotation(self.name.hash, 2) ^ flipBitsWithAdditionalRotation(self.localizedName.hash, 3) ^ flipBitsWithAdditionalRotation(self.languageVariantCode.hash, 4) ^ flipBitsWithAdditionalRotation(self.altISOCode.hash, 4); // When languageVariantCode or altISOCode is nil, the XOR flips the bits +} + +- (NSString *)description { + return [NSString stringWithFormat: + @"%@ { \n" + "\tlanguageCode: %@, \n" + "\tlanguageVariantCode: %@, \n" + "\taltISOCode: %@, \n" + "\tpageTitleText: %@, \n" + "\tname: %@, \n" + "\tlocalizedName: %@ \n" + "}", + [super description], self.languageCode, self.languageVariantCode, self.altISOCode, self.pageTitleText, self.name, self.localizedName]; +} + +#pragma mark - Computed Properties + +- (NSString *)contentLanguageCode { + return (self.languageVariantCode == nil || [self.languageVariantCode isEqualToString:@""]) ? [self.languageCode copy] : [self.languageVariantCode copy]; +} + +- (NSURL *)siteURL { + NSURL *siteURL = [NSURL wmf_URLWithDefaultSiteAndLanguageCode:self.languageCode]; + siteURL.wmf_languageVariantCode = self.languageVariantCode; + return siteURL; +} + +- (NSURL *)articleURL { + NSURL *articleURL = [[self siteURL] wmf_URLWithTitle:self.pageTitleText]; + articleURL.wmf_languageVariantCode = self.languageVariantCode; + return articleURL; +} + +- (MWKLanguageLink *)languageLinkWithPageTitleText:(NSString *)pageTitleText { + return [[MWKLanguageLink alloc] initWithLanguageCode:self.languageCode + pageTitleText:pageTitleText + name:self.name + localizedName:self.localizedName + languageVariantCode:self.languageVariantCode + altISOCode:self.altISOCode]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkController.h b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkController.h new file mode 100644 index 0000000..3d7ad1e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkController.h @@ -0,0 +1,149 @@ +#import +#import +@import UIKit.UIView; +@class NSManagedObjectContext; +@class MWKLanguageLink; + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const WMFPreferredLanguagesDidChangeNotification; + +extern NSString *const WMFAppLanguageDidChangeNotification; + +// User info keys for WMFPreferredLanguagesDidChangeNotification +extern NSString *const WMFPreferredLanguagesLastChangedLanguageKey; // An MWKLanguageLink instance +extern NSString *const WMFPreferredLanguagesChangeTypeKey; // An NSNumber containing a WMFPreferredLanguagesChangeType value + +typedef NS_ENUM(NSInteger, WMFPreferredLanguagesChangeType) { + WMFPreferredLanguagesChangeTypeAdd = 1, + WMFPreferredLanguagesChangeTypeRemove, + WMFPreferredLanguagesChangeTypeReorder +}; + +@interface MWKLanguageLinkController : NSObject + +/// Initializes `MWKLanguageLinkController` with the `NSManagedObjectContext` used for storage +- (instancetype)initWithManagedObjectContext:(NSManagedObjectContext *)moc; + +/** + * Returns all languages of the receiver, sorted by name, minus unsupported languages. + */ +@property (readonly, copy, nonatomic) NSArray *allLanguages; + +/** + * Returns the user's 1st preferred language - used as the "App Language". + */ +@property (readonly, copy, nonatomic, nullable) MWKLanguageLink *appLanguage; + +/** + * Returns the user's preferred languages. + */ +@property (readonly, copy, nonatomic) NSArray *preferredLanguages; + +/** + * Returns the user's preferred site URLs. + */ +@property (readonly, copy, nonatomic) NSArray *preferredSiteURLs; + +/** + * All the languages in the receiver minus @c preferredLanguages. + */ +@property (readonly, copy, nonatomic) NSArray *otherLanguages; + +/** + * Uniquely appends a new preferred language. The new language will be the last preferred language. + * + * @param language the language to append + */ +- (void)appendPreferredLanguage:(MWKLanguageLink *)language; + +/** + * Reorders a preferred language to the index given. The language must already exist in a user's preferred languages + * + * @param language the language to reorder + * @param newIndex the new index of the langage + */ +- (void)reorderPreferredLanguage:(MWKLanguageLink *)language toIndex:(NSInteger)newIndex; + +/** + * Removes a preferred language. + * + * @param language the language to remove + */ +- (void)removePreferredLanguage:(MWKLanguageLink *)language; + +/** + * Given a language code, return the preferred language variant code if the language supports variants. + * Returns nil for languages without variants. + * This first looks for the preferred variant in the receiver's preferredLanguages. + * If none is found, for the preferred variant based on the OS language settings. + * If none is found, uses a default language variant for that language. + * Returns nil for a nil language code. + * @param languageCode the language code to find the language variant code for + * @return The preferred language variant code for if the language supports variants + */ +- (nullable NSString *)preferredLanguageVariantCodeForLanguageCode:(nullable NSString *)languageCode; + +/** + * Given an ISO language code, returns the correct Wikipedia language code for use in the app. + * + * Used when creating article language links to use the correct Wikipedia language code for + * use within the app when the returned ISO language code is different. + * + * For example, the language links service returns 'nb' as the language code for Norwegian. + * Within the app and in querying Norwegian Wikipedia, the code 'no' should be used. + * + * For languages with an altISOCode value, returns the languageCode for the corresopnding altISOCode. + * For all other values returns the same value passed in without validating if it is a valid ISO language code. + * Returns nil for a nil ISO language code. + * @param isoLanguageCode an ISO language code + * @return The Wikipedia languageCode for the ISO language code. Usually the same value except for languages with an altISOCode. + */ ++ (nullable NSString *)languageCodeForISOLanguageCode:(nullable NSString *)isoLanguageCode; + ++ (void)migratePreferredLanguagesToManagedObjectContext:(NSManagedObjectContext *)moc; + +/// The expected dictionary uses language codes as the key with the value being the desired language variant code for that language. +- (void)migratePreferredLanguagesToLanguageVariants:(NSDictionary *)languageMapping inManagedObjectContext:(NSManagedObjectContext *)moc; + +/// Removes duplicate language codes from the saved preferred language codes. If there are duplicate codes, all duplicate codes after the first found are removed. +/// If the number of codes changes after uniquing, saves the new array of codes. +/// Although generally useful, this is added specifically to address the case of duplicate codes for Norwegian being introduced. https://phabricator.wikimedia.org/T281378 +- (void)migrateToUniquedPreferredLanguagesInManagedObjectContext:(NSManagedObjectContext *)moc; + +@end + +/// This category is specific to processing MWKLanguageLink instances that represent articles +@interface MWKLanguageLinkController (ArticleLanguageLinkVariants) + +/// Given an article URL and an array of language links for that article in different languages, this method does the following: +/// +/// - If any of the provided article language links is of a language that supports variants, the single language element is replaced by an article language link for each language variant +/// e.g. If Chinese 'zh' is in the array, it will be replaced by article language links for Chinese, Simplified; Chinese, Traditional; Malaysian Simplified; etc. +/// This allows users to view the corresponding article in a particular variant. +/// +/// - If the provided articleURL has a language variant, an article language link for the remaining language variants is appended to the returned array +/// e.g. If the displayed article is shown in Serbian, Cyrillic an article language link for the remaining variant for that language; Serbian, Latin; will be added to the array. +/// This allows users to choose to view the currently displayed article in a different variant of the same language. +- (NSArray *)articleLanguageLinksWithVariantsFromArticleURL:(NSURL *)articleURL articleLanguageLinks:(NSArray *)articleLanguageLinks; + +@end + +/// Methods to provide layout direction information for language codes. +@interface MWKLanguageLinkController (LayoutDirectionAdditions) + +/// Returns whether the language represented by the @c contentLanguageCode displays right-to-left. +/// Returns NO if @c contentLangaugeCode is nil. ++ (BOOL)isLanguageRTLForContentLanguageCode:(nullable NSString *)contentLanguageCode; + +/// Returns either "rtl" or "ltr" depending whether the language represented by the @c contentLanguageCode displays right-to-left. +/// Returns "ltr" if @c contentLangaugeCode is nil. ++ (NSString *)layoutDirectionForContentLanguageCode:(nullable NSString *)contentLanguageCode; + +/// Returns the semantic content attribute to force appropriate text direction for the language represented by @c contentLanguageCode. +/// Returns UISemanticContentAttributeUnspecified if @c contentLangaugeCode is nil. ++ (UISemanticContentAttribute)semanticContentAttributeForContentLanguageCode:(nullable NSString *)contentLanguageCode; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkController.m b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkController.m new file mode 100644 index 0000000..500c9c1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkController.m @@ -0,0 +1,397 @@ +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +NSString *const WMFPreferredLanguagesDidChangeNotification = @"WMFPreferredLanguagesDidChangeNotification"; + +NSString *const WMFAppLanguageDidChangeNotification = @"WMFAppLanguageDidChangeNotification"; + +NSString *const WMFPreferredLanguagesChangeTypeKey = @"WMFPreferredLanguagesChangeTypeKey"; +NSString *const WMFPreferredLanguagesLastChangedLanguageKey = @"WMFPreferredLanguagesLastChangedLanguageKey"; + +static NSString *const WMFPreviousLanguagesKey = @"WMFPreviousSelectedLanguagesKey"; + +@interface MWKLanguageLinkController () + +@property (weak, nonatomic) NSManagedObjectContext *moc; +@property (nullable, copy, nonatomic) NSArray *cachedPreferredLanguages; + +@end + +@implementation MWKLanguageLinkController + +- (instancetype)initWithManagedObjectContext:(NSManagedObjectContext *)moc { + if (self = [super init]) { + self.moc = moc; + self.cachedPreferredLanguages = nil; + } + return self; +} + +#pragma mark - Getters & Setters + ++ (NSDictionary *> *)allLanguageVariantsBySiteLanguageCode { + /// Separate static dictionary here so the dictionary is only bridged once + static dispatch_once_t onceToken; + static NSDictionary *> *allLanguageVariantsBySiteLanguageCode; + dispatch_once(&onceToken, ^{ + allLanguageVariantsBySiteLanguageCode = WikipediaLookup.allLanguageVariantsByWikipediaLanguageCode; + }); + return allLanguageVariantsBySiteLanguageCode; +} + ++ (NSArray *)allLanguages { + /// Separate static array here so the array is only bridged once and variant substitution happens once + static dispatch_once_t onceToken; + static NSArray *allLanguages; + dispatch_once(&onceToken, ^{ + allLanguages = [MWKLanguageLinkController languagesReplacingSiteLanguagesWithVariants:WikipediaLookup.allLanguageLinks]; + }); + return allLanguages; +} + +/// Since MWKLanguageLink instances represent a language choice in the user-interface, language variants must be included. +/// The autogenerated all languages list and the list of languges for an article both include only site languages, not variants. +/// This method takes an array of site language links and replaces the site language with the language variants for sites with variants. ++ (NSArray *)languagesReplacingSiteLanguagesWithVariants:(NSArray *)languages { + NSMutableArray *tempResult = [[NSMutableArray alloc] init]; + for (MWKLanguageLink *language in languages) { + NSAssert((language.languageVariantCode == nil && ![language.languageVariantCode isEqualToString:@""]), @"The method %s should only be called with MWKLanguageLink objects with a nil or empty-string languageVariantCode", __PRETTY_FUNCTION__); + NSArray *variants = [MWKLanguageLinkController allLanguageVariantsBySiteLanguageCode][language.languageCode]; + if (variants) { + [tempResult addObjectsFromArray:variants]; + } else { + [tempResult addObject:language]; + } + } + return [tempResult copy]; +} + +- (NSArray *)allLanguages { + return [MWKLanguageLinkController allLanguages]; +} + +- (nullable NSString *)preferredLanguageVariantCodeForLanguageCode:(nullable NSString *)languageCode { + if (!languageCode) { + return nil; + } + + // Find first with matching language code in app preferred languages + MWKLanguageLink *matchingLanguageLink = [self.preferredLanguages wmf_match:^BOOL(MWKLanguageLink *obj) { + return [obj.languageCode isEqual:languageCode]; + }]; + + // If the matching link does not have a language variant code, the language does not have variants. Return nil. + if (matchingLanguageLink) { + return matchingLanguageLink.languageVariantCode ?: nil; + } + + // If not found in the app's preferred languages, get the best guess from the user's OS settings. + return [NSLocale wmf_bestLanguageVariantCodeForLanguageCode:languageCode]; +} + ++ (nullable NSString *)languageCodeForISOLanguageCode:(nullable NSString *)isoLanguageCode { + // Map altISOCodes to languageCodes once for fast lookups + static dispatch_once_t onceToken; + static NSDictionary *languageCodesByAltISOCode; + dispatch_once(&onceToken, ^{ + NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] init]; + for (MWKLanguageLink *language in self.allLanguages) { + if (language.altISOCode) { + tempDict[language.altISOCode] = language.languageCode; + } + } + languageCodesByAltISOCode = [tempDict copy]; + }); + if (!isoLanguageCode) { + return nil; + } + + NSString *languageCode = languageCodesByAltISOCode[isoLanguageCode]; + return languageCode ? : isoLanguageCode; // If no alternative ISO code, use the original value +} + +- (nullable MWKLanguageLink *)appLanguage { + return [self.preferredLanguages firstObject]; +} + +- (NSArray *)preferredLanguages { + // Without caching, every call does a database fetch and lookup through allLanguages + // even though the array contents change only when user updates preferred language settings + if (!self.cachedPreferredLanguages) { + NSArray *preferredLanguageCodes = [self readPreferredLanguageCodes]; + self.cachedPreferredLanguages = [preferredLanguageCodes wmf_mapAndRejectNil:^id(NSString *isoLanguageCode) { + NSString *contentLanguageCode = [MWKLanguageLinkController languageCodeForISOLanguageCode:isoLanguageCode]; + return [self.allLanguages wmf_match:^BOOL(MWKLanguageLink *languageLink) { + return [languageLink.contentLanguageCode isEqualToString:contentLanguageCode]; + }]; + }]; + } + return self.cachedPreferredLanguages; +} + +- (NSArray *)preferredSiteURLs { + return [[self preferredLanguages] wmf_mapAndRejectNil:^NSURL *_Nullable(MWKLanguageLink *_Nonnull obj) { + return [obj siteURL]; + }]; +} + +- (NSArray *)otherLanguages { + return [self.allLanguages wmf_select:^BOOL(MWKLanguageLink *langLink) { + return ![self.preferredLanguages containsObject:langLink]; + }]; +} + +#pragma mark - Preferred Language Management + +- (void)appendPreferredLanguage:(MWKLanguageLink *)language { + NSParameterAssert(language); + NSMutableArray *langCodes = [[self readPreferredLanguageCodes] mutableCopy]; + [langCodes removeObject:language.contentLanguageCode]; + [langCodes addObject:language.contentLanguageCode]; + [self savePreferredLanguageCodes:langCodes changeType:WMFPreferredLanguagesChangeTypeAdd changedLanguage:language]; +} + +- (void)reorderPreferredLanguage:(MWKLanguageLink *)language toIndex:(NSInteger)newIndex { + NSMutableArray *langCodes = [[self readPreferredLanguageCodes] mutableCopy]; + NSAssert(newIndex < (NSInteger)[langCodes count], @"new language index is out of range"); + if (newIndex >= (NSInteger)[langCodes count]) { + return; + } + NSInteger oldIndex = (NSInteger)[langCodes indexOfObject:language.contentLanguageCode]; + NSAssert(oldIndex != NSNotFound, @"Language is not a preferred language"); + if (oldIndex == NSNotFound) { + return; + } + [langCodes removeObject:language.contentLanguageCode]; + [langCodes insertObject:language.contentLanguageCode atIndex:(NSUInteger)newIndex]; + [self savePreferredLanguageCodes:langCodes changeType:WMFPreferredLanguagesChangeTypeReorder changedLanguage:language]; +} + +- (void)removePreferredLanguage:(MWKLanguageLink *)language { + NSMutableArray *langCodes = [[self readPreferredLanguageCodes] mutableCopy]; + [langCodes removeObject:language.contentLanguageCode]; + [self savePreferredLanguageCodes:langCodes changeType:WMFPreferredLanguagesChangeTypeRemove changedLanguage:language]; +} + +#pragma mark - Reading/Saving Preferred Language Codes + +- (NSArray *)readSavedPreferredLanguageCodes { + return [self readSavedPreferredLanguageCodesInManagedObjectContext:self.moc]; +} + +- (NSArray *)readSavedPreferredLanguageCodesInManagedObjectContext:(NSManagedObjectContext *)moc { + __block NSArray *preferredLanguages = nil; + [moc performBlockAndWait:^{ + preferredLanguages = [moc wmf_arrayValueForKey:WMFPreviousLanguagesKey] ?: @[]; + }]; + return preferredLanguages; +} + +- (NSArray *)readPreferredLanguageCodes { + NSMutableArray *preferredLanguages = [[self readSavedPreferredLanguageCodes] mutableCopy]; + + if (preferredLanguages.count == 0) { + NSArray *osLanguages = NSLocale.wmf_preferredWikipediaLanguageCodes; + [osLanguages enumerateObjectsWithOptions:0 + usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { + if (![preferredLanguages containsObject:obj]) { + [preferredLanguages addObject:obj]; + } + }]; + } + return [preferredLanguages wmf_reject:^BOOL(id obj) { + return [obj isEqual:[NSNull null]]; + }]; +} + +- (void)savePreferredLanguageCodes:(NSArray *)languageCodes changeType:(WMFPreferredLanguagesChangeType)changeType changedLanguage:(MWKLanguageLink *)changedLanguage { + [self savePreferredLanguageCodes:languageCodes changeType:changeType changedLanguage:changedLanguage inManagedObjectContext:self.moc]; +} + +- (void)savePreferredLanguageCodes:(NSArray *)languageCodes changeType:(WMFPreferredLanguagesChangeType)changeType changedLanguage:(nullable MWKLanguageLink *)changedLanguage inManagedObjectContext:(NSManagedObjectContext *)moc { + NSString *previousAppContentLanguageCode = self.appLanguage.contentLanguageCode; + [moc performBlockAndWait:^{ + [moc wmf_setValue:languageCodes forKey:WMFPreviousLanguagesKey]; + NSError *preferredLanguageCodeSaveError = nil; + if (![moc save:&preferredLanguageCodeSaveError]) { + DDLogError(@"Error saving preferred languages: %@", preferredLanguageCodeSaveError); + } + }]; + + // Even when supressing external notifications during language variant migration, + // the internal cache always needs to be refreshed when new preferred language codes are saved. + self.cachedPreferredLanguages = nil; + + // Send notifications only if there is a change type and changed language + // Used to avoid sending notifications during language variant migration + if (changeType && changedLanguage) { + [[NSNotificationCenter defaultCenter] postNotificationName:MWKLanguageFilterDataSourceLanguagesDidChangeNotification object:self]; + NSDictionary *userInfo = @{WMFPreferredLanguagesChangeTypeKey: @(changeType), WMFPreferredLanguagesLastChangedLanguageKey: changedLanguage}; + [[NSNotificationCenter defaultCenter] postNotificationName:WMFPreferredLanguagesDidChangeNotification object:self userInfo:userInfo]; + if (self.appLanguage.contentLanguageCode && ![self.appLanguage.contentLanguageCode isEqualToString:previousAppContentLanguageCode]) { + [[NSNotificationCenter defaultCenter] postNotificationName:WMFAppLanguageDidChangeNotification object:self]; + } + } +} + +// Reminder: "resetPreferredLanguages" is for testing only! +- (void)resetPreferredLanguages { + [self.moc performBlockAndWait:^{ + [self.moc wmf_setValue:nil forKey:WMFPreviousLanguagesKey]; + }]; + self.cachedPreferredLanguages = nil; + [[NSNotificationCenter defaultCenter] postNotificationName:MWKLanguageFilterDataSourceLanguagesDidChangeNotification object:self]; + [[NSNotificationCenter defaultCenter] postNotificationName:WMFPreferredLanguagesDidChangeNotification object:self]; +} + +- (void)getPreferredContentLanguageCodes:(void (^)(NSArray *))completion { + [self.moc performBlock:^{ + completion([self readPreferredLanguageCodes]); + }]; +} + +- (void)getPreferredLanguageCodes:(void (^)(NSArray *))completion { + [self.moc performBlock:^{ + NSArray *preferredLanguages = [self preferredLanguages]; + NSMutableSet *preferredLanguageCodes = [[NSMutableSet alloc] init]; + for (MWKLanguageLink *language in preferredLanguages) { + [preferredLanguageCodes addObject:language.languageCode]; + } + + completion(preferredLanguageCodes.allObjects); + }]; +} + +// This method can only be safely called from the main app target, as an extension's standard `NSUserDefaults` are independent from the main app and other targets. ++ (void)migratePreferredLanguagesToManagedObjectContext:(NSManagedObjectContext *)moc { + NSArray *preferredLanguages = [[NSUserDefaults standardUserDefaults] arrayForKey:WMFPreviousLanguagesKey]; + [moc wmf_setValue:preferredLanguages forKey:WMFPreviousLanguagesKey]; +} + +- (void)migratePreferredLanguagesToLanguageVariants:(NSDictionary *)languageMapping inManagedObjectContext:(NSManagedObjectContext *)moc { + NSArray *preferredLanguageCodes = [[self readSavedPreferredLanguageCodesInManagedObjectContext:moc] copy]; + NSMutableArray *updatedLanguageCodes = [preferredLanguageCodes mutableCopy]; + BOOL languageCodesChanged = NO; + NSInteger currentIndex = 0; + for (NSString *languageCode in preferredLanguageCodes) { + NSString *migratedLanguageCode = languageMapping[languageCode]; + if (migratedLanguageCode) { + [updatedLanguageCodes replaceObjectAtIndex:currentIndex withObject:languageMapping[languageCode]]; + languageCodesChanged = YES; + } + currentIndex++; + } + + if (languageCodesChanged) { + // No changeType and nil changedLanguage will skip sending of notifications that preferred languages changed + [self savePreferredLanguageCodes:updatedLanguageCodes changeType:0 changedLanguage:nil inManagedObjectContext:moc]; + } +} + +- (void)migrateToUniquedPreferredLanguagesInManagedObjectContext:(NSManagedObjectContext *)moc { + NSArray *preferredLangaugeCodes = [self readSavedPreferredLanguageCodesInManagedObjectContext:moc]; + NSArray *uniquedCodes = [[NSOrderedSet orderedSetWithArray:preferredLangaugeCodes] array]; + if (preferredLangaugeCodes.count != uniquedCodes.count) { + // No changeType and nil changedLanguage will skip sending of notifications that preferred languages changed + [self savePreferredLanguageCodes:uniquedCodes changeType:0 changedLanguage:nil inManagedObjectContext:moc]; + } +} + +@end + +#pragma mark - + +@implementation MWKLanguageLinkController (ArticleLanguageLinkVariants) + +/// Given an article URL, if the URL has a language variant, an array of MWKLanguageLink instances of the remaining variants for that language is returned. +/// This allows a user viewing an article in one language variant to choose to view the article using another variant. +/// If the provided URL does not have a language variant, returns an empty array. +- (NSArray *)remainingLanguageLinkVariantsForArticleURL:(NSURL *)articleURL { + // If the original URL is a variant, include the other variants as choices + NSString *originalURLLanguageVariantCode = articleURL.wmf_languageVariantCode; + NSString *originalURLLanguageCode = articleURL.wmf_languageCode; + NSMutableArray *remainingLanguageVariantLinks = [[NSMutableArray alloc] init]; + if (originalURLLanguageVariantCode && originalURLLanguageCode) { + NSArray *variants = [MWKLanguageLinkController allLanguageVariantsBySiteLanguageCode][originalURLLanguageCode]; + if (variants) { + for (MWKLanguageLink *variant in variants) { + if (![variant.languageVariantCode isEqualToString:originalURLLanguageVariantCode]) { + MWKLanguageLink *articleVariant = [variant languageLinkWithPageTitleText:articleURL.wmf_titleWithUnderscores]; + [remainingLanguageVariantLinks addObject:articleVariant]; + } + } + } + } + return remainingLanguageVariantLinks; +} + +/// Given an array of article language links, returns an array where any language with variants is replaced with one article language link per variant +- (NSArray *)languageLinksReplacingArticleLanguageLinksWithVariants:(NSArray *)articleLanguageLinks { + NSMutableArray *processedLanguageLinks = [[NSMutableArray alloc] init]; + for (MWKLanguageLink *language in articleLanguageLinks) { + NSAssert((language.languageVariantCode == nil && ![language.languageVariantCode isEqualToString:@""]), @"The method %s should only be called with MWKLanguageLink objects with a nil or empty-string languageVariantCode", __PRETTY_FUNCTION__); + NSArray *variants = [MWKLanguageLinkController allLanguageVariantsBySiteLanguageCode][language.languageCode]; + if (variants) { + for (MWKLanguageLink *variant in variants) { + MWKLanguageLink *articleVariant = [variant languageLinkWithPageTitleText:language.pageTitleText]; + [processedLanguageLinks addObject:articleVariant]; + } + } else { + [processedLanguageLinks addObject:language]; + } + } + return processedLanguageLinks; +} + +- (NSArray *)articleLanguageLinksWithVariantsFromArticleURL:(NSURL *)articleURL articleLanguageLinks:(NSArray *)articleLanguageLinks { + + // If the original URL is a variant, include the other variants as choices + NSArray *remainingLanguageLinkVariants = [self remainingLanguageLinkVariantsForArticleURL:articleURL]; + + // If any of the available languages has variants, substitute in the variants. + NSArray *articleLanguageLinksWithVariants = [self languageLinksReplacingArticleLanguageLinksWithVariants:articleLanguageLinks]; + + return [articleLanguageLinksWithVariants arrayByAddingObjectsFromArray:remainingLanguageLinkVariants]; +} + +@end + +#pragma mark - + +@implementation MWKLanguageLinkController (LayoutDirectionAdditions) + ++ (BOOL)isLanguageRTLForContentLanguageCode:(nullable NSString *)contentLanguageCode { + return contentLanguageCode && [[MWKLanguageLinkController rtlContentLanguageCodes] containsObject:contentLanguageCode]; +} + ++ (NSString *)layoutDirectionForContentLanguageCode:(nullable NSString *)contentLanguageCode { + return [MWKLanguageLinkController isLanguageRTLForContentLanguageCode:contentLanguageCode] ? @"rtl" : @"ltr"; +} + ++ (UISemanticContentAttribute)semanticContentAttributeForContentLanguageCode:(nullable NSString *)contentLanguageCode { + if (!contentLanguageCode) { + return UISemanticContentAttributeUnspecified; + } + return [MWKLanguageLinkController isLanguageRTLForContentLanguageCode:contentLanguageCode] ? UISemanticContentAttributeForceRightToLeft : UISemanticContentAttributeForceLeftToRight; +} + +/* + * The set of content language codes that should be displayed right-to-left. This includes both language codes as well as any right-to-left language variants. + * For example, Kurdish has a Latin left-to-right variant (kk-latn) and an Arabic right-to-left variant (kk-arab). For languages with variants only variants + * that are right-to-left should be included in the set. + */ ++ (NSSet *)rtlContentLanguageCodes { + static dispatch_once_t onceToken; + static NSSet *rtlLanguages; + dispatch_once(&onceToken, ^{ + rtlLanguages = [NSSet setWithObjects:@"arc", @"arz", @"ar", @"azb", @"bcc", @"bqi", @"ckb", @"dv", @"fa", @"glk", @"lrc", @"he", @"khw", @"ks", @"mzn", @"nqo", @"pnb", @"ps", @"sd", @"ug", @"ur", @"yi", @"kk-arab", @"ku-arab", nil]; + }); + return rtlLanguages; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkController_Private.h b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkController_Private.h new file mode 100644 index 0000000..f2eecb7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkController_Private.h @@ -0,0 +1,22 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MWKLanguageLinkController (WMFTesting) + +/** + * Reads previously selected languages from storage. + * @return The preferred languages, or an empty array if none were previously added to the preferred list. + */ +- (NSArray *)readSavedPreferredLanguageCodes; + +/** + * Delete all previously selected languages. + * calling preferredLanguages will automatically restore the OS languages + * @warning For testing only! + */ +- (void)resetPreferredLanguages; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkFetcher.h b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkFetcher.h new file mode 100644 index 0000000..e8c3515 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkFetcher.h @@ -0,0 +1,20 @@ +@import WMF.WMFLegacyFetcher; + +/* + Note about langlinks: + This returns info about *any* langlinks found on a page, which might not always directly lead to a + translation of the current page. Specifically, main pages have lots of langlinks to other wikis' main pages. As such, + they will all be returned in a lanklinks query[0], giving the client the (arguably false) impression that the EN wiki + main page has been translated into other languages. + + 0: https://en.wikipedia.org/w/api.php?action=query&titles=Main_Page&prop=langlinks&lllimit=500&format=json + */ +NS_ASSUME_NONNULL_BEGIN + +@interface MWKLanguageLinkFetcher : WMFLegacyFetcher + +- (void)fetchLanguageLinksForArticleURL:(NSURL *)url success:(void (^)(NSArray *langLinks))success failure:(void (^)(NSError *error))error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkFetcher.m b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkFetcher.m new file mode 100644 index 0000000..b25be36 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLanguageLinkFetcher.m @@ -0,0 +1,57 @@ +#import "MWKLanguageLinkFetcher.h" +@import WMF.NSURL_WMFLinkParsing; +@import WMF.Swift; +@import WMF.MWKLanguageLink; +@import WMF.MWKLanguageLinkController; +@import WMF.WMFComparison; + +@implementation MWKLanguageLinkFetcher + +- (void)fetchLanguageLinksForArticleURL:(NSURL *)articleURL + success:(void (^)(NSArray *))success + failure:(void (^)(NSError *))failure { + NSString *title = articleURL.wmf_title; + if (!title.length) { + failure([WMFFetcher invalidParametersError]); + return; + } + NSDictionary *params = @{ + @"action": @"query", + @"prop": @"langlinks", + @"titles": title, + @"lllimit": @"500", + @"llprop": [@[@"langname", @"autonym"] componentsJoinedByString:@"|"], + @"llinlanguagecode": [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode], + @"redirects": @"", + @"format": @"json" + }; + [self performMediaWikiAPIGETForURL:articleURL + withQueryParameters:params + completionHandler:^(NSDictionary *_Nullable result, NSHTTPURLResponse *_Nullable response, NSError *_Nullable error) { + if (error) { + failure(error); + return; + } + NSDictionary *pagesByID = result[@"query"][@"pages"]; + NSDictionary *indexedLanguageLinks = [[pagesByID wmf_map:^id(id key, NSDictionary *result) { + return [result[@"langlinks"] wmf_map:^MWKLanguageLink *(NSDictionary *jsonLink) { + NSString *languageCode = [MWKLanguageLinkController languageCodeForISOLanguageCode:jsonLink[@"lang"]]; + return [[MWKLanguageLink alloc] initWithLanguageCode:languageCode + pageTitleText:jsonLink[@"*"] + name:jsonLink[@"autonym"] + localizedName:jsonLink[@"langname"] + languageVariantCode:nil + altISOCode:nil]; + }]; + }] wmf_reject:^BOOL(id key, id obj) { + return WMF_IS_EQUAL(obj, [NSNull null]); + }]; + NSAssert(indexedLanguageLinks.count < 2, + @"Expected language links to return one or no objects for the title we fetched, but got: %@", + indexedLanguageLinks); + NSArray *languageLinksForTitle = [[indexedLanguageLinks allValues] firstObject]; + success(languageLinksForTitle); + }]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLicense.h b/Apps/Wikipedia/Wikipedia/Code/MWKLicense.h new file mode 100644 index 0000000..a50c5f7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLicense.h @@ -0,0 +1,18 @@ +#import +NS_ASSUME_NONNULL_BEGIN +@interface MWKLicense : MWKDataObject + +@property (nullable, nonatomic, readonly, copy) NSString *code; +@property (nullable, nonatomic, readonly, copy) NSString *shortDescription; +@property (nullable, nonatomic, readonly, copy) NSURL *URL; + ++ (instancetype)licenseWithExportedData:(NSDictionary *)exportedData; + +- (instancetype)initWithCode:(nullable NSString *)code + shortDescription:(nullable NSString *)shortDescription + URL:(nullable NSURL *)URL; + +- (BOOL)isEqualToLicense:(MWKLicense *)other; + +@end +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLicense.m b/Apps/Wikipedia/Wikipedia/Code/MWKLicense.m new file mode 100644 index 0000000..292a46c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLicense.m @@ -0,0 +1,68 @@ +#import + +static NSString *const MWKLicenseCodeKey = @"code"; +static NSString *const MWKLicenseShortDescKey = @"shortDescription"; +static NSString *const MWKLicenseURLKey = @"URL"; + +@interface MWKLicense () + +@property (nonatomic, readwrite, copy) NSString *code; +@property (nonatomic, readwrite, copy) NSString *shortDescription; +@property (nonatomic, readwrite, copy) NSURL *URL; + +@end + +@implementation MWKLicense + +- (instancetype)initWithCode:(NSString *)code + shortDescription:(NSString *)shortDescription + URL:(NSURL *)URL { + self = [super init]; + if (self) { + self.code = code; + self.shortDescription = shortDescription; + self.URL = URL; + } + return self; +} + ++ (instancetype)licenseWithExportedData:(NSDictionary *)exportedData { + if (!exportedData) { + return nil; + } + return [[MWKLicense alloc] initWithCode:exportedData[MWKLicenseCodeKey] + shortDescription:exportedData[MWKLicenseShortDescKey] + URL:[NSURL URLWithString:exportedData[MWKLicenseURLKey]]]; +} + +- (id)dataExport { + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:3]; + [dict setValue:self.code forKey:MWKLicenseCodeKey]; + [dict setValue:self.shortDescription forKey:MWKLicenseShortDescKey]; + [dict setValue:self.URL.absoluteString forKey:MWKLicenseURLKey]; + return dict; +} + +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } else if ([object isKindOfClass:[MWKLicense class]]) { + return [self isEqualToLicense:object]; + } else { + return NO; + } +} + +- (BOOL)isEqualToLicense:(MWKLicense *)other { + return [self.code isEqualToString:other.code]; +} + +- (NSUInteger)hash { + return [self.code hash]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %@ %@", [super description], self.code, self.shortDescription]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKList+Subclass.h b/Apps/Wikipedia/Wikipedia/Code/MWKList+Subclass.h new file mode 100644 index 0000000..80f3e27 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKList+Subclass.h @@ -0,0 +1,65 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Block passed to lists when an entry is being updated. + * + * @param entry The entry to update. + * + * @return Whether or not the list should be considered dirty after the update. + */ +typedef BOOL (^MWKListUpdateBlock)(MWKListEntry entry); + +@interface MWKList (Subclasses) + +/** + * Invoked during @c initWithEntries: to set the receiver's internal @c entries to the given objects. + * + * Override this method to perform any preprocessing on the entries before they're set. Your implementation should + * call @c super at the end. + * + * @param entries The entries to be set in the receiver. + */ +- (void)importEntries:(nullable NSArray *)entries; + +/** + * Update the entry associated with @c listIndex, updating the internal @c dirty flag if necessary. + * + * @param listIndex The index of the entry to update. + * @param update A block which is given the entry to modify. + * + * @see MWKListUpdateBlock + */ +- (void)updateEntryWithListIndex:(MWKListIndex)listIndex update:(MWKListUpdateBlock)update; + +/** + * Insert @c entry at the given index. + * + * @param entry The entry to insert. + * @param index The index in the list to insert it, will raise an exception if out of bounds. + */ +- (void)insertEntry:(MWKListEntry)entry atIndex:(NSUInteger)index; + +/** + * Return sort descriptors used for sorting the list. + * Sorting will occur whenever the list is updated. + * The defualt implementation will return nil which results in no sorting + * + * @return The sort descriptors to use for sorting the entries + */ +- (nullable NSArray *)sortDescriptors; + +/* + * Indicates if the list has been mutated since the last save. + */ +@property (nonatomic, assign, readonly) BOOL dirty; + +/** + * Subclasses must implement to support saving + */ +- (void)performSaveWithCompletion:(dispatch_block_t)completion error:(WMFErrorHandler)errorHandler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKList.h b/Apps/Wikipedia/Wikipedia/Code/MWKList.h new file mode 100644 index 0000000..68bb1e6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKList.h @@ -0,0 +1,62 @@ +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol MWKListObject + +- (id)listIndex; + +@end + +typedef id MWKListEntry; +typedef id MWKListIndex; + +/** + * Abstract base class for homogeneous lists of model objects. + * + * Can be specialized to contain instances of @c EntryType, which are queryable by index or an associated key of type + * @c IndexType. + */ +@interface MWKList : WMFMTLModel + + - (instancetype)initWithEntries:(NSArray* __nullable)entries; + +/** + * Observable - observe to get KVO notifications + */ +@property (nonatomic, strong, readonly) NSArray *entries; + +#pragma mark - Querying the List + +- (NSUInteger)countOfEntries; + +- (NSUInteger)indexForEntry:(EntryType)entry; + +- (EntryType)entryAtIndex:(NSUInteger)index; + +- (EntryType __nullable)entryForListIndex:(IndexType)listIndex; + +- (BOOL)containsEntryForListIndex:(IndexType)listIndex; + +#pragma mark - Mutating the List + +- (void)addEntry:(EntryType)entry; + +- (void)removeEntry:(EntryType)entry; + +- (void)removeEntryWithListIndex:(IndexType)listIndex; + +- (void)removeAllEntries; + +- (NSArray *)pruneToMaximumCount:(NSUInteger)maximumCount; + +#pragma mark - Persisting Changes to the List + +- (void)saveWithFailure:(WMFErrorHandler)failure success:(WMFSuccessHandler)success; +- (void)save; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKList.m b/Apps/Wikipedia/Wikipedia/Code/MWKList.m new file mode 100644 index 0000000..f5fcbfb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKList.m @@ -0,0 +1,225 @@ +#import +#import + +@interface MWKList () + +@property (nonatomic, strong) NSMutableArray> *mutableEntries; +@property (nonatomic, readwrite, assign) BOOL dirty; + +@end + +@implementation MWKList + +#pragma mark - Setup + +- (instancetype)init { + self = [super init]; + if (self) { + _mutableEntries = [NSMutableArray array]; + } + return self; +} + +- (instancetype)initWithEntries:(NSArray *__nullable)entries { + self = [self init]; + if (self) { + [self importEntries:entries]; + [self sortEntries]; + } + return self; +} + ++ (MTLPropertyStorage)storageBehaviorForPropertyWithKey:(NSString *)propertyKey { + if ([propertyKey isEqualToString:WMF_SAFE_KEYPATH([MWKList new], mutableEntries)]) { + return MTLPropertyStoragePermanent; + } + return MTLPropertyStorageNone; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %@", [super description], self.entries]; +} + +#pragma mark - Import + +- (void)importEntries:(NSArray *)entries { + [self.mutableEntries setArray:entries]; +} + +#pragma mark - NSFastEnumeration + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(__unsafe_unretained id[])stackbuf + count:(NSUInteger)len { + return [self.entries countByEnumeratingWithState:state objects:stackbuf count:len]; +} + +#pragma mark - Entry Access + +- (NSArray *)entries { + return _mutableEntries; +} + +- (void)addEntry:(id)entry { + NSAssert([entry conformsToProtocol:@protocol(MWKListObject)], @"attempting to add object that does not implement MWKListObject"); + [self.mutableEntries addObject:entry]; + [self sortEntries]; + self.dirty = YES; +} + +- (void)insertEntry:(id)entry atIndex:(NSUInteger)index { + NSAssert([entry conformsToProtocol:@protocol(MWKListObject)], @"attempting to insert object that does not implement MWKListObject"); + [self.mutableEntries insertObject:entry atIndex:index]; + [self sortEntries]; + self.dirty = YES; +} + +- (NSUInteger)indexForEntry:(id)entry { + return [self.mutableEntries indexOfObject:entry]; +} + +- (id)entryAtIndex:(NSUInteger)index { + return (id)[self objectInEntriesAtIndex:index]; +} + +- (id __nullable)entryForListIndex:(MWKListIndex)listIndex { + return [self.entries wmf_match:^BOOL(id obj) { + if ([[obj listIndex] isEqual:listIndex]) { + return YES; + } + return NO; + }]; +} + +- (BOOL)containsEntryForListIndex:(MWKListIndex)listIndex { + id entry = [self entryForListIndex:listIndex]; + return (entry != nil); +} + +- (void)updateEntryWithListIndex:(id)listIndex update:(BOOL (^)(id entry))update { + id obj = [self entryForListIndex:listIndex]; + if (update) { + BOOL dirty = update(obj); + if (dirty) { + [self sortEntries]; + self.dirty = YES; + } + } +} + +- (void)removeEntry:(id)entry { + [self.mutableEntries removeObject:entry]; + self.dirty = YES; +} + +- (void)removeEntryWithListIndex:(id)listIndex { + id obj = [self entryForListIndex:listIndex]; + if (obj) { + [self removeEntry:obj]; + } +} + +- (void)removeAllEntries { + [self.mutableEntries removeAllObjects]; + self.dirty = YES; +} + +- (NSArray *)pruneToMaximumCount:(NSUInteger)maximumCount { + if (self.mutableEntries.count > maximumCount) { + NSRange range = NSMakeRange(maximumCount, self.mutableEntries.count - maximumCount); + NSArray *removed = [self.mutableEntries subarrayWithRange:range]; + [self.mutableEntries removeObjectsInRange:range]; + self.dirty = YES; + return removed; + } else { + return @[]; + } +} + +- (void)sortEntries { + if ([[self sortDescriptors] count] > 0) { + [self.mutableEntries sortUsingDescriptors:[self sortDescriptors]]; + } +} + +- (nullable NSArray *)sortDescriptors { + return nil; +} + +#pragma mark - Save + +- (void)saveWithFailure:(WMFErrorHandler)failure success:(WMFSuccessHandler)success { + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.dirty) { + [self performSaveWithCompletion:^{ + self.dirty = NO; + success(); + } + error:^(NSError *error) { + failure(error); + }]; + } else { + self.dirty = NO; + success(); + } + }); +} + +- (void)save { + [self saveWithFailure:^(NSError *_Nonnull error) { + + } + success:^{ + + }]; +} + +- (void)performSaveWithCompletion:(dispatch_block_t)completion error:(WMFErrorHandler)errorHandler { + assert(false); + if (errorHandler) { + errorHandler([NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:nil]); + } +} + +#pragma mark - KVO + +- (NSMutableArray *)mutableEntries { + return [self mutableArrayValueForKey:WMF_SAFE_KEYPATH(self, entries)]; +} + +- (NSUInteger)countOfEntries { + return [_mutableEntries count]; +} + +- (id)objectInEntriesAtIndex:(NSUInteger)idx { + if (![self countOfEntries] || [self countOfEntries] <= idx) { + return NULL; + } + return [_mutableEntries objectAtIndex:idx]; +} + +- (void)insertObject:(id)anObject inEntriesAtIndex:(NSUInteger)idx { + [_mutableEntries insertObject:anObject atIndex:idx]; +} + +- (void)insertEntries:(NSArray *)entrieArray atIndexes:(NSIndexSet *)indexes { + [_mutableEntries insertObjects:entrieArray atIndexes:indexes]; +} + +- (void)removeObjectFromEntriesAtIndex:(NSUInteger)idx { + [_mutableEntries removeObjectAtIndex:idx]; +} + +- (void)removeEntriesAtIndexes:(NSIndexSet *)indexes { + [_mutableEntries removeObjectsAtIndexes:indexes]; +} + +- (void)replaceObjectInEntriesAtIndex:(NSUInteger)idx withObject:(id)anObject { + [_mutableEntries replaceObjectAtIndex:idx withObject:anObject]; +} + +- (void)replaceEntriesAtIndexes:(NSIndexSet *)indexes withEntries:(NSArray *)entrieArray { + [_mutableEntries replaceObjectsAtIndexes:indexes withObjects:entrieArray]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLocationSearchResult.h b/Apps/Wikipedia/Wikipedia/Code/MWKLocationSearchResult.h new file mode 100644 index 0000000..fc31938 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLocationSearchResult.h @@ -0,0 +1,16 @@ +#import +@import CoreLocation; + +/** + * Response object model for search results which have geocoordinates. + * + * @warning This object only supports deserialization from JSON, not serialization to JSON. + */ +@interface MWKLocationSearchResult : MWKSearchResult + +/** + * Number of meters between the receiver and the coordinate parameters of the originating search. + */ +@property (nonatomic, assign, readonly) CLLocationDistance distanceFromQueryCoordinates; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKLocationSearchResult.m b/Apps/Wikipedia/Wikipedia/Code/MWKLocationSearchResult.m new file mode 100644 index 0000000..ab39ec1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKLocationSearchResult.m @@ -0,0 +1,32 @@ +#import +#import + +@implementation MWKLocationSearchResult + ++ (NSValueTransformer *)distanceFromQueryCoordinatesJSONTransformer { + return [MTLValueTransformer transformerUsingForwardBlock:^id(NSArray *value, + BOOL *success, + NSError *__autoreleasing *error) { + NSDictionary *coords = [value firstObject]; + NSNumber *distance = coords[@"dist"]; + if (![distance isKindOfClass:[NSNumber class]]) { + distance = @(0); + } + return distance; + }]; +} + ++ (NSDictionary *)JSONKeyPathsByPropertyKey { + NSMutableDictionary *mapping = [[super JSONKeyPathsByPropertyKey] mutableCopy]; + /* + coordinates is an array of objects which have lat, lon, & dist fields. pick the fist one and set the corresponding + properties here. + */ + [mapping addEntriesFromDictionary:@{ + WMF_SAFE_KEYPATH([MWKLocationSearchResult new], distanceFromQueryCoordinates): @"coordinates", + }]; + + return mapping; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchEntry.h b/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchEntry.h new file mode 100644 index 0000000..31d163b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchEntry.h @@ -0,0 +1,11 @@ +#import +#import + +@interface MWKRecentSearchEntry : MWKSiteDataObject + +@property (readonly, copy, nonatomic) NSString *searchTerm; + +- (instancetype)initWithURL:(NSURL *)url searchTerm:(NSString *)searchTerm; +- (instancetype)initWithDict:(NSDictionary *)dict; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchEntry.m b/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchEntry.m new file mode 100644 index 0000000..64ab1ad --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchEntry.m @@ -0,0 +1,72 @@ +#import +#import +#import +#import + +@interface MWKRecentSearchEntry () + +@property (readwrite, copy, nonatomic) NSString *searchTerm; + +@end + +@implementation MWKRecentSearchEntry + +- (instancetype)initWithURL:(NSURL *)url searchTerm:(NSString *)searchTerm { + url = [NSURL wmf_desktopURLForURL:url]; + NSParameterAssert(url); + NSParameterAssert(searchTerm); + self = [self initWithURL:url]; + if (self) { + self.searchTerm = searchTerm; + } + return self; +} + +- (instancetype)initWithDict:(NSDictionary *)dict { + NSString *urlString = dict[@"url"]; + NSString *domain = dict[@"domain"]; + NSString *language = dict[@"language"]; + + NSURL *url; + + if ([urlString length]) { + url = [NSURL URLWithString:urlString]; + } else if (domain && language) { + url = [NSURL wmf_URLWithDomain:domain languageCode:language]; + } else { + return nil; + } + + NSString *searchTerm = dict[@"searchTerm"]; + self = [self initWithURL:url searchTerm:searchTerm]; + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %@", [super description], self.searchTerm]; +} + +WMF_SYNTHESIZE_IS_EQUAL(MWKRecentSearchEntry, isEqualToRecentSearch:) + +- (BOOL)isEqualToRecentSearch:(MWKRecentSearchEntry *)rhs { + return WMF_RHS_PROP_EQUAL(url, isEqual:) && WMF_RHS_PROP_EQUAL(searchTerm, isEqualToString:); +} + +- (NSUInteger)hash { + return self.searchTerm.hash ^ flipBitsWithAdditionalRotation(self.url.hash, 1); +} + +#pragma mark - MWKListObject + +- (id)listIndex { + return self.searchTerm; +} + +- (id)dataExport { + return @{ + @"url": [self.url absoluteString], + @"searchTerm": self.searchTerm + }; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchList.h b/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchList.h new file mode 100644 index 0000000..b39a5ba --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchList.h @@ -0,0 +1,12 @@ +#import +#import +#import + +@class MWKDataStore; + +@interface MWKRecentSearchList : MWKList + + + - (NSArray *)dataExport; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchList.m b/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchList.m new file mode 100644 index 0000000..8c2313c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKRecentSearchList.m @@ -0,0 +1,75 @@ +#import +#import +#import + +@interface MWKRecentSearchList () + +@property (readwrite, weak, nonatomic) MWKDataStore *dataStore; + +@end + +@implementation MWKRecentSearchList + +#pragma mark - Setup + +- (instancetype)initWithDataStore:(MWKDataStore *)dataStore { + NSArray *entries = [[dataStore recentSearchListData] wmf_mapAndRejectNil:^id(id obj) { + @try { + return [[MWKRecentSearchEntry alloc] initWithDict:obj]; + } @catch (NSException *e) { + DDLogError(@"Encountered exception while reading entry %@: %@", e, obj); + return nil; + } + }]; + + self = [super initWithEntries:entries]; + if (self) { + self.dataStore = dataStore; + } + return self; +} + +#pragma mark - Validation + +- (BOOL)isEntryValid:(MWKRecentSearchEntry *)entry { + return entry.searchTerm.length > 0 && entry.url; +} + +#pragma mark - Data Update + +- (void)importEntries:(NSArray *)entries { + [super importEntries:[entries wmf_select:^BOOL(MWKRecentSearchEntry *entry) { + return [self isEntryValid:entry]; + }]]; +} + +- (void)addEntry:(MWKRecentSearchEntry *)entry { + if (![self isEntryValid:entry]) { + return; + } + [self removeEntryWithListIndex:entry.searchTerm]; + [self insertEntry:entry atIndex:0]; +} + +#pragma mark - Save + +- (void)performSaveWithCompletion:(dispatch_block_t)completion error:(WMFErrorHandler)errorHandler { + NSError *error; + if ([self.dataStore saveRecentSearchList:self error:&error]) { + if (completion) { + completion(); + } + } else { + if (errorHandler) { + errorHandler(error); + } + } +} + +- (NSArray *)dataExport { + return [self.entries wmf_map:^id(MWKRecentSearchEntry *obj) { + return [obj dataExport]; + }]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKSavedPageList.h b/Apps/Wikipedia/Wikipedia/Code/MWKSavedPageList.h new file mode 100644 index 0000000..195dbd5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKSavedPageList.h @@ -0,0 +1,65 @@ +@import Foundation; +@class WMFArticle, MWKDataStore; + +NS_ASSUME_NONNULL_BEGIN + +@interface MWKSavedPageList : NSObject + +- (instancetype)initWithDataStore:(MWKDataStore *)dataStore NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; + +@property (readonly, weak, nonatomic) MWKDataStore *dataStore; + +#pragma mark - Convienence Methods + +- (NSInteger)numberOfItems; + +- (nullable WMFArticle *)mostRecentEntry; + +- (nullable WMFArticle *)entryForURL:(NSURL *)url; + +// This method retrieves whichever variant of the article specified by the key is currently saved +// Even if a different article variant is being shown to the user, it is the variant that is actually saved +// that will be removed. +- (nullable WMFArticle *)articleToUnsaveForKey:(NSString *)key; + +- (void)enumerateItemsWithBlock:(void (^)(WMFArticle *_Nonnull entry, BOOL *stop))block; + +- (BOOL)isAnyVariantSaved:(NSURL *)url; + +#pragma mark - Update Methods + +/** + * Toggle the save state for the article with `key`. + * + * @param key to toggle state for, either saving or un-saving it. Key is a standardized version of the article URL obtained by the key property on WMFArticle or from a URL with wmf_databaseKey + * @param variant to toggle state for. Variant is a language variant code. + * @return whether or not the key is now saved + */ +- (BOOL)toggleSavedPageForKey:(NSString *)key variant:(nullable NSString *)variant; + +/** + * Toggle the save state for `url`. + * + * @param url URL to toggle state for, either saving or un-saving it. + * @return whether or not the URL is now saved + */ +- (BOOL)toggleSavedPageForURL:(NSURL *)url; + +/** + * Add a saved page + * + * @param url The url of the page to add + */ +- (void)addSavedPageWithURL:(NSURL *)url; + +/** + * Remove a saved page + * + * @param url The url of the page to remove + */ +- (void)removeEntryWithURL:(NSURL *)url; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKSavedPageList.m b/Apps/Wikipedia/Wikipedia/Code/MWKSavedPageList.m new file mode 100644 index 0000000..b161357 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKSavedPageList.m @@ -0,0 +1,146 @@ +#import +#import + +@interface MWKSavedPageList () + +@property (readwrite, weak, nonatomic) MWKDataStore *dataStore; + +@property (nonatomic, readonly) NSFetchRequest *savedPageListFetchRequest; + +@end + +@implementation MWKSavedPageList + +#pragma mark - Setup + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (instancetype)initWithDataStore:(MWKDataStore *)dataStore { + NSParameterAssert(dataStore); + self = [super init]; + if (self) { + self.dataStore = dataStore; + } + return self; +} + +#pragma mark - Convienence Methods + +- (NSFetchRequest *)savedPageListFetchRequest { + NSFetchRequest *request = [WMFArticle fetchRequest]; + request.predicate = [NSPredicate predicateWithFormat:@"savedDate != NULL"]; + request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"savedDate" ascending:NO]]; + return request; +} + +- (NSInteger)numberOfItems { + return [self.dataStore.viewContext countForFetchRequest:self.savedPageListFetchRequest error:nil]; +} + +- (nullable WMFArticle *)mostRecentEntry { + NSFetchRequest *request = self.savedPageListFetchRequest; + request.fetchLimit = 1; + return [[self.dataStore.viewContext executeFetchRequest:request error:nil] firstObject]; +} + +- (nullable WMFArticle *)entryForURL:(NSURL *)url { + NSString *key = url.wmf_databaseKey; + if (!key) { + return nil; + } + WMFArticle *article = [self.dataStore fetchArticleWithKey:key variant:url.wmf_languageVariantCode]; + if (article.savedDate) { + return article; + } else { + return nil; + } +} + +- (nullable WMFArticle *)articleToUnsaveForKey:(NSString *)key { + if (!key) { + return nil; + } + WMFArticle *article = [self.dataStore fetchArticleWithKey:key]; + if (article.savedDate) { + return article; + } else { + return nil; + } +} + +- (void)enumerateItemsWithBlock:(void (^)(WMFArticle *_Nonnull entry, BOOL *stop))block { + if (!block) { + return; + } + + NSFetchRequest *request = self.savedPageListFetchRequest; + NSArray *allEntries = [self.dataStore.viewContext executeFetchRequest:request error:nil]; + [allEntries enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { + block(obj, stop); + }]; +} + +- (BOOL)isAnyVariantSaved:(NSURL *)url { + if ([url.wmf_title length] == 0) { + return NO; + } + WMFArticle *article = [self.dataStore fetchArticleWithURL:url]; + return article.isAnyVariantSaved; +} + +#pragma mark - Update Methods + +/** These methods accept a 'fully qualified' article specification consisting of the database key and the language variant. + * When adding to the list, that specific variant is added to the list. + * When removed from the list, *any* article that matches that database key is removed from the list. + * That logic is handled in the reading lists controller, and these methods just pass along the 'fully qualified' articles or URLs. + * However, the methods in this class do need to take into account whether any variants are saved to determine the correct toggle behavior. + */ + +- (BOOL)toggleSavedPageForURL:(NSURL *)url { + if ([self isAnyVariantSaved:url]) { + [self removeEntryWithURL:url]; + return NO; + } else { + [self addSavedPageWithURL:url]; + return YES; + } +} + +- (BOOL)toggleSavedPageForKey:(NSString *)key variant:(nullable NSString *)variant { + if (!key) { + return NO; + } + NSManagedObjectContext *moc = self.dataStore.viewContext; + if (!moc) { + return NO; + } + WMFArticle *article = [self.dataStore fetchArticleWithKey:key variant:variant]; + if (article.isAnyVariantSaved) { + [self.dataStore.readingListsController userUnsave:article]; + } else { + [self.dataStore.readingListsController userSave:article]; + } + return article.savedDate != nil; +} + +- (void)addSavedPageWithURL:(NSURL *)url { + WMFArticle *article = [self.dataStore fetchOrCreateArticleWithURL:url]; + [self.dataStore.readingListsController userSave:article]; +} + +- (void)removeEntryWithURL:(NSURL *)url { + NSManagedObjectContext *moc = self.dataStore.viewContext; + if (!moc) { + return; + } + WMFArticle *article = [self.dataStore fetchArticleWithURL:url]; + if (!article) { + return; + } + [self.dataStore.readingListsController userUnsave:article]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKSearchRedirectMapping.h b/Apps/Wikipedia/Wikipedia/Code/MWKSearchRedirectMapping.h new file mode 100644 index 0000000..05d8ea1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKSearchRedirectMapping.h @@ -0,0 +1,14 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MWKSearchRedirectMapping : WMFMTLModel + +@property (nonatomic, copy, readonly) NSString *redirectFromTitle; +@property (nonatomic, copy, readonly) NSString *redirectToTitle; + ++ (instancetype)mappingFromTitle:(NSString *)from toTitle:(NSString *)to; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKSearchRedirectMapping.m b/Apps/Wikipedia/Wikipedia/Code/MWKSearchRedirectMapping.m new file mode 100644 index 0000000..76b3cf0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKSearchRedirectMapping.m @@ -0,0 +1,34 @@ +#import "MWKSearchRedirectMapping.h" +@import WMF.WMFComparison; + +NS_ASSUME_NONNULL_BEGIN + +@interface MWKSearchRedirectMapping () + +@property (nonatomic, copy, readwrite) NSString *redirectFromTitle; +@property (nonatomic, copy, readwrite) NSString *redirectToTitle; + +@end + +@implementation MWKSearchRedirectMapping + ++ (instancetype)mappingFromTitle:(NSString *)from toTitle:(NSString *)to { + MWKSearchRedirectMapping *instance = [self new]; + instance.redirectFromTitle = from; + instance.redirectToTitle = to; + return instance; +} + ++ (NSDictionary *)JSONKeyPathsByPropertyKey { + return @{ + WMF_SAFE_KEYPATH(MWKSearchRedirectMapping.new, redirectFromTitle): @"from", + WMF_SAFE_KEYPATH(MWKSearchRedirectMapping.new, redirectToTitle): @"to", + }; +} + +// No languageVariantCodePropagationSubelementKeys +// No languageVariantCodePropagationURLKeys + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKSearchResult.h b/Apps/Wikipedia/Wikipedia/Code/MWKSearchResult.h new file mode 100644 index 0000000..177f24e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKSearchResult.h @@ -0,0 +1,50 @@ +#import +@import CoreLocation; + +@interface MWKSearchResult : WMFMTLModel + +@property (nonatomic, assign, readonly) NSInteger articleID; + +@property (nonatomic, assign, readonly) NSInteger revID; + +@property (nullable, nonatomic, copy, readonly) NSString *displayTitle; + +@property (nullable, nonatomic, copy, readonly) NSString *displayTitleHTML; + +@property (nullable, nonatomic, copy, readonly) NSString *title; + +@property (nullable, nonatomic, copy, readonly) NSString *wikidataDescription; + +@property (nullable, nonatomic, copy, readonly) NSString *extract; + +@property (nullable, nonatomic, copy, readonly) NSURL *thumbnailURL; + +@property (nullable, nonatomic, copy, readonly) NSNumber *index; + +@property (nullable, nonatomic, copy, readonly) NSNumber *titleNamespace; + +@property (nullable, nonatomic, copy) NSArray *viewCounts; + +- (nullable NSURL *)articleURLForSiteURL:(nullable NSURL *)siteURL; + +/** + * Location serialized from the first set of coordinates in the response. + */ +@property (nullable, nonatomic, copy, readonly) CLLocation *location; + +@property (nullable, nonatomic, copy) NSNumber *geoDimension; +@property (nullable, nonatomic, copy) NSNumber *geoType; + +- (nullable instancetype)initWithArticleID:(NSInteger)articleID + revID:(NSInteger)revID + title:(nullable NSString *)title + displayTitle:(nullable NSString *)displayTitle + displayTitleHTML:(nullable NSString *)displayTitleHTML + wikidataDescription:(nullable NSString *)wikidataDescription + extract:(nullable NSString *)extract + thumbnailURL:(nullable NSURL *)thumbnailURL + index:(nullable NSNumber *)index + titleNamespace:(nullable NSNumber *)titleNamespace + location:(nullable CLLocation *)location; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKSearchResult.m b/Apps/Wikipedia/Wikipedia/Code/MWKSearchResult.m new file mode 100644 index 0000000..484f44b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKSearchResult.m @@ -0,0 +1,264 @@ +#import +#import +#import +#import +#import +#import +#import + +@interface MWKSearchResult () + +@property (nonatomic, assign, readwrite) NSInteger articleID; + +@property (nonatomic, assign, readwrite) NSInteger revID; + +@property (nonatomic, copy, readwrite) NSString *title; + +@property (nonatomic, copy, readwrite) NSString *displayTitle; + +@property (nonatomic, copy, readwrite) NSString *displayTitleHTML; + +@property (nonatomic, copy, readwrite) NSString *wikidataDescription; + +@property (nonatomic, copy, readwrite) NSString *extract; + +@property (nonatomic, copy, readwrite) NSURL *thumbnailURL; + +@property (nonatomic, copy, readwrite) NSNumber *index; + +@property (nonatomic, copy, readwrite) NSNumber *titleNamespace; + +@property (nonatomic, copy, readwrite) CLLocation *location; + +@end + +@implementation MWKSearchResult + +- (instancetype)initWithArticleID:(NSInteger)articleID + revID:(NSInteger)revID + title:(NSString *)title + displayTitle:(NSString *)displayTitle + displayTitleHTML:(NSString *)displayTitleHTML + wikidataDescription:(NSString *)wikidataDescription + extract:(NSString *)extract + thumbnailURL:(NSURL *)thumbnailURL + index:(NSNumber *)index + titleNamespace:(NSNumber *)titleNamespace + location:(nullable CLLocation *)location { + self = [super init]; + if (self) { + self.articleID = articleID; + self.revID = revID; + self.title = title; + self.displayTitle = displayTitle; + self.displayTitleHTML = displayTitleHTML; + self.wikidataDescription = wikidataDescription; + self.extract = extract; + self.thumbnailURL = thumbnailURL; + self.index = index; + self.titleNamespace = titleNamespace; + self.location = location; + } + return self; +} + ++ (NSUInteger)modelVersion { + return 4; +} + +#pragma mark - MTLJSONSerializing + ++ (NSValueTransformer *)thumbnailURLJSONTransformer { + return [MTLValueTransformer + transformerUsingForwardBlock:^NSURL *(NSString *urlString, + BOOL *success, + NSError *__autoreleasing *error) { + return [NSURL wmf_optionalURLWithString:urlString]; + } + reverseBlock:^NSString *(NSURL *thumbnailURL, + BOOL *success, + NSError *__autoreleasing *error) { + return [thumbnailURL absoluteString]; + }]; +} + ++ (MTLValueTransformer *)extractJSONTransformer { + return [MTLValueTransformer transformerUsingForwardBlock:^id(NSString *extract, BOOL *success, NSError *__autoreleasing *error) { + // Remove trailing ellipsis added by the API + if ([extract hasSuffix:@"..."]) { + if (extract.length == 3) { + // HAX: sometimes the api gives us "..." for the extract, which is not useful and messes up how random + // weights relative quality of the random titles it retrieves. + extract = nil; + } else { + extract = [extract substringWithRange:NSMakeRange(0, extract.length - 3)]; + } + } + + return [extract wmf_summaryFromText]; + }]; +} + ++ (NSString *)displayTitleFromValue:(NSDictionary *)value { + NSString *displayTitle = value[@"pageprops.displaytitle"]; + if ([displayTitle isKindOfClass:[NSString class]]) { // nil & type check just to be safe + return displayTitle; + } + NSString *title = value[@"title"]; + if ([title isKindOfClass:[NSString class]]) { // nil & type check just to be safe + return title; + } + return @""; +} + ++ (NSValueTransformer *)displayTitleJSONTransformer { + return [MTLValueTransformer + transformerUsingForwardBlock:^(NSDictionary *value, BOOL *success, NSError **error) { + return [[self displayTitleFromValue:value] wmf_stringByRemovingHTML]; + }]; +} + ++ (NSValueTransformer *)displayTitleHTMLJSONTransformer { + return [MTLValueTransformer + transformerUsingForwardBlock:^(NSDictionary *value, BOOL *success, NSError **error) { + return [self displayTitleFromValue:value]; + }]; +} + ++ (NSValueTransformer *)revIDJSONTransformer { + return [MTLValueTransformer + transformerUsingForwardBlock:^id(NSArray *value, BOOL *success, NSError **error) { + return (value.count > 0 && value.firstObject[@"revid"]) ? value.firstObject[@"revid"] : @(0); + }]; +} + ++ (NSValueTransformer *)locationJSONTransformer { + return [MTLValueTransformer transformerUsingForwardBlock:^id(NSArray *value, + BOOL *success, + NSError *__autoreleasing *error) { + NSDictionary *coords = [value firstObject]; + NSNumber *lat = coords[@"lat"]; + NSNumber *lon = coords[@"lon"]; + + if (![lat isKindOfClass:[NSNumber class]] || ![lon isKindOfClass:[NSNumber class]]) { + return nil; + } + + return [[CLLocation alloc] initWithLatitude:[lat doubleValue] longitude:[lon doubleValue]]; + }]; +} + ++ (NSValueTransformer *)geoTypeJSONTransformer { + static NSDictionary *geoTypeLookup; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + geoTypeLookup = @{@"country": @(WMFGeoTypeCountry), + @"satellite": @(WMFGeoTypeSatellite), + @"adm1st": @(WMFGeoTypeAdm1st), + @"adm2nd": @(WMFGeoTypeAdm2nd), + @"adm3rd": @(WMFGeoTypeAdm3rd), + @"city": @(WMFGeoTypeCity), + @"airport": @(WMFGeoTypeAirport), + @"mountain": @(WMFGeoTypeMountain), + @"isle": @(WMFGeoTypeIsle), + @"waterbody": @(WMFGeoTypeWaterBody), + @"forest": @(WMFGeoTypeForest), + @"river": @(WMFGeoTypeRiver), + @"glacier": @(WMFGeoTypeGlacier), + @"event": @(WMFGeoTypeEvent), + @"edu": @(WMFGeoTypeEdu), + @"pass": @(WMFGeoTypePass), + @"railwaystation": @(WMFGeoTypeRailwayStation), + @"landmark": @(WMFGeoTypeLandmark)}; + }); + + return [MTLValueTransformer transformerUsingForwardBlock:^id(NSArray *value, + BOOL *success, + NSError *__autoreleasing *error) { + NSDictionary *coords = [value firstObject]; + NSString *type = coords[@"type"]; + + if (![type isKindOfClass:[NSString class]]) { + return nil; + } + + type = [type lowercaseString]; + + if ([type hasPrefix:@"city"]) { + type = @"city"; + } + + return geoTypeLookup[type]; + }]; +} + ++ (NSValueTransformer *)geoDimensionJSONTransformer { + static dispatch_once_t onceToken; + static NSCharacterSet *nonNumericCharacterSet; + dispatch_once(&onceToken, ^{ + nonNumericCharacterSet = [[NSCharacterSet decimalDigitCharacterSet] invertedSet]; + }); + return [MTLValueTransformer transformerUsingForwardBlock:^id(NSArray *value, + BOOL *success, + NSError *__autoreleasing *error) { + NSDictionary *coords = [value firstObject]; + NSString *dim = coords[@"dim"]; + + if (![dim isKindOfClass:[NSString class]]) { + return nil; + } + + NSString *dimToParse = [dim stringByTrimmingCharactersInSet:nonNumericCharacterSet]; + long long dimension = [dimToParse longLongValue]; + if (dimension == 0) { + return nil; + } + + dim = [dim lowercaseString]; + if ([dim hasSuffix:@"km"]) { + dimension = dimension * 1000; + } + + return @(dimension); + }]; +} + ++ (NSDictionary *)JSONKeyPathsByPropertyKey { + return @{WMF_SAFE_KEYPATH(MWKSearchResult.new, title): @"title", + WMF_SAFE_KEYPATH(MWKSearchResult.new, displayTitle): @[@"pageprops.displaytitle", @"title"], + WMF_SAFE_KEYPATH(MWKSearchResult.new, displayTitleHTML): @[@"pageprops.displaytitle", @"title"], + WMF_SAFE_KEYPATH(MWKSearchResult.new, articleID): @"pageid", + WMF_SAFE_KEYPATH(MWKSearchResult.new, revID): @"revisions", + WMF_SAFE_KEYPATH(MWKSearchResult.new, thumbnailURL): @"thumbnail.source", + WMF_SAFE_KEYPATH(MWKSearchResult.new, wikidataDescription): @"description", + WMF_SAFE_KEYPATH(MWKSearchResult.new, extract): @"extract", + WMF_SAFE_KEYPATH(MWKSearchResult.new, index): @"index", + WMF_SAFE_KEYPATH(MWKSearchResult.new, location): @"coordinates", + WMF_SAFE_KEYPATH(MWKSearchResult.new, geoDimension): @"coordinates", + WMF_SAFE_KEYPATH(MWKSearchResult.new, geoType): @"coordinates", + WMF_SAFE_KEYPATH(MWKSearchResult.new, titleNamespace): @"ns"}; +} + +- (nullable NSURL *)articleURLForSiteURL:(nullable NSURL *)siteURL { + if (siteURL == nil) { + return nil; + } + if (self.title == nil) { + return nil; + } + return [siteURL wmf_URLWithTitle:self.title]; +} + +- (NSString *)displayTitleHTML { + return _displayTitleHTML && ![_displayTitleHTML isEqualToString:@""] ? _displayTitleHTML : _displayTitle; +} + +#pragma mark - Propagate Language Variant Code + +// No languageVariantCodePropagationSubelementKeys + ++ (NSArray *)languageVariantCodePropagationURLKeys { + return @[@"thumbnailURL"]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKSiteDataObject.h b/Apps/Wikipedia/Wikipedia/Code/MWKSiteDataObject.h new file mode 100644 index 0000000..40847a3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKSiteDataObject.h @@ -0,0 +1,15 @@ +#import + +@interface MWKSiteDataObject : MWKDataObject + +@property (readonly, strong, nonatomic) NSURL *url; + +- (instancetype)initWithURL:(NSURL *)url NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +- (NSURL *)optionalURL:(NSString *)key dict:(NSDictionary *)dict; +- (NSURL *)requiredURL:(NSString *)key dict:(NSDictionary *)dict; +- (NSURL *)requiredURL:(NSString *)key dict:(NSDictionary *)dict allowEmpty:(BOOL)allowEmpty; + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKSiteDataObject.m b/Apps/Wikipedia/Wikipedia/Code/MWKSiteDataObject.m new file mode 100644 index 0000000..a4b44ad --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKSiteDataObject.m @@ -0,0 +1,45 @@ +#import +#import + +@interface MWKSiteDataObject () + +@property (readwrite, strong, nonatomic) NSURL *url; + +@end + +@implementation MWKSiteDataObject + +- (instancetype)initWithURL:(NSURL *)url { + NSParameterAssert(url); + self = [super init]; + if (self) { + self.url = url; + } + return self; +} + +#pragma mark - title methods + +- (NSURL *)optionalURL:(NSString *)key dict:(NSDictionary *)dict { + if ([dict[key] isKindOfClass:[NSNumber class]] && ![dict[key] boolValue]) { + // false sometimes happens. Thanks PHP and weak typing! + return nil; + } + NSString *str = [self optionalString:key dict:dict]; + if (str == nil || str.length == 0) { + return nil; + } else { + return [self.url wmf_URLWithTitle:str]; + } +} + +- (NSURL *)requiredURL:(NSString *)key dict:(NSDictionary *)dict { + return [self requiredURL:key dict:dict allowEmpty:YES]; +} + +- (NSURL *)requiredURL:(NSString *)key dict:(NSDictionary *)dict allowEmpty:(BOOL)allowEmpty { + NSString *str = [self requiredString:key dict:dict allowEmpty:allowEmpty]; + return [self.url wmf_URLWithTitle:str]; +} + +@end diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKTitleLanguageController.h b/Apps/Wikipedia/Wikipedia/Code/MWKTitleLanguageController.h new file mode 100644 index 0000000..a7636a9 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKTitleLanguageController.h @@ -0,0 +1,35 @@ +#import + +@class MWKLanguageLinkController; +@class MWKLanguageLink; + +NS_ASSUME_NONNULL_BEGIN + +@interface MWKTitleLanguageController : NSObject + +- (instancetype)initWithArticleURL:(NSURL *)url languageController:(MWKLanguageLinkController *)controller; + +@property (copy, nonatomic, readonly) NSURL *articleURL; +@property (strong, nonatomic, readonly) MWKLanguageLinkController *languageController; + +- (void)fetchLanguagesWithSuccess:(dispatch_block_t)success + failure:(void (^__nullable)(NSError *__nonnull))failure; + +/** + * Returns all languages of the receiver, with preferred languages listed first. + */ +@property (readonly, copy, nonatomic) NSArray *allLanguages; + +/** + * Returns the user's preferred languages. + * Preferred languages will always contain the user's OS preferred languages, even if they are removed. + */ +@property (readonly, copy, nonatomic) NSArray *preferredLanguages; + +/** + * All the languages in the receiver minus @c preferredLanguages. + */ +@property (readonly, copy, nonatomic) NSArray *otherLanguages; +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MWKTitleLanguageController.m b/Apps/Wikipedia/Wikipedia/Code/MWKTitleLanguageController.m new file mode 100644 index 0000000..bbb1896 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MWKTitleLanguageController.m @@ -0,0 +1,95 @@ +#import "MWKTitleLanguageController.h" +@import WMF; +#import "MWKLanguageLinkFetcher.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MWKTitleLanguageController () + +@property (copy, nonatomic, readwrite) NSURL *articleURL; +@property (strong, nonatomic, readwrite) MWKLanguageLinkController *languageController; +@property (strong, nonatomic) MWKLanguageLinkFetcher *fetcher; +@property (copy, nonatomic) NSArray *availableLanguages; +@property (readwrite, copy, nonatomic) NSArray *allLanguages; +@property (readwrite, copy, nonatomic) NSArray *preferredLanguages; +@property (readwrite, copy, nonatomic) NSArray *otherLanguages; + +@end + +@implementation MWKTitleLanguageController + +- (instancetype)initWithArticleURL:(NSURL *)url languageController:(MWKLanguageLinkController *)controller { + self = [super init]; + if (self) { + self.articleURL = url; + self.languageController = controller; + } + return self; +} + +- (MWKLanguageLinkFetcher *)fetcher { + if (!_fetcher) { + _fetcher = [[MWKLanguageLinkFetcher alloc] init]; + } + return _fetcher; +} + +- (void)fetchLanguagesWithSuccess:(dispatch_block_t)success + failure:(void (^__nullable)(NSError *__nonnull))failure { + [self.fetcher fetchLanguageLinksForArticleURL:self.articleURL + success:^(NSArray *languageLinks) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSArray *adjustedLinks = [self.languageController articleLanguageLinksWithVariantsFromArticleURL:self.articleURL articleLanguageLinks:languageLinks]; + self.availableLanguages = adjustedLinks; + if (success) { + success(); + } + }); + } + failure:^(NSError *_Nonnull error) { + dispatch_async(dispatch_get_main_queue(), ^{ + failure(error); + }); + }]; +} + +- (void)setAvailableLanguages:(NSArray *)availableLanguages { + _availableLanguages = availableLanguages; + [self updateLanguageArrays]; +} + +- (void)updateLanguageArrays { + self.otherLanguages = [[self.languageController.otherLanguages wmf_select:^BOOL(MWKLanguageLink *language) { + return [self languageIsAvailable:language]; + }] wmf_map:^id(MWKLanguageLink *language) { + return [self titleLanguageForLanguage:language]; + }]; + + self.preferredLanguages = [[self.languageController.preferredLanguages wmf_select:^BOOL(MWKLanguageLink *language) { + return [self languageIsAvailable:language]; + }] wmf_map:^id(MWKLanguageLink *language) { + return [self titleLanguageForLanguage:language]; + }]; + + self.allLanguages = [[self.languageController.allLanguages wmf_select:^BOOL(MWKLanguageLink *language) { + return [self languageIsAvailable:language]; + }] wmf_map:^id(MWKLanguageLink *language) { + return [self titleLanguageForLanguage:language]; + }]; + + [[NSNotificationCenter defaultCenter] postNotificationName:MWKLanguageFilterDataSourceLanguagesDidChangeNotification object: self]; +} + +- (nullable MWKLanguageLink *)titleLanguageForLanguage:(MWKLanguageLink *)language { + return [self.availableLanguages wmf_match:^BOOL(MWKLanguageLink *availableLanguage) { + return [language.contentLanguageCode isEqualToString:availableLanguage.contentLanguageCode]; + }]; +} + +- (BOOL)languageIsAvailable:(MWKLanguageLink *)language { + return [self titleLanguageForLanguage:language] != nil; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apps/Wikipedia/Wikipedia/Code/MapAnnotation.swift b/Apps/Wikipedia/Wikipedia/Code/MapAnnotation.swift new file mode 100644 index 0000000..9b30891 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MapAnnotation.swift @@ -0,0 +1,31 @@ +import MapKit + +public class MapAnnotation: NSObject, MKAnnotation { + public dynamic var coordinate: CLLocationCoordinate2D + + init?(coordinate: CLLocationCoordinate2D) { + self.coordinate = coordinate + super.init() + setup() + } + + open func setup() { + } +} + +public class MapAnnotationView: MKAnnotationView { + var isSetup = false + + override init(annotation: MKAnnotation?, reuseIdentifier: String?) { + super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) + setup() + } + + required public init?(coder aDecoder: NSCoder) { + return nil + } + + open func setup() { + isSetup = true + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/MapUtilities.swift b/Apps/Wikipedia/Wikipedia/Code/MapUtilities.swift new file mode 100644 index 0000000..7ca932a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MapUtilities.swift @@ -0,0 +1,32 @@ +import MapKit + +extension Array where Element == CLLocationCoordinate2D { + func wmf_boundingRegion(with boundingMetersPerPoint: Double) -> MKCoordinateRegion { + var rect: MKMapRect? + for coordinate in self { + let point = MKMapPoint(coordinate) + let mapPointsPerMeter = MKMapPointsPerMeterAtLatitude(coordinate.latitude) + let dimension = mapPointsPerMeter * boundingMetersPerPoint + let size = MKMapSize(width: dimension, height: dimension) + let coordinateRect = MKMapRect(origin: MKMapPoint(x: point.x - 0.5*dimension, y: point.y - 0.5*dimension), size: size) + guard let currentRect = rect else { + rect = coordinateRect + continue + } + rect = currentRect.union(coordinateRect) + } + + guard let finalRect = rect else { + return MKCoordinateRegion() + } + + var region = MKCoordinateRegion(finalRect) + if region.span.latitudeDelta < 0.01 { + region.span.latitudeDelta = 0.01 + } + if region.span.longitudeDelta < 0.01 { + region.span.longitudeDelta = 0.01 + } + return region + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/MapView.swift b/Apps/Wikipedia/Wikipedia/Code/MapView.swift new file mode 100644 index 0000000..f8e4ded --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MapView.swift @@ -0,0 +1,7 @@ +import MapKit + +class MapView: MKMapView { + var visibleAnnotations: Set { + return annotations(in: visibleMapRect) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/MediaList.swift b/Apps/Wikipedia/Wikipedia/Code/MediaList.swift new file mode 100644 index 0000000..2aaed9b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MediaList.swift @@ -0,0 +1,81 @@ +public struct MediaListItemSource: Codable { + public let urlString: String + public let scale: String + + enum CodingKeys: String, CodingKey { + case urlString = "src" + case scale + } + + public init (urlString: String, scale: String) { + self.urlString = urlString + self.scale = scale + } +} + +public enum MediaListItemType: String { + case image + case audio + case video +} + +public struct MediaListItem: Codable { + public let title: String? + public let sectionID: Int + public let type: String + public let showInGallery: Bool + public let isLeadImage: Bool + public let sources: [MediaListItemSource]? + public let audioType: String? + enum CodingKeys: String, CodingKey { + case title + case sectionID = "section_id" + case showInGallery + case isLeadImage = "leadImage" + case sources = "srcset" + case type + case audioType + } + + public init(title: String?, sectionID: Int, type: String, showInGallery: Bool, isLeadImage: Bool, sources: [MediaListItemSource]?, audioType: String? = nil) { + self.title = title + self.sectionID = sectionID + self.type = type + self.showInGallery = showInGallery + self.isLeadImage = isLeadImage + self.sources = sources + self.audioType = audioType + } +} + +extension MediaListItem { + public var itemType: MediaListItemType? { + return MediaListItemType(rawValue: type) + } +} + +public struct MediaList: Codable { + public let items: [MediaListItem] + + public init(items: [MediaListItem]) { + self.items = items + } + + // This failable initializer is used for a single-image MediaList, given via a URL. + public init?(from url: URL?) { + guard let imageURL = url, + let imageName = WMFParseUnescapedNormalizedImageNameFromSourceURL(imageURL) else { + return nil + } + let filename = "File:" + imageName + let urlString = imageURL.absoluteString + let sources: [MediaListItemSource] = [ + MediaListItemSource(urlString: urlString, scale: "1x"), + MediaListItemSource(urlString: urlString, scale: "2x"), + MediaListItemSource(urlString: urlString, scale: "1.5x") + ] + + let mediaListItem = MediaListItem(title: filename, sectionID: 0, type: "image", showInGallery: true, isLeadImage: true, sources: sources, audioType: nil) + self = MediaList(items: [mediaListItem]) + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/MediaListGalleryViewController.swift b/Apps/Wikipedia/Wikipedia/Code/MediaListGalleryViewController.swift new file mode 100644 index 0000000..c7e5334 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MediaListGalleryViewController.swift @@ -0,0 +1,130 @@ +class MediaListGalleryViewController: WMFImageGalleryViewController { + let imageController: ImageCacheController + let imageInfoFetcher: MWKImageInfoFetcher + let articleURL: URL + required init(articleURL: URL, mediaList: MediaList, dataStore: MWKDataStore, initialItem: MediaListItem?, theme: Theme, overlayViewTopBarHidden: Bool = false) { + self.articleURL = articleURL + let photos = mediaList.items.filter { $0.showInGallery }.compactMap { MediaListItemNYTPhotoWrapper($0) } + let initialPhoto: WMFPhoto? + if let initialItem = initialItem { + initialPhoto = photos.first { $0.mediaListItem.title == initialItem.title } + } else { + initialPhoto = photos.first + } + imageInfoFetcher = MWKImageInfoFetcher(dataStore: dataStore) + imageController = dataStore.cacheController.imageCache + super.init(photos: photos, initialPhoto: initialPhoto, delegate: nil, theme: theme, overlayViewTopBarHidden:overlayViewTopBarHidden) + fetchImageForPhoto(initialPhoto) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + var imageInfos: [String: MWKImageInfo] = [:] + func fetchImageInfoForTitle(_ title: String, completion: @escaping (Result) -> Void) { + assert(Thread.isMainThread, "Protect accesss to imageInfos") + // If we have the cached version, return it + if let info = imageInfos[title] { + completion(.success(info)) + return + } + // Otherwise fetch it and cache it + imageInfoFetcher.fetchGalleryInfo(forImageFiles: [title], fromSiteURL: articleURL, success: { (info) in + DispatchQueue.main.async { + guard let info = info.first as? MWKImageInfo else { + completion(.failure(RequestError.unexpectedResponse)) + return + } + self.imageInfos[title] = info + completion(.success(info)) + } + }) { (error) in + DispatchQueue.main.async { + completion(.failure(error)) + } + } + } + + func fetchImageForPhoto(_ photo: NYTPhoto?) { + guard + let photo = photo as? MediaListItemNYTPhotoWrapper, + let title = photo.mediaListItem.title + else { + return + } + + fetchImageInfoForTitle(title) { (result) in + switch result { + case .failure(let error): + self.wmf_showAlertWithError(error as NSError) + case .success(let imageInfo): + self.fetchImageForPhoto(photo, imageInfo: imageInfo) + } + } + } + + func fetchImageForPhoto(_ photo: MediaListItemNYTPhotoWrapper, imageInfo: MWKImageInfo) { + if photo.imageInfo == nil { + // Set the image info on the photo object + // And update the overlay info so the caption is shown + photo.imageInfo = imageInfo + updateOverlayInformation() + } + // Gallery image width is based on the trait collection + let width = traitCollection.wmf_galleryImageWidth + guard let imageURL = imageInfo.imageURL(forTargetWidth: width) else { + self.wmf_showAlertWithError(RequestError.unexpectedResponse as NSError) + return + } + + imageController.fetchImage(withURL: imageURL, failure: { (error) in + DispatchQueue.main.async { + self.wmf_showAlertWithError(error as NSError) + } + }) { [weak self] (download) in + DispatchQueue.main.async { + if let animatedImage = download.image.animatedImage { + photo.imageData = animatedImage.data + } else { + photo.image = download.image.staticImage + } + self?.updateImageForPhoto(afterUserInteractionIsFinished: photo) + } + } + } + + override func photosViewController(_ photosViewController: NYTPhotosViewController, didNavigateTo photo: NYTPhoto, at photoIndex: UInt) { + fetchImageForPhoto(photo) + } +} + +// Model object for the NYTGalleryViewController +// Holds state for the gallery view +class MediaListItemNYTPhotoWrapper: NSObject, WMFPhoto { + func bestImageURL() -> URL? { + return nil + } + + func bestImageInfo() -> MWKImageInfo? { + return imageInfo + } + + var image: UIImage? + var imageData: Data? + var imageDataUTType: String? + var placeholderImage: UIImage? + var attributedCaptionTitle: NSAttributedString? + var attributedCaptionSummary: NSAttributedString? + var attributedCaptionCredit: NSAttributedString? + + let mediaListItem: MediaListItem + var imageInfo: MWKImageInfo? + + init?(_ mediaListItem: MediaListItem?) { + guard let mediaListItem = mediaListItem, mediaListItem.type == "image" else { + return nil + } + self.mediaListItem = mediaListItem + } +} diff --git a/Apps/Wikipedia/Wikipedia/Code/MobileviewToMobileHTMLConverter.swift b/Apps/Wikipedia/Wikipedia/Code/MobileviewToMobileHTMLConverter.swift new file mode 100644 index 0000000..77d2195 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/Code/MobileviewToMobileHTMLConverter.swift @@ -0,0 +1,83 @@ +import WebKit + +@objc public class MobileviewToMobileHTMLConverter : NSObject, WKNavigationDelegate { + private var isConverterLoaded = false + lazy private var conversionBuffer: [() -> Void] = [] + + // The 'domain' and 'baseURI' parameters are used by the mobileview-to-mobilehtml converter + // to create + + + + +
    wikipedia
    +
    version
    +
    contributors_title
    +
    contributors_body
    +
    translators_title
    +
    translators_body
    +
    testers_title
    +
    testers_body
    +
    libraries_title
    +
    libraries_body
    +
    repositories_title
    +
    repositories_body
    +
    repositories_subtitle
    +
    license_title
    +
    license_body
    +
    places_maps_license_title
    +
    places_maps_license_body
    + + + + + + + + + + + \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/about.js b/Apps/Wikipedia/Wikipedia/assets/about.js new file mode 100644 index 0000000..000ccb3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/about.js @@ -0,0 +1,29 @@ +const wmf = {} + +wmf.applyDarkThemeLogo = () => { + hideElementById('dark-logo') + showElementById('light-logo') +} + +wmf.applyLightThemeLogo = () => { + hideElementById('light-logo') + showElementById('dark-logo') +} + +const showElementById = id => { + show(document.getElementById(id)) +} + +const hideElementById = id => { + hide(document.getElementById(id)) +} + +const show = element => { + element.style.display = '' +} + +const hide = element => { + element.style.display = 'none' +} + +window.wmf = wmf \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-black.css b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-black.css new file mode 100644 index 0000000..018e80d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-black.css @@ -0,0 +1,95 @@ +.cm-mw-black, .cm-mw-black .CodeMirror { + background-color: #000; + color: #FFF; +} + +.cm-mw-black .CodeMirror-gutters { + background-color: #000; + border-right: 4px solid #000; +} + +.cm-mw-black .CodeMirror-linenumber { + color: #C8CCD1; + font-size: 0.8em; +} + +.cm-mw-black .cm-mw-template { + color: #FFF; /* plain text color */ +} + +.cm-mw-black .cm-mw-link, +.cm-mw-black .cm-mw-link-pagename, +.cm-mw-black .cm-mw-link-tosection, +.cm-mw-black .cm-mw-link-bracket, +.cm-mw-black .cm-mw-link-delimiter, +.cm-mw-black .cm-mw-extlink, +.cm-mw-black .cm-mw-free-extlink, +.cm-mw-black .cm-mw-extlink-protocol, +.cm-mw-black .cm-mw-free-extlink-protocol, +.cm-mw-black .cm-mw-extlink-bracket, +.cm-mw-black .cm-mw-doubleUnderscore, +.cm-mw-black .cm-mw-signature, +.cm-mw-black .cm-mw-hr { + color: #6699FF; + font-weight: normal; +} + +.cm-mw-black .cm-mw-mnemonic, +.cm-mw-black .cm-mw-exttag-name, +.cm-mw-black .cm-mw-exttag-bracket, +.cm-mw-black .cm-mw-exttag-attribute, +.cm-mw-black .cm-mw-htmltag-name, +.cm-mw-black .cm-mw-htmltag-bracket, +.cm-mw-black .cm-mw-htmltag-attribute { + color: #00AF89; + font-weight: normal; +} + +.cm-mw-black .cm-mw-parserfunction-name, +.cm-mw-black .cm-mw-parserfunction-bracket, +.cm-mw-black .cm-mw-parserfunction-delimiter { + color: #FF6E6E; +} + +.cm-mw-black .cm-mw-table-bracket, +.cm-mw-black .cm-mw-table-delimiter, +.cm-mw-black .cm-mw-table-definition { + color: #F06695; + font-weight: normal; +} + +.cm-mw-black .cm-mw-template-name, +.cm-mw-black .cm-mw-template-argument-name, +.cm-mw-black .cm-mw-template-delimiter, +.cm-mw-black .cm-mw-template-bracket { + color: #C180BB; + font-weight: normal; +} + +.cm-mw-black .cm-mw-list, +.cm-mw-black .cm-mw-doubleUnderscore, +.cm-mw-black .cm-mw-signature, +.cm-mw-black .cm-mw-hr, +.cm-mw-black .cm-mw-indenting, +.cm-mw-black .cm-mw-apostrophes-bold, +.cm-mw-black .cm-mw-apostrophes-italic, +.cm-mw-black .cm-mw-section-header, +.cm-mw-black .cm-mw-templatevariable, +.cm-mw-black .cm-mw-templatevariable-name, +.cm-mw-black .cm-mw-templatevariable-bracket, +.cm-mw-black .cm-mw-templatevariable-delimiter, +.cm-mw-black .cm-mw-matching { + color: #FF9500; + font-weight: normal; +} + +.cm-mw-black .cm-mw-comment, +.cm-mw-black .cm-mw-skipformatting { + color: #C8CCD1; +} + +.cm-mw-black .cm-searching { + color: #000; + background-color: rgba(247, 215, 121, 0.7); + border-radius: 2px; +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-common.css b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-common.css new file mode 100644 index 0000000..243f433 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-common.css @@ -0,0 +1,63 @@ +body { + margin: 0; +} + +.CodeMirror { + height: 100%; + width: 100%; + box-sizing: border-box; + font-family: -apple-system, sans-serif; + line-height: 1.75em; +} + +.cm-mw-table-caption { + font-weight: normal; +} + +.cm-mw-template2-ground, +.cm-mw-template3-ground, +.cm-mw-template-ext-ground, +.cm-mw-template-ext2-ground, +.cm-mw-template-ext3-ground, +.cm-mw-template-link-ground, +.cm-mw-template-ext-link-ground, +.cm-mw-template-ext2-link-ground, +.cm-mw-template-ext3-link-ground, +.cm-mw-template2-ext-ground, +.cm-mw-template2-ext2-ground, +.cm-mw-template2-ext3-ground, +.cm-mw-template2-link-ground, +.cm-mw-template2-ext-link-ground, +.cm-mw-template2-ext2-link-ground, +.cm-mw-template2-ext3-link-ground, +.cm-mw-template3-ext-ground, +.cm-mw-template3-ext2-ground, +.cm-mw-template3-ext3-ground, +.cm-mw-template3-link-ground, +.cm-mw-template3-ext-link-ground, +.cm-mw-template3-ext2-link-ground, +.cm-mw-template3-ext3-link-ground, +.cm-mw-ext-ground, +.cm-mw-ext2-ground, +.cm-mw-ext3-ground, +.cm-mw-ext-link-ground, +.cm-mw-ext2-link-ground, +.cm-mw-ext3-link-ground, +.cm-mw-link-ground { + background-image: none; + background-color: none; +} + +.cm-searching-focus { + background-color: #fc3 !important; +} + +.cm-mw-skipformatting { + background-color: unset !important; +} + +.cm-searching-replaced { + color: #000 !important; + background-color: #d0e4fc !important; + border-radius: 2px; +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-dark.css b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-dark.css new file mode 100644 index 0000000..6eb9bf4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-dark.css @@ -0,0 +1,95 @@ +.cm-mw-dark, .cm-mw-dark .CodeMirror { + background-color: #2E3136; + color: #FFF; +} + +.cm-mw-dark .CodeMirror-gutters { + background-color: #2E3136; + border-right: 4px solid #2E3136; +} + +.cm-mw-dark .CodeMirror-linenumber { + color: #C8CCD1; + font-size: 0.8em; +} + +.cm-mw-dark .cm-mw-template { + color: #FFF; /* plain text color */ +} + +.cm-mw-dark .cm-mw-link, +.cm-mw-dark .cm-mw-link-pagename, +.cm-mw-dark .cm-mw-link-tosection, +.cm-mw-dark .cm-mw-link-bracket, +.cm-mw-dark .cm-mw-link-delimiter, +.cm-mw-dark .cm-mw-extlink, +.cm-mw-dark .cm-mw-free-extlink, +.cm-mw-dark .cm-mw-extlink-protocol, +.cm-mw-dark .cm-mw-free-extlink-protocol, +.cm-mw-dark .cm-mw-extlink-bracket, +.cm-mw-dark .cm-mw-doubleUnderscore, +.cm-mw-dark .cm-mw-signature, +.cm-mw-dark .cm-mw-hr { + color: #6699FF; + font-weight: normal; +} + +.cm-mw-dark .cm-mw-mnemonic, +.cm-mw-dark .cm-mw-exttag-name, +.cm-mw-dark .cm-mw-exttag-bracket, +.cm-mw-dark .cm-mw-exttag-attribute, +.cm-mw-dark .cm-mw-htmltag-name, +.cm-mw-dark .cm-mw-htmltag-bracket, +.cm-mw-dark .cm-mw-htmltag-attribute { + color: #00AF89; + font-weight: normal; +} + +.cm-mw-dark .cm-mw-parserfunction-name, +.cm-mw-dark .cm-mw-parserfunction-bracket, +.cm-mw-dark .cm-mw-parserfunction-delimiter { + color: #FF6E6E; +} + +.cm-mw-dark .cm-mw-table-bracket, +.cm-mw-dark .cm-mw-table-delimiter, +.cm-mw-dark .cm-mw-table-definition { + color: #F06695; + font-weight: normal; +} + +.cm-mw-dark .cm-mw-template-name, +.cm-mw-dark .cm-mw-template-argument-name, +.cm-mw-dark .cm-mw-template-delimiter, +.cm-mw-dark .cm-mw-template-bracket { + color: #C180BB; + font-weight: normal; +} + +.cm-mw-dark .cm-mw-list, +.cm-mw-dark .cm-mw-doubleUnderscore, +.cm-mw-dark .cm-mw-signature, +.cm-mw-dark .cm-mw-hr, +.cm-mw-dark .cm-mw-indenting, +.cm-mw-dark .cm-mw-apostrophes-bold, +.cm-mw-dark .cm-mw-apostrophes-italic, +.cm-mw-dark .cm-mw-section-header, +.cm-mw-dark .cm-mw-templatevariable, +.cm-mw-dark .cm-mw-templatevariable-name, +.cm-mw-dark .cm-mw-templatevariable-bracket, +.cm-mw-dark .cm-mw-templatevariable-delimiter, +.cm-mw-dark .cm-mw-matching { + color: #FF9500; + font-weight: normal; +} + +.cm-mw-dark .cm-mw-comment, +.cm-mw-dark .cm-mw-skipformatting { + color: #C8CCD1; +} + +.cm-mw-dark .cm-searching { + color: #000; + background-color: rgba(247, 215, 121, 0.7); + border-radius: 2px; +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-editTextSelection.js b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-editTextSelection.js new file mode 100644 index 0000000..8f33755 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-editTextSelection.js @@ -0,0 +1,92 @@ + +// Reminder: after we start using broswerify for code mirror bits DRY this up with the `SelectedAndAdjacentText` class in `editTextSelection.js` +class SelectedAndAdjacentText { + constructor(selectedText, textBeforeSelectedText, textAfterSelectedText) { + this.selectedText = selectedText + this.textBeforeSelectedText = textBeforeSelectedText + this.textAfterSelectedText = textAfterSelectedText + } +} + +const getNumberOfWordsFromBeginningOfString = (string, wordCount) => string.split(' ').slice(0, wordCount) +const getNumberOfWordsFromEndOfString = (string, wordCount) => string.split(' ').slice(-wordCount) + +const calculateScore = (wordsFromHTML, wordsFromWikitext) => wordsFromHTML.reduce((score, htmlWord, htmlWordIndex) => { + const indexOfHTMLWordInWikitextWords = wordsFromWikitext.indexOf(htmlWord); + const distance = indexOfHTMLWordInWikitextWords - htmlWordIndex; + const wordScore = indexOfHTMLWordInWikitextWords === -1 ? 0 : (wordsFromWikitext.length - htmlWordIndex - distance); + return score + wordScore +}, 0) + +const wordsOnly = string => string.replace(/\(.*?\)/g, '').replace(/{{.*}}/g, '').replace(/\W+/g, ' ').trim() +const adjacentComparisonWordCount = 6 +const adjacentCharsToGather = 200 + +const scoreForMatch = (match, lastIndex, wordsBeforeFromHTML, wordsAfterFromHTML) => { + const wordsBeforeFromWikitext = getNumberOfWordsFromEndOfString(wordsOnly(match.input.substring(Math.max(0, match.index - adjacentCharsToGather), match.index)), adjacentComparisonWordCount) + const wordsAfterFromWikitext = getNumberOfWordsFromBeginningOfString(wordsOnly(match.input.substring(lastIndex, lastIndex + adjacentCharsToGather)), adjacentComparisonWordCount) + const wordsAfterScore = calculateScore(wordsAfterFromHTML, wordsAfterFromWikitext) + const wordsBeforeScore = calculateScore(wordsBeforeFromHTML.slice().reverse(), wordsBeforeFromWikitext.slice().reverse()) + return wordsBeforeScore + wordsAfterScore +} + +const wikitextRangeForSelectedAndAdjacentText = (selectedAndAdjacentText, wikitext) => { + const wordsBeforeFromHTML = getNumberOfWordsFromEndOfString(selectedAndAdjacentText.textBeforeSelectedText, adjacentComparisonWordCount) + const wordsAfterFromHTML = getNumberOfWordsFromBeginningOfString(selectedAndAdjacentText.textAfterSelectedText, adjacentComparisonWordCount) + const selectedTextRegexPattern = `(${selectedAndAdjacentText.selectedText.replace(/\s+/g, '(?:(?:\\[\\[[^\\]\\|]+\\|)|{{[^}]*}}|<[^>]*>|\\W)+')})` + const selectedTextRegex = new RegExp(selectedTextRegexPattern, 'gs') + + let bestScoredMatch = null + while ((match = selectedTextRegex.exec(wikitext)) !== null) { + const thisMatchScore = scoreForMatch(match, selectedTextRegex.lastIndex, wordsBeforeFromHTML, wordsAfterFromHTML) + if (bestScoredMatch === null || thisMatchScore > bestScoredMatch.score) { + bestScoredMatch = { + match, + score: thisMatchScore, + matchedWikitextBeforeSelection: {start: 0, end: match.index}, + matchedWikitextSelection: {start: match.index, end: selectedTextRegex.lastIndex} + } + } + } + + if (bestScoredMatch === null) { + return null + } + + const matchedWikitextBeforeSelection = bestScoredMatch.match.input.substring(bestScoredMatch.matchedWikitextBeforeSelection.start, bestScoredMatch.matchedWikitextBeforeSelection.end) + const matchedWikitextSelection = bestScoredMatch.match.input.substring(bestScoredMatch.matchedWikitextSelection.start, bestScoredMatch.matchedWikitextSelection.end) + + return getWikitextRangeToSelect(matchedWikitextBeforeSelection, matchedWikitextSelection) +} + +const getWikitextRangeToSelect = (wikitextBeforeSelection, wikitextSelection) => { + const wikitextBeforeSelectionLines = wikitextBeforeSelection.split('\n') + const startLine = wikitextBeforeSelectionLines.length - 1 + const startCh = wikitextBeforeSelectionLines.pop().length + + const wikitextSelectionLines = wikitextSelection.split('\n') + const endLine = startLine + wikitextSelectionLines.length - 1 + const endCh = wikitextSelectionLines.pop().length + (startLine === endLine ? startCh : 0) + + let from = {line: startLine, ch: startCh} + let to = {line: endLine, ch: endCh} + + return {from, to} +} + +const highlightAndScrollToWikitextForSelectedAndAdjacentText = (selectedText, textBeforeSelectedText, textAfterSelectedText) => { + const throwError = () => {throw('Could not determine range to highlight')} // The message doesn't matter here. It's not displayed. + if (selectedText.trim().length === 0) { + throwError() + } + const selectedAndAdjacentText = new SelectedAndAdjacentText(selectedText, textBeforeSelectedText, textAfterSelectedText) + const wikitext = editor.getValue() + const rangeToHighlight = wikitextRangeForSelectedAndAdjacentText(selectedAndAdjacentText, wikitext) + if (rangeToHighlight === null) { + throwError() + } + + // Calling `setSelection` triggers our codemirror `cursorActivity` event, which will call our + // scrolling code as needed. + editor.setSelection(rangeToHighlight.from, rangeToHighlight.to, {scroll: false}) +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-index.html b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-index.html new file mode 100644 index 0000000..e5591ab --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-index.html @@ -0,0 +1,1375 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-light.css b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-light.css new file mode 100644 index 0000000..8b24eba --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-light.css @@ -0,0 +1,95 @@ +.cm-mw-light, .cm-mw-light .CodeMirror { + background-color: #FFF; + color: #222; +} + +.cm-mw-light .CodeMirror-gutters { + background-color: #FFF; + border-right: 4px solid #FFF; +} + +.cm-mw-light .CodeMirror-linenumber { + color: #72777D; + font-size: 0.8em; +} + +.cm-mw-light .cm-mw-template { + color: #222; /* plain text color */ +} + +.cm-mw-light .cm-mw-link, +.cm-mw-light .cm-mw-link-pagename, +.cm-mw-light .cm-mw-link-tosection, +.cm-mw-light .cm-mw-link-bracket, +.cm-mw-light .cm-mw-link-delimiter, +.cm-mw-light .cm-mw-extlink, +.cm-mw-light .cm-mw-free-extlink, +.cm-mw-light .cm-mw-extlink-protocol, +.cm-mw-light .cm-mw-free-extlink-protocol, +.cm-mw-light .cm-mw-extlink-bracket, +.cm-mw-light .cm-mw-doubleUnderscore, +.cm-mw-light .cm-mw-signature, +.cm-mw-light .cm-mw-hr { + color: #36C; + font-weight: normal; +} + +.cm-mw-light .cm-mw-mnemonic, +.cm-mw-light .cm-mw-exttag-name, +.cm-mw-light .cm-mw-exttag-bracket, +.cm-mw-light .cm-mw-exttag-attribute, +.cm-mw-light .cm-mw-htmltag-name, +.cm-mw-light .cm-mw-htmltag-bracket, +.cm-mw-light .cm-mw-htmltag-attribute { + color: #00AF89; + font-weight: normal; +} + +.cm-mw-light .cm-mw-parserfunction-name, +.cm-mw-light .cm-mw-parserfunction-bracket, +.cm-mw-light .cm-mw-parserfunction-delimiter { + color: #B32424; +} + +.cm-mw-light .cm-mw-table-bracket, +.cm-mw-light .cm-mw-table-delimiter, +.cm-mw-light .cm-mw-table-definition { + color: #E6275d; + font-weight: normal; +} + +.cm-mw-light .cm-mw-template-name, +.cm-mw-light .cm-mw-template-argument-name, +.cm-mw-light .cm-mw-template-delimiter, +.cm-mw-light .cm-mw-template-bracket { + color: #97498F; + font-weight: normal; +} + +.cm-mw-light .cm-mw-list, +.cm-mw-light .cm-mw-doubleUnderscore, +.cm-mw-light .cm-mw-signature, +.cm-mw-light .cm-mw-hr, +.cm-mw-light .cm-mw-indenting, +.cm-mw-light .cm-mw-apostrophes-bold, +.cm-mw-light .cm-mw-apostrophes-italic, +.cm-mw-light .cm-mw-section-header, +.cm-mw-light .cm-mw-templatevariable, +.cm-mw-light .cm-mw-templatevariable-name, +.cm-mw-light .cm-mw-templatevariable-bracket, +.cm-mw-light .cm-mw-templatevariable-delimiter, +.cm-mw-light .cm-mw-matching { + color: #FF9500; + font-weight: normal; +} + +.cm-mw-light .cm-mw-comment, +.cm-mw-light .cm-mw-skipformatting { + color: #72777D; +} + +.cm-mw-light .cm-searching { + color: #000; + background-color: rgba(255, 204, 51, 0.3); + border-radius: 2px; +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-range-determination-bundle.js b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-range-determination-bundle.js new file mode 100644 index 0000000..ede8fe6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-range-determination-bundle.js @@ -0,0 +1,816 @@ +//*****BEGIN: codemirror-range-objects.js + +class MarkupItem { + constructor(type, innerRange, outerRange) { + this.type = type + this.innerRange = innerRange + this.outerRange = outerRange + this.buttonName = MarkupItem.buttonNameForType(type) + } + isComplete() { + return this.innerRange.isComplete() && this.outerRange.isComplete() + } + static buttonNameForType(type) { + if (type === 'mw-apostrophes-bold') { + return 'bold' + } + if (type === 'mw-section-header') { + return 'header' + } + if (type === 'mw-link-bracket') { + return 'link' + } + if (type === 'mw-template-bracket') { + return 'template' + } + if (type === 'mw-template-argument-name') { + return 'template-argument' + } + if (type === 'mw-template-name') { + return 'template-name' + } + if (type == 'mw-template-delimiter') { + return 'template-delimiter' + } + if (type === 'mw-apostrophes-italic') { + return 'italic' + } + if (type === 'ref') { + return 'reference' + } + return type + } + + openingMarkupRange() { + return new ItemRange( + this.outerRange.startLocation, + this.innerRange.startLocation + ) + } + + closingMarkupRange() { + return new ItemRange( + this.innerRange.endLocation, + this.outerRange.endLocation + ) + } + + innerRangeStartsOrEndsInRange(range, allowEdgeOverlap = false) { + return this.innerRange.startsInsideRange(range, allowEdgeOverlap) || this.innerRange.endsInsideRange(range, allowEdgeOverlap) + } +} + +class ItemRange { + constructor(startLocation, endLocation) { + this.startLocation = startLocation + this.endLocation = endLocation + } + isComplete() { + return this.startLocation.isComplete() && this.endLocation.isComplete() + } + + endsInsideRange(range, allowEdgeOverlap = false) { + return allowEdgeOverlap ? + this.endLocation.greaterThanOrEquals(range.startLocation) && this.endLocation.lessThanOrEquals(range.endLocation) : + this.endLocation.greaterThan(range.startLocation) && this.endLocation.lessThan(range.endLocation) + } + + startsInsideRange(range, allowEdgeOverlap = false) { + return allowEdgeOverlap ? + this.startLocation.greaterThanOrEquals(range.startLocation) && this.startLocation.lessThanOrEquals(range.endLocation) : + this.startLocation.greaterThan(range.startLocation) && this.startLocation.lessThan(range.endLocation) + } + + intersectsRange(range, allowEdgeOverlap = false) { + return ( + this.endsInsideRange(range, allowEdgeOverlap) + || + this.startsInsideRange(range, allowEdgeOverlap) + || + range.endsInsideRange(this, allowEdgeOverlap) + || + range.startsInsideRange(this, allowEdgeOverlap) + ) + } + + isZeroLength() { + return this.startLocation.line === this.endLocation.line && this.startLocation.ch === this.endLocation.ch + } + + lineNumbers() { + const startLine = this.startLocation.line + const endLine = this.endLocation.line + return new Array(endLine - startLine + 1).fill().map((d, i) => i + startLine) + } +} + +class ItemLocation { + constructor(line, ch) { + this.line = line + this.ch = ch + } + isComplete() { + return this.line !== -1 && this.ch !== -1 + } + greaterThan(location) { + if (this.line < location.line) { + return false + } + if (this.line === location.line && this.ch <= location.ch) { + return false + } + return true + } + lessThan(location) { + if (this.line > location.line) { + return false + } + if (this.line === location.line && this.ch >= location.ch) { + return false + } + return true + } + equals(location) { + return (this.line === location.line && this.ch === location.ch) + } + greaterThanOrEquals(location) { + return this.greaterThan(location) || this.equals(location) + } + lessThanOrEquals(location) { + return this.lessThan(location) || this.equals(location) + } + withOffset(line, ch) { + return new ItemLocation(this.line + line, this.ch + ch) + } +} + +//*****END: codemirror-range-objects.js + +//*****BEGIN: codemirror-range-utilities.js + +const getItemRangeFromSelection = (codeMirror) => { + const fromCursor = codeMirror.getCursor('from') + const toCursor = codeMirror.getCursor('to') + const fromLocation = new ItemLocation(fromCursor.line, fromCursor.ch) + const toLocation = new ItemLocation(toCursor.line, toCursor.ch) + const selectionRange = new ItemRange(fromLocation, toLocation) + return selectionRange +} + +const getMarkupItemsIntersectingSelection = (codeMirror, markupItems, selectionRange) => { + const itemIntersectsSelection = item => item.outerRange.intersectsRange(selectionRange, true) + const itemDoesNotStartsAtSelectionEnd = item => !item.openingMarkupRange().startLocation.equals(selectionRange.endLocation) + const itemDoesNotEndAtSelectionStart = item => !item.closingMarkupRange().endLocation.equals(selectionRange.startLocation) + return markupItems + .filter(itemIntersectsSelection) + .filter(itemDoesNotStartsAtSelectionEnd) + .filter(itemDoesNotEndAtSelectionStart) +} + +const getButtonNamesFromMarkupItems = (markupItems) => markupItems.map(item => item.buttonName) + +/* +TODO: add generic (non-regex or otherwise string aware) functional methods here for: +- unwrapping existing markup item (i.e. turning off something like bold) + would simply look at the item's inner and outer range and replace the code mirror string from the + outer range with the code mirror string from the inner range, +- others t.b.d. - need to think a bit more about best way to implement wrapping related method(s) +*/ + +//*****END: codemirror-range-utilities.js + +//*****BEGIN: codemirror-range-set-utilities.js + +// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set + +// Returns set containing items that exist in both sets. +const intersection = (a, b) => new Set([...a].filter(x => b.has(x))) + +// Returns set containing items that exist only in the first set and not in both sets. +const difference = (a, b) => new Set([...a].filter(x => !b.has(x))) + +// Returns set containing all items from both sets, without dupes. +const union = (a, b) => new Set([...a, ...b]) + +// Returns true if all items set b are in set a. +const isSuperset = (a, b) => { + for (let e of b) { + if (!a.has(e)) { + return false + } + } + return true +} + +// Returns set containing all items from both sets, except items that are present in both sets. +const symmetricDifference = (a, b) => { + let diff = new Set(a) + for (let e of b) { + if (diff.has(e)) { + diff.delete(e) + } else { + diff.add(e) + } + } + return diff +} + +//*****END: codemirror-range-set-utilities.js + +//*****BEGIN: codemirror-range-determination-non-tag.js + +// - returns set of types for token +// - smooths out inconsistent nested bold and italic types +const tokenTypes = (token) => { + const types = (token.type || '') + .trim() + .split(' ') + .filter(s => s.length > 0) + .map(s => { + // the parser fails to add 'mw-apostrophes-bold' for nested bold (it only adds 'mw-apostrophes') + if (s === 'mw-apostrophes' && token.string === `'''`) { + return 'mw-apostrophes-bold' + } + // the parser fails to add 'mw-apostrophes-italic' for nested italic (it only adds 'mw-apostrophes') + if (s === 'mw-apostrophes' && token.string === `''`) { + return 'mw-apostrophes-italic' + } + return s + }) + + return new Set(types) +} + +const nonTagMarkupItemsForLineTokens = (lineTokens, line) => { + const soughtBoundaryTokenTypes = new Set(['mw-apostrophes-bold', 'mw-apostrophes-italic', 'mw-link-bracket', 'mw-section-header', 'mw-template-bracket']) + const soughtTokenTypes = new Set(['mw-template-bracket', 'mw-template-name', 'mw-template-argument-name', 'mw-template-delimiter']) + + let trackedTypes = new Set() + let outputMarkupItems = [] + + const tokenWithEnrichedInHtmlTagArray = (token, index, tokens) => { + + const boundaryTypes = intersection(tokenTypes(token), soughtBoundaryTokenTypes) + const types = intersection(tokenTypes(token), soughtTokenTypes) + + const typesToStopTracking = Array.from(intersection(trackedTypes, boundaryTypes)) + const typesToStartTracking = Array.from(difference(boundaryTypes, trackedTypes)) + + const addMarkupItemWithRangeStarts = (type) => { + const inner = new ItemRange(new ItemLocation(line, token.end), new ItemLocation(line, -1)) + const outer = new ItemRange(new ItemLocation(line, token.start), new ItemLocation(line, -1)) + const markupItem = new MarkupItem(type, inner, outer) + outputMarkupItems.push(markupItem) + } + + const updateMarkupItemRangeEnds = (type) => { + const markupItem = outputMarkupItems.find(markupItem => { + return markupItem.type === type && !markupItem.isComplete() + }) + if (markupItem) { + markupItem.innerRange.endLocation.ch = token.start + markupItem.outerRange.endLocation.ch = token.end + } + } + + const addCompleteMarkupRanges = (type) => { + const inner = new ItemRange(new ItemLocation(line, token.start), new ItemLocation(line, token.end)) + const outer = new ItemRange(new ItemLocation(line, token.start), new ItemLocation(line, token.end)) + const markupItem = new MarkupItem(type, inner, outer) + outputMarkupItems.push(markupItem) + } + + typesToStartTracking.forEach(addMarkupItemWithRangeStarts) + typesToStopTracking.forEach(updateMarkupItemRangeEnds) + types.forEach(addCompleteMarkupRanges) + + typesToStopTracking.forEach(tag => trackedTypes.delete(tag)) + typesToStartTracking.forEach(tag => trackedTypes.add(tag)) + } + + lineTokens.forEach(tokenWithEnrichedInHtmlTagArray) + + correctForCodeMirrorBoldItalicNestingBugsIfNeeded(outputMarkupItems) + + return outputMarkupItems +} + +// Codemirror incorrectly tokenizes end tokens for italic markup ('') nested inside bold markup (''') in some cases. +// Example strings: +// Hello '''''there''''' one +// Hello '''x''there''''' two +// This method detects such instances and corrects inner and outer range end locations. +const correctForCodeMirrorBoldItalicNestingBugsIfNeeded = (markupItems) => { + const aStartsInsideBAndEndsExactlyAfterB = (a, b) => a.outerRange.startsInsideRange(b.innerRange, true) && a.innerRange.endLocation.equals(b.outerRange.endLocation) + const isItemItalic = item => item.buttonName === 'italic' + const isItemBold = item => item.buttonName === 'bold' + const soughtItalicItemForBoldItem = boldItem => markupItems + .filter(isItemItalic) + .find(italicItem => { + return aStartsInsideBAndEndsExactlyAfterB(italicItem, boldItem) + }) + const adjustBoldItalicPair = pair => { + adjustMarkupItemInnerAndOuterRangeEndLocations(pair.boldItem, 2) + adjustMarkupItemInnerAndOuterRangeEndLocations(pair.italicItem, -3) + } + const adjustMarkupItemInnerAndOuterRangeEndLocations = (markupItem, endLocationsOffset) => { + markupItem.innerRange.endLocation.ch = markupItem.innerRange.endLocation.ch + endLocationsOffset + markupItem.outerRange.endLocation.ch = markupItem.outerRange.endLocation.ch + endLocationsOffset + } + + // Finds bold and italic pairs where: + // - bold contains the italic start + // - italic inner range end location equals the bold outer range end location + // Then makes needed adjustments on these pairs. + markupItems + .filter(isItemBold) + .map(boldItem => { + const italicItem = soughtItalicItemForBoldItem(boldItem) + return (italicItem !== undefined) ? {boldItem, italicItem} : null + }) + .filter(maybePair => maybePair !== null) + .forEach(adjustBoldItalicPair) +} + +//*****END: codemirror-range-determination-non-tag.js +//*****BEGIN: codemirror-range-determination-tag.js + +const isTokenForTagBracket = (token) => tokenIncludesType(token, 'mw-htmltag-bracket') || tokenIncludesType(token, 'mw-exttag-bracket') +const isTokenStartOfOpenTag = (token) => isTokenForTagBracket(token) && token.string === '<' +const isTokenEndOfOpenTag = (token) => isTokenForTagBracket(token) && token.string === '>' +const isTokenStartOfCloseTag = (token) => isTokenForTagBracket(token) && token.string === ' { + let openTagStartTokenIndices = [] + const possiblyRecordOpenTagTokenIndex = (token, index) => { + if (isTokenStartOfOpenTag(token)) { + openTagStartTokenIndices.push(index) + } + } + lineTokens.forEach(possiblyRecordOpenTagTokenIndex) + return openTagStartTokenIndices +} + +const getOpenTagEndTokenIndices = (lineTokens, openTagStartTokenIndices) => { + const getOpenTagEndTokenIndex = (openTagStartTokenIndex) => { + return lineTokens.findIndex((t, i) => { + return i > openTagStartTokenIndex && isTokenEndOfOpenTag(t) + }) + } + return openTagStartTokenIndices.map(getOpenTagEndTokenIndex) +} + +const tagMarkupItemsForLineTokens = (lineTokens, line) => { + const openTagStartTokenIndices = getOpenTagStartTokenIndices(lineTokens) + const tagTypeTokenIndices = openTagStartTokenIndices.map(i => i + 1) + const openTagEndTokenIndices = getOpenTagEndTokenIndices(lineTokens, openTagStartTokenIndices) + + const closeTagStartTokenIndices = getCloseTagStartTokenIndices(lineTokens, openTagEndTokenIndices) + const closeTagEndTokenIndices = closeTagStartTokenIndices.map(i => i + 2) + + let output = [] + const tagCount = openTagStartTokenIndices.length + + for (let i = 0; i < tagCount; i++) { + const openTagStartTokenIndex = openTagStartTokenIndices[i] + const tagTypeTokenIndex = tagTypeTokenIndices[i] + const openTagEndTokenIndex = openTagEndTokenIndices[i] + const closeTagStartTokenIndex = closeTagStartTokenIndices[i] + const closeTagEndTokenIndex = closeTagEndTokenIndices[i] + + if ( + openTagStartTokenIndex === undefined || + tagTypeTokenIndex === undefined || + openTagEndTokenIndex === undefined || + closeTagStartTokenIndex === undefined || + closeTagEndTokenIndex === undefined + ) { + continue + } + + let outer = new ItemRange(new ItemLocation(line, lineTokens[openTagStartTokenIndex].start), new ItemLocation(line, lineTokens[closeTagEndTokenIndex].end)) + let inner = new ItemRange(new ItemLocation(line, lineTokens[openTagEndTokenIndex].end), new ItemLocation(line, lineTokens[closeTagStartTokenIndex].start)) + let type = lineTokens[tagTypeTokenIndex].string.trim() + output.push(new MarkupItem(type, inner, outer)) + } + return output +} + +const getCloseTagStartTokenIndices = (lineTokens, openTagEndTokenIndices) => { + let closeTagStartTokenIndices = [] + + openTagEndTokenIndices.forEach(openTagEndTokenIndex => { + let depth = 0 + for (let i = openTagEndTokenIndex + 1; i < lineTokens.length; i++) { + let thisToken = lineTokens[i] + if (isTokenStartOfOpenTag(thisToken)){ + depth = depth + 1 + } else if (isTokenStartOfCloseTag(thisToken)) { + if (depth === 0) { + closeTagStartTokenIndices.push(i) + break + } + depth = depth - 1 + } + } + + }) + + return closeTagStartTokenIndices +} + +//*****END: codemirror-range-determination-tag.js +//*****BEGIN: codemirror-range-determination.js + +const markupItemsForLineTokens = (lineTokens, line) => { + const tagMarkupItems = tagMarkupItemsForLineTokens(lineTokens, line) + const nonTagMarkupItems = nonTagMarkupItemsForLineTokens(lineTokens, line) + const markupItems = tagMarkupItems.concat(nonTagMarkupItems) + return markupItems +} + +// Gets all markup items for ALL lines between range's start and end. +// May include markup items to left of range start or right range end. +const markupItemsForItemRangeLines = (codeMirror, range) => { + let markupItems = [] + range.lineNumbers().forEach(lineNumber => { + const tokens = codeMirror.getLineTokens(lineNumber, true) + const items = markupItemsForLineTokens(tokens, lineNumber) + markupItems.push(...items) + }) + return markupItems.sort((a, b) => { + return a.openingMarkupRange().startLocation.greaterThan(b.openingMarkupRange().startLocation) + }) +} + +//*****END: codemirror-range-determination.js + +//*****BEGIN: codemirror-range-clear-formatting.js + +const buttonNamesInSelectionRange = (codeMirror, selectionRange) => { + const markupItems = markupItemsForItemRangeLines(codeMirror, selectionRange) + const markupItemsIntersectingSelection = getMarkupItemsIntersectingSelection(codeMirror, markupItems, selectionRange) + const buttonNames = getButtonNamesFromMarkupItems(markupItemsIntersectingSelection) + return buttonNames +} + +const canClearFormatting = (codeMirror) => { + let selectionRange = getItemRangeFromSelection(codeMirror) + if (selectionRange.isZeroLength()) { + return false + } + + const buttonNames = buttonNamesInSelectionRange(codeMirror, selectionRange) + if (buttonNames.includes('reference') || buttonNames.includes('template') || buttonNames.includes('template-argument') || buttonNames.includes('template-name') || buttonNames.includes('template-delimiter')) { + return false + } + + return canRelocateOrRemoveExistingMarkupForSelectionRange(codeMirror) || canSplitMarkupAroundSelectionRange(codeMirror) +} + +const clearFormatting = (codeMirror) => { + codeMirror.operation(() => { + relocateOrRemoveExistingMarkupForSelectionRange(codeMirror, false) + splitMarkupAroundSelectionRange(codeMirror, false) + pruneWhitespaceOnlyMarkupItems(codeMirror) + }) +} + +const canRelocateOrRemoveExistingMarkupForSelectionRange = (codeMirror) => relocateOrRemoveExistingMarkupForSelectionRange(codeMirror, true) + +const unsplittableMarkupTypes = ["mw-section-header"] // headers should always be removed, never split + +const relocateOrRemoveExistingMarkupForSelectionRange = (codeMirror, evaluateOnly = false) => { + let selectionRange = getItemRangeFromSelection(codeMirror) + const originalSelectionRange = selectionRange + + const allMarkupItems = markupItemsForItemRangeLines(codeMirror, selectionRange) + const markupItems = allMarkupItems.filter(item => !unsplittableMarkupTypes.includes(item.type) && item.innerRangeStartsOrEndsInRange(selectionRange, true)) + const unsplittableItems = allMarkupItems.filter(item => unsplittableMarkupTypes.includes(item.type)) + + selectionRange = getExpandedSelectionRange(codeMirror, markupItems, selectionRange) + if (!evaluateOnly) { + codeMirror.setSelection(selectionRange.startLocation, selectionRange.endLocation, '+') + } + + const markupItemsIntersectingSelection = getMarkupItemsIntersectingSelection(codeMirror, markupItems, selectionRange) + + let markupRangesToMoveAfterSelection = [] + let markupRangesToMoveBeforeSelection = [] + let unsplittableMarkupRangesToRemove = [] + let markupRangesToRemove = [] + unsplittableItems.forEach(item => { + unsplittableMarkupRangesToRemove.push(item.openingMarkupRange()) + unsplittableMarkupRangesToRemove.push(item.closingMarkupRange()) + }) + markupItemsIntersectingSelection.forEach(item => { + const startsInside = item.outerRange.startsInsideRange(selectionRange, true) + const endsInside = item.outerRange.endsInsideRange(selectionRange, true) + if (!(startsInside === endsInside)) { // XOR + if (startsInside) { + markupRangesToMoveAfterSelection.push(item.openingMarkupRange()) + } + if (endsInside) { + markupRangesToMoveBeforeSelection.unshift(item.closingMarkupRange()) + } + } else if (startsInside && endsInside) { + markupRangesToRemove.push(item.openingMarkupRange()) + markupRangesToRemove.push(item.closingMarkupRange()) + } + }) + + const noMarkupToBeMovedToEitherSide = (markupRangesToMoveAfterSelection.length === 0 && markupRangesToMoveBeforeSelection.length === 0) + if (noMarkupToBeMovedToEitherSide) { + const openingMarkupRanges = markupItemsIntersectingSelection.map(item => item.openingMarkupRange()) + const closingMarkupRanges = markupItemsIntersectingSelection.map(item => item.closingMarkupRange()) + const allMarkupRanges = unsplittableMarkupRangesToRemove.concat(openingMarkupRanges.concat(closingMarkupRanges)) + if (evaluateOnly) { + return allMarkupRanges.length > 0 + } + removeTextFromRanges(codeMirror, allMarkupRanges) + return allMarkupRanges.length > 0 + } + if (evaluateOnly) { + return true + } + + let accumulatedLeftMarkup = getTextFromRanges(codeMirror, markupRangesToMoveAfterSelection) + let accumulatedRightMarkup = getTextFromRanges(codeMirror, markupRangesToMoveBeforeSelection) + + // Work-around for cases which will result in empty markup items such as ``, which, in some + // cases, confuses the CodeMirror wikitext parsing. Injects a space inside such markup items so + // they tokenize correctly can later be identified as whitespace-only and pruned. + if (selectionStartsAtOpeningMarkupEnd(markupItems, selectionRange)) { + accumulatedRightMarkup = ` ${accumulatedRightMarkup}` + } + if (selectionEndsAtClosingMarkupStart(markupItems, selectionRange)) { + accumulatedLeftMarkup = `${accumulatedLeftMarkup} ` + } + + // Relocate any markup that needs to be moved after selection + codeMirror.replaceRange(accumulatedLeftMarkup, selectionRange.endLocation, null, '+') + + // Remove any markup that needs to be blasted + const allMarkupRangesToRemove = markupRangesToMoveBeforeSelection.concat(markupRangesToMoveAfterSelection).concat(markupRangesToRemove).concat(unsplittableMarkupRangesToRemove) + removeTextFromRanges(codeMirror, allMarkupRangesToRemove) + + // Relocate any markup that needs to be moved before selection + codeMirror.replaceRange(accumulatedRightMarkup, selectionRange.startLocation, null, '+') + + // Adjust selection to account for adjustments made above. + codeMirror.setSelection( + selectionRange.startLocation.withOffset(0, accumulatedRightMarkup.length), + selectionRange.endLocation.withOffset(0, -getTextFromRanges(codeMirror, markupRangesToMoveAfterSelection.concat(markupRangesToRemove)).length), + '+' + ) + return true +} + +// Need to remove in reverse order of appearance to avoid invalidating yet-to-be-removed ranges. +const removeTextFromRanges = (codeMirror, ranges) => { + const reverseSortedRanges = Array.from(ranges).sort((a, b) => { + return a.startLocation.lessThan(b.startLocation) + }) + reverseSortedRanges.forEach(range => codeMirror.replaceRange('', range.startLocation, range.endLocation, '+')) +} + +const getTextFromRanges = (codeMirror, ranges) => ranges.map(range => codeMirror.getRange(range.startLocation, range.endLocation)).join('') + +const getExpandedSelectionRange = (codeMirror, markupItems, selectionRange) => { + let newSelectionRange = selectionRange + + // If selectionRange starts inside a markup item's opening markup, the returned range start will be moved to start of opening markup. + const selectionStartsInOpeningMarkupOfItem = markupItems.find(item => selectionRange.startsInsideRange(item.openingMarkupRange(), true)) + if (selectionStartsInOpeningMarkupOfItem) { + newSelectionRange.startLocation = selectionStartsInOpeningMarkupOfItem.openingMarkupRange().startLocation + } else { + // If selectionRange starts inside a markup item's closing markup, the returned range start will be moved to end of closing markup. + const selectionStartsInClosingMarkupOfItem = markupItems.find(item => selectionRange.startsInsideRange(item.closingMarkupRange(), true)) + if (selectionStartsInClosingMarkupOfItem) { + newSelectionRange.startLocation = selectionStartsInClosingMarkupOfItem.closingMarkupRange().endLocation + } + } + + // If selectionRange ends inside a markup item's closing markup, the returned range end will be moved to end of closing markup. + const selectionEndsInClosingMarkupOfItem = markupItems.find(item => selectionRange.endsInsideRange(item.closingMarkupRange(), true)) + if (selectionEndsInClosingMarkupOfItem) { + newSelectionRange.endLocation = selectionEndsInClosingMarkupOfItem.closingMarkupRange().endLocation + } else { + // If selectionRange ends inside a markup item's opening markup, the returned range end will be moved to start of opening markup. + const selectionEndsInOpeningMarkupOfItem = markupItems.find(item => selectionRange.endsInsideRange(item.openingMarkupRange(), true)) + if (selectionEndsInOpeningMarkupOfItem) { + newSelectionRange.endLocation = selectionEndsInOpeningMarkupOfItem.openingMarkupRange().startLocation + } + } + + return newSelectionRange +} + +const canSplitMarkupAroundSelectionRange = (codeMirror) => splitMarkupAroundSelectionRange(codeMirror, true) + +const splitMarkupAroundSelectionRange = (codeMirror, evaluateOnly = false) => { + const selectionRange = getItemRangeFromSelection(codeMirror) + + let markupItems = markupItemsForItemRangeLines(codeMirror, selectionRange) + + const markupItemOpeningOrClosingMarkupIntersectsSelectionRange = (item) => item.openingMarkupRange().intersectsRange(selectionRange, false) || item.closingMarkupRange().intersectsRange(selectionRange, false) + + const selectionIncludesAnyOpeningOrClosingMarkup = markupItems.find(markupItemOpeningOrClosingMarkupIntersectsSelectionRange) !== undefined + + if (selectionIncludesAnyOpeningOrClosingMarkup) { + return false + } + + const selectionIntersectsItemInnerRange = (item) => item.innerRange.intersectsRange(selectionRange, true) + markupItems = markupItems.filter(selectionIntersectsItemInnerRange)// === undefined + // Return if selection doesn't intersect with any markup items (selected word at end of line after last markup item etc). + if (markupItems.length === 0) { + return false + } + + if (evaluateOnly) { + return true + } + + let markupRangesToAddBeforeSelection = [] + let markupRangesToAddAfterSelection = [] + + markupItems.forEach(item => { + markupRangesToAddBeforeSelection.unshift(item.closingMarkupRange()) + markupRangesToAddAfterSelection.push(item.openingMarkupRange()) + }) + + let accumulatedLeftMarkup = getTextFromRanges(codeMirror, markupRangesToAddBeforeSelection) + let accumulatedRightMarkup = getTextFromRanges(codeMirror, markupRangesToAddAfterSelection) + + // Work-around for cases which will result in empty markup items such as ``, which, in some + // cases, confuses the CodeMirror wikitext parsing. Injects a space inside such markup items so + // they tokenize correctly can later be identified as whitespace-only and pruned. + if (selectionStartsAtOpeningMarkupEnd(markupItems, selectionRange)) { + accumulatedLeftMarkup = ` ${accumulatedLeftMarkup}` + } + if (selectionEndsAtClosingMarkupStart(markupItems, selectionRange)) { + accumulatedRightMarkup = `${accumulatedRightMarkup} ` + } + + // At selection end add opening tags. + codeMirror.replaceRange(accumulatedRightMarkup, selectionRange.endLocation, null, '+') + // At selection start add closing tags. + codeMirror.replaceRange(accumulatedLeftMarkup, selectionRange.startLocation, null, '+') + + // Adjust selection. + const origSelectionRangeLineExtent = selectionRange.endLocation.line - selectionRange.startLocation.line + const origSelectionRangeChExtent = selectionRange.endLocation.ch - selectionRange.startLocation.ch + const newSelectionRange = getItemRangeFromSelection(codeMirror) + codeMirror.setSelection( + newSelectionRange.startLocation, + newSelectionRange.startLocation.withOffset(origSelectionRangeLineExtent, origSelectionRangeChExtent), + '+' + ) + + return true +} + +const selectionStartsAtOpeningMarkupEnd = (markupItems, selectionRange) => markupItems.find(item => item.openingMarkupRange().endLocation.equals(selectionRange.startLocation)) !== undefined +const selectionEndsAtClosingMarkupStart = (markupItems, selectionRange) => markupItems.find(item => item.closingMarkupRange().startLocation.equals(selectionRange.endLocation)) !== undefined + +// Safely prunes even items which end up empty as a result of pruning other nested items. +const pruneWhitespaceOnlyMarkupItems = (codeMirror) => { + const itemContainsOnlyWhitespace = item => codeMirror.getRange(item.innerRange.startLocation, item.innerRange.endLocation).trim().length === 0 + const pruneItem = item => codeMirror.replaceRange('', item.outerRange.startLocation, item.outerRange.endLocation, '+') + const selectionRange = getItemRangeFromSelection(codeMirror) + let maxItemsToPrune = markupItemsForItemRangeLines(codeMirror, selectionRange).length + while (maxItemsToPrune > 0) { + let itemsToPrune = markupItemsForItemRangeLines(codeMirror, selectionRange).filter(itemContainsOnlyWhitespace) + if (itemsToPrune.length === 0) { + break + } + itemsToPrune.reverse().forEach(item => { + pruneItem(item) + }) + maxItemsToPrune = maxItemsToPrune - 1 + } +} + +//*****END: codemirror-range-clear-formatting.js +//*****BEGIN: codemirror-range-debugging.js + +// Uncomment the chunk below to add debugging buttons to top of article wikitext editor. + +/* +setTimeout(() => { + showRangeDebuggingButtonsForCursorLine(editor) +}, 1000) +*/ + +let markupItems = [] +let currentItemIndex = 0 +let highlightHandle = null +let useOuter = true +let codeMirror = null + +const addButton = (title, tapClosure) => { + const button = document.createElement('button') + button.innerHTML = title + document.body.insertBefore(button, document.body.firstChild) + button.addEventListener ('click', tapClosure) +} + +const clearItems = () => { + markupItems = [] +} + +const clearHighlightHandle = () => { + if (highlightHandle) { + highlightHandle.clear() + } + highlightHandle = null +} + +const reset = () => { + clearHighlightHandle() + clearItems() +} + +const kickoff = () => { + reset() + const line = codeMirror.getCursor().line + const lineTokens = codeMirror.getLineTokens(line, true) + markupItems = markupItemsForLineTokens(lineTokens, line) + highlightTextForMarkupItemAtIndex(currentItemIndex) +} + +const rangeDebuggingCSSClassName = 'range-debugging' + +function addRangeDebuggingStyleOnce() { + const id = 'debugging-style-element' + if (document.getElementById(id)) { + return + } + const cssNode = document.createElement('style') + cssNode.id = id + cssNode.innerHTML = `.${rangeDebuggingCSSClassName} { background-color: #cccccc; }` + document.body.appendChild(cssNode) +} + +const showRangeDebuggingButtonsForCursorLine = (cm) => { + codeMirror = cm + + addRangeDebuggingStyleOnce() + + addButton('reset', () => { + reset() + currentItemIndex = 0 + console.log('reset') + }) + + addButton('>', () => { + clearHighlightHandle() + currentItemIndex = currentItemIndex + 1 + if (currentItemIndex > (markupItems.length - 1)) { + currentItemIndex = markupItems.length - 1 + } + highlightTextForMarkupItemAtIndex(currentItemIndex) + console.log('next') + }) + + addButton('<', () => { + clearHighlightHandle() + currentItemIndex = currentItemIndex - 1 + if (currentItemIndex < 0) { + currentItemIndex = 0 + } + highlightTextForMarkupItemAtIndex(currentItemIndex) + console.log('prev') + }) + + addButton('test outer', () => { + useOuter = true + kickoff() + }) + + addButton('test inner', () => { + useOuter = false + kickoff() + }) + + const span = document.createElement('span') + span.innerHTML = 'Tap line, then tap:' + document.body.insertBefore(span, document.body.firstChild) +} + +const highlightTextForMarkupItemAtIndex = (index) => { + if (markupItems.length === 0) { + return + } + + const markupItem = markupItems[index] + const range = useOuter ? markupItem.outerRange : markupItem.innerRange + + clearHighlightHandle() + highlightHandle = codeMirror.markText(range.startLocation, range.endLocation, { + className: rangeDebuggingCSSClassName + }) +} + +//*****END: codemirror-range-debugging.js diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-sepia.css b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-sepia.css new file mode 100644 index 0000000..be0d222 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-sepia.css @@ -0,0 +1,95 @@ +.cm-mw-sepia, .cm-mw-sepia .CodeMirror { + background-color: #F8F1E3; + color: #222; +} + +.cm-mw-sepia .CodeMirror-gutters { + background-color: #F8F1E3; + border-right: 4px solid #F8F1E3; +} + +.cm-mw-sepia .CodeMirror-linenumber { + color: #6C6760; + font-size: 0.8em; +} + +.cm-mw-sepia .cm-mw-template { + color: #222; /* plain text color */ +} + +.cm-mw-sepia .cm-mw-link, +.cm-mw-sepia .cm-mw-link-pagename, +.cm-mw-sepia .cm-mw-link-tosection, +.cm-mw-sepia .cm-mw-link-bracket, +.cm-mw-sepia .cm-mw-link-delimiter, +.cm-mw-sepia .cm-mw-extlink, +.cm-mw-sepia .cm-mw-free-extlink, +.cm-mw-sepia .cm-mw-extlink-protocol, +.cm-mw-sepia .cm-mw-free-extlink-protocol, +.cm-mw-sepia .cm-mw-extlink-bracket, +.cm-mw-sepia .cm-mw-doubleUnderscore, +.cm-mw-sepia .cm-mw-signature, +.cm-mw-sepia .cm-mw-hr { + color: #36C; + font-weight: normal; +} + +.cm-mw-sepia .cm-mw-mnemonic, +.cm-mw-sepia .cm-mw-exttag-name, +.cm-mw-sepia .cm-mw-exttag-bracket, +.cm-mw-sepia .cm-mw-exttag-attribute, +.cm-mw-sepia .cm-mw-htmltag-name, +.cm-mw-sepia .cm-mw-htmltag-bracket, +.cm-mw-sepia .cm-mw-htmltag-attribute { + color: #00AF89; + font-weight: normal; +} + +.cm-mw-sepia .cm-mw-parserfunction-name, +.cm-mw-sepia .cm-mw-parserfunction-bracket, +.cm-mw-sepia .cm-mw-parserfunction-delimiter { + color: #B32424; +} + +.cm-mw-sepia .cm-mw-table-bracket, +.cm-mw-sepia .cm-mw-table-delimiter, +.cm-mw-sepia .cm-mw-table-definition { + color: #F06695; + font-weight: normal; +} + +.cm-mw-sepia .cm-mw-template-name, +.cm-mw-sepia .cm-mw-template-argument-name, +.cm-mw-sepia .cm-mw-template-delimiter, +.cm-mw-sepia .cm-mw-template-bracket { + color: #97498F; + font-weight: normal; +} + +.cm-mw-sepia .cm-mw-list, +.cm-mw-sepia .cm-mw-doubleUnderscore, +.cm-mw-sepia .cm-mw-signature, +.cm-mw-sepia .cm-mw-hr, +.cm-mw-sepia .cm-mw-indenting, +.cm-mw-sepia .cm-mw-apostrophes-bold, +.cm-mw-sepia .cm-mw-apostrophes-italic, +.cm-mw-sepia .cm-mw-section-header, +.cm-mw-sepia .cm-mw-templatevariable, +.cm-mw-sepia .cm-mw-templatevariable-name, +.cm-mw-sepia .cm-mw-templatevariable-bracket, +.cm-mw-sepia .cm-mw-templatevariable-delimiter, +.cm-mw-sepia .cm-mw-matching { + color: #FF9500; + font-weight: normal; +} + +.cm-mw-sepia .cm-mw-comment, +.cm-mw-sepia .cm-mw-skipformatting { + color: #6C6760; +} + +.cm-mw-sepia .cm-searching { + color: #000; + background-color: rgba(255, 204, 51, 0.3); + border-radius: 2px; +} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-syntax-highlighting-off.css b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-syntax-highlighting-off.css new file mode 100644 index 0000000..57729ff --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/codemirror-syntax-highlighting-off.css @@ -0,0 +1,47 @@ +.cm-mw-off .cm-mw-link, +.cm-mw-off .cm-mw-link-pagename, +.cm-mw-off .cm-mw-link-tosection, +.cm-mw-off .cm-mw-link-bracket, +.cm-mw-off .cm-mw-link-delimiter, +.cm-mw-off .cm-mw-extlink, +.cm-mw-off .cm-mw-free-extlink, +.cm-mw-off .cm-mw-extlink-protocol, +.cm-mw-off .cm-mw-free-extlink-protocol, +.cm-mw-off .cm-mw-extlink-bracket, +.cm-mw-off .cm-mw-doubleUnderscore, +.cm-mw-off .cm-mw-signature, +.cm-mw-off .cm-mw-hr, +.cm-mw-off .cm-mw-mnemonic, +.cm-mw-off .cm-mw-exttag-name, +.cm-mw-off .cm-mw-exttag-bracket, +.cm-mw-off .cm-mw-exttag-attribute, +.cm-mw-off .cm-mw-htmltag-name, +.cm-mw-off .cm-mw-htmltag-bracket, +.cm-mw-off .cm-mw-htmltag-attribute, +.cm-mw-off .cm-mw-parserfunction-name, +.cm-mw-off .cm-mw-parserfunction-bracket, +.cm-mw-off .cm-mw-parserfunction-delimiter, +.cm-mw-off .cm-mw-table-bracket, +.cm-mw-off .cm-mw-table-delimiter, +.cm-mw-off .cm-mw-table-definition, +.cm-mw-off .cm-mw-template-name, +.cm-mw-off .cm-mw-template-argument-name, +.cm-mw-off .cm-mw-template-delimiter, +.cm-mw-off .cm-mw-template-bracket, +.cm-mw-off .cm-mw-list, +.cm-mw-off .cm-mw-doubleUnderscore, +.cm-mw-off .cm-mw-signature, +.cm-mw-off .cm-mw-hr, +.cm-mw-off .cm-mw-indenting, +.cm-mw-off .cm-mw-apostrophes-bold, +.cm-mw-off .cm-mw-apostrophes-italic, +.cm-mw-off .cm-mw-section-header, +.cm-mw-off .cm-mw-templatevariable, +.cm-mw-off .cm-mw-templatevariable-name, +.cm-mw-off .cm-mw-templatevariable-bracket, +.cm-mw-off .cm-mw-templatevariable-delimiter, +.cm-mw-off .cm-mw-matching, +.cm-mw-off .cm-mw-comment, +.cm-mw-off .cm-mw-skipformatting { + color: inherit; +} diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-aa.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-aa.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-aa.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ab.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ab.json new file mode 100644 index 0000000..39bdcc1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ab.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__АИНДЕКС__":"index","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect", +"__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол": +"gender","gender":"gender","множественное_число":"plural","plural":"plural","bidi":"bidi","#абызшәа":"language","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#цастәи":"special","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if", +"#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath", +"stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2": +"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ": +"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace", +"ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits", +"ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ": +"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime", +"МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}], +"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгӷҕдежзӡикқҟлмнопԥҧрстҭуфхҳцҵчҷҽҿшыҩџьә]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ace.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ace.json new file mode 100644 index 0000000..04dbf3d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ace.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa": +"language","#bhs":"language","#language":"language","isikiri":"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1":"timel","#timel":"timel","#hitung":"expr", +"#expr":"expr","#rel2abs":"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","HALAMANDIKATEGORI":"pagesincategory","HALDIKAT": +"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee", +"NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE":"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid","HARIREVISI":"revisionday","HAREV":"revisionday" +,"REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace","RUANGNAMAE":"namespacee","RUNAME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace","RUTAMA":"subjectspace","RUTIKEL":"subjectspace", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","JUMLAHARTIKEL":"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits","NUMBEROFEDITS":"numberofedits","JUDULTAMPILAN":"displaytitle" +,"JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME":"currentdayname","TAHUNKINI":"currentyear","TAKIN":"currentyear", +"CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN":"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname","LOCALDAYNAME":"localdayname","TAHUNLOKAL":"localyear","TALOK":"localyear", +"LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK":"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","BAHASAISI":"contentlanguage", +"BHSISI":"contentlanguage","BASI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ady.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ady.json new file mode 100644 index 0000000..7576fde --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ady.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюяӀ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-af.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-af.json new file mode 100644 index 0000000..3df726c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-af.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__geenio__":"notoc","__notoc__":"notoc","__geengalery__":"nogallery","__nogallery__":"nogallery","__dwingio__":"forcetoc","__forcetoc__":"forcetoc","__io__":"toc","__toc__":"toc","__geennuweafdeling__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert", +"__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__GEENINDEKS__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","volurl":"fullurl","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","geslag":"gender","gender":"gender","meervoud":"plural","plural":"plural","bidi":"bidi","#taal":"language","#language":"language","padleft":"padleft","padright":"padright", +"anchorencode":"anchorencode","lêerpad":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#spesiaal":"special","#special":"special","#speciale":"speciale","#etiket":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorieboom":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","bediener":"server","server":"server","bedienernaam":"servername","servername":"servername", +"scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BLADSYGROOTTE":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","BLADSYNAAM":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","VOLBLADSYNAAM":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE": +"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAAMSPASIE":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","BESPREKINGSBLADSY":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","AANTALARTIKELS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","AANTALLêERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","AANTALGEBRUIKERS":"numberofusers","NUMBEROFUSERS":"numberofusers","AANTALAKTIEWEGEBRUIKERS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","AANTALBLADSYE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS" +:"numberofadmins","AANTALWYSIGINGS":"numberofedits","NUMBEROFEDITS":"numberofedits","VERTOONTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","HUIDIGEMAAND":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","HUIDIGEMAAND1":"currentmonth1","CURRENTMONTH1":"currentmonth1","HUIDIGEMAANDNAAM":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","HUIDIGEMAANDAFK":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HUIDIGEDAG":"currentday","CURRENTDAY":"currentday","HUIDIGEDAG2":"currentday2","CURRENTDAY2":"currentday2","HUIDIGEDAGNAAM":"currentdayname","CURRENTDAYNAME":"currentdayname","HUIDIGEJAAR":"currentyear","CURRENTYEAR":"currentyear","HUIDIGETYD":"currenttime","CURRENTTIME":"currenttime","HUIDIGEUUR":"currenthour","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN": +"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","WERFNAAM":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","HUIDIGEWEEK":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","HUIDIGEWEERGAWE":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ak.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ak.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ak.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-als.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-als.json new file mode 100644 index 0000000..7608f2c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-als.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__":"forcetoc","__inhaltsverzeichnis__":"toc","__toc__":"toc", +"__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__":"noindex","__NOINDEX__":"noindex","__PERMANENTE_WEITERLEITUNG__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","lokale_url":"localurl","localurl":"localurl","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zahlenformat":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#sprache":"language","#language":"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright": +"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorienbaum":"categorytree","#kategoriebaum":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx", +"#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE":"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SEITENNAME":"pagename","PAGENAME":"pagename","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSSIONSSEITE":"talkpagename","DISK":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","HAUPTSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR":"revisionyear","VERSIONSJAHR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp", +"REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","NAMENSRAUM":"namespace","NAMESPACE":"namespace","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DATEIANZAHL":"numberoffiles","NUMBEROFFILES":"numberoffiles","BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTIVE_BENUTZER": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SYTETITEL":"displaytitle","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JETZIGER_MONATSNAME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JETZIGER_KALENDERTAG":"currentday","JETZIGER_TAG":"currentday","CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2" +,"JETZIGER_TAG_2":"currentday2","CURRENTDAY2":"currentday2","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV":"localmonthnamegen","LOKALER_MONATSNAME_GEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2","LOKALER_TAG_2":"localday2","LOCALDAY2":"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME": +"localdayname","LOKALES_JAHR":"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize","JETZIGE_VERSION":"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHALTSSPRACHE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöüßa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-alt.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-alt.json new file mode 100644 index 0000000..c98fdea --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-alt.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender", +"множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist": +"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ": +"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY": +"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour", +"МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename", +"ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюяјҥӧӱ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-am.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-am.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-am.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ami.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ami.json new file mode 100644 index 0000000..8f21bcd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ami.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__無目錄__":"notoc","__无目录__":"notoc","__notoc__":"notoc","__無圖庫__":"nogallery","__无图库__":"nogallery","__nogallery__":"nogallery","__強制目錄__":"forcetoc","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__目錄__":"toc","__目录__":"toc","__toc__":"toc","__無段落編輯__": +"noeditsection","__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__不轉換標題__":"notitleconvert","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換內容__":"nocontentconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__新段落链接__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__无新段落链接__":"nonewsectionlink","__隱藏分類__":"hiddencat","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__索引__":"index","__NOINDEX__":"noindex","__无索引__":"noindex","__靜態重新導向__":"staticredirect","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__消除歧义__": +"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"命名空間":"ns","名字空间":"ns","ns":"ns","名称空间":"ns","命名空間e":"nse","名字空间e":"nse","nse":"nse","名称空间e":"nse","urlencode":"urlencode","url编码":"urlencode","lcfirst":"lcfirst","小写首字":"lcfirst","ucfirst":"ucfirst","大写首字":"ucfirst","lc":"lc","小写":"lc","uc":"uc","大写":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","fullurl":"fullurl","完整url":"fullurl","fullurle":"fullurle","完整url等同":"fullurle","canonicalurl":"canonicalurl","规范url":"canonicalurl","canonicalurle":"canonicalurle","规范url等同":"canonicalurle","formatnum":"formatnum","格式化数字":"formatnum","grammar":"grammar","语法":"grammar","性別":"gender","性":"gender","性别":"gender","gender":"gender","plural":"plural","复数":"plural","bidi":"bidi","#語言": +"language","#语言":"language","#language":"language","padleft":"padleft","左填充":"padleft","padright":"padright","右填充":"padright","anchorencode":"anchorencode","锚编码":"anchorencode","filepath":"filepath","文件路径":"filepath","頁面id":"pageid","页面id":"pageid","pageid":"pageid","int":"int","界面":"int","#special":"special","#特殊":"special","#speciale":"speciale","#特殊等同":"speciale","#tag":"tag","#标记":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#格式化日期":"formatdate","#日期格式化":"formatdate","#目標":"target","#目标":"target","#target":"target","#巴別":"babel","#巴别":"babel","#babel":"babel","#coordinates":"coordinates","#調動":"invoke","#调用":"invoke","#invoke":"invoke","#related":"related","#若":"if","#非空式":"if","#如果":"if","#if":"if","#相同式":"ifeq","#匹配式":"ifeq","#若相等":"ifeq","#如果相等":"ifeq","#ifeq":"ifeq","#轉換":"switch","#多选式":"switch","#多条件式": +"switch","#双射式":"switch","#开关":"switch","#转换":"switch","#switch":"switch","#存在式":"ifexist","#若有":"ifexist","#如有":"ifexist","#ifexist":"ifexist","#若表達式":"ifexpr","#若表达式":"ifexpr","#ifexpr":"ifexpr","#如果錯誤":"iferror","#错误式":"iferror","#如果错误":"iferror","#iferror":"iferror","#時間":"time","#时间":"time","#time":"time","#時間l":"timel","#时间l":"timel","#timel":"timel","#表達式":"expr","#计算式":"expr","#表达式":"expr","#expr":"expr","#rel2abs":"rel2abs","#标题组成部分":"titleparts","#titleparts":"titleparts","#分類樹":"categorytree","#分类树":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","隱藏跨語言連結":"noexternallanglinks","无外部语言连接":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#屬性":"property","#属性":"property","#property":"property","#statements" +:"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","条目路径":"articlepath","伺服器":"server","服务器":"server","server":"server","伺服器名稱":"servername","服务器名":"servername","servername":"servername","scriptpath":"scriptpath","脚本路径":"scriptpath","stylepath":"stylepath","样式路径":"stylepath","numberofwikis":"numberofwikis","wb報表名稱":"wbreponame","wb报告名":"wbreponame","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","组中用户数":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","默认排序":"defaultsort","默认排序关键字":"defaultsort","默认分类排序":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","分类中页面数":"pagesincategory","PAGESIZE":"pagesize","页面大小":"pagesize","PROTECTIONLEVEL":"protectionlevel","保护级别": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","頁面名稱":"pagename","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","页面名等同":"pagenamee","页面名称等同":"pagenamee","FULLPAGENAME":"fullpagename","页面全称":"fullpagename","完整页面名称":"fullpagename","FULLPAGENAMEE":"fullpagenamee","完整页面名称等同":"fullpagenamee","SUBPAGENAME":"subpagename","子页面名称":"subpagename","SUBPAGENAMEE":"subpagenamee","子页面名称等同":"subpagenamee","根頁面名稱":"rootpagename","ROOTPAGENAME":"rootpagename","根页面名称":"rootpagename","根頁面名稱E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","根页面名称等同":"rootpagenamee","BASEPAGENAME":"basepagename","基础页面名称":"basepagename","BASEPAGENAMEE":"basepagenamee","基础页面名称等同":"basepagenamee","TALKPAGENAME":"talkpagename","讨论页面名称":"talkpagename","对话页面名称": +"talkpagename","TALKPAGENAMEE":"talkpagenamee","讨论页面名称等同":"talkpagenamee","对话页面名称等同":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主名字空间页面名称":"subjectpagename","条目页面名称":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","主名字空间页面名称等同":"subjectpagenamee","条目页面名称等同":"subjectpagenamee","REVISIONID":"revisionid","修订ID":"revisionid","REVISIONDAY":"revisionday","修订日":"revisionday","REVISIONDAY2":"revisionday2","修订日2":"revisionday2","REVISIONMONTH":"revisionmonth","修订月":"revisionmonth","REVISIONMONTH1":"revisionmonth1","修订月1":"revisionmonth1","REVISIONYEAR":"revisionyear","修订年":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","修订时间戳":"revisiontimestamp","修訂使用者":"revisionuser","REVISIONUSER":"revisionuser","修订用户":"revisionuser", +"CASCADINGSOURCES":"cascadingsources","级联来源":"cascadingsources","命名空間":"namespace","名字空间":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","名字空间等同":"namespacee","命名空間數":"namespacenumber","名字空间编号":"namespacenumber","NAMESPACENUMBER":"namespacenumber","對話空間":"talkspace","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","讨论空间等同":"talkspacee","讨论名字空间等同":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主名字空间":"subjectspace","条目名字空间":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","主名字空间等同":"subjectspacee","条目名字空间等同":"subjectspacee","文章數":"numberofarticles","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","檔案數":"numberoffiles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles", +"使用者人數量":"numberofusers","用户数":"numberofusers","NUMBEROFUSERS":"numberofusers","活躍使用者人數":"numberofactiveusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","頁面數":"numberofpages","页面数":"numberofpages","NUMBEROFPAGES":"numberofpages","管理員數":"numberofadmins","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","编辑数":"numberofedits","顯示標題":"displaytitle","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","本月1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","本月名":"currentmonthname","本月名称":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月名属格":"currentmonthnamegen","本月名称属格":"currentmonthnamegen","本月縮寫": +"currentmonthabbrev","本月简称":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","今天2":"currentday2","CURRENTDAYNAME":"currentdayname","星期":"currentdayname","今天名":"currentdayname","今天名称":"currentdayname","CURRENTYEAR":"currentyear","今年":"currentyear","目前時間":"currenttime","当前时间":"currenttime","此时":"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","当前小时":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","本地月":"localmonth","本地月2":"localmonth","LOCALMONTH1":"localmonth1","本地月1":"localmonth1","LOCALMONTHNAME":"localmonthname","本地月份名":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","本地月历":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","本地月缩写":"localmonthabbrev","LOCALDAY":"localday","本地日":"localday","LOCALDAY2":"localday2","本地日2": +"localday2","LOCALDAYNAME":"localdayname","本地日名":"localdayname","LOCALYEAR":"localyear","本地年":"localyear","LOCALTIME":"localtime","本地时间":"localtime","LOCALHOUR":"localhour","本地小时":"localhour","網站名稱":"sitename","站点名称":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","本周":"currentweek","CURRENTDOW":"currentdow","当前DOW":"currentdow","LOCALWEEK":"localweek","本地周":"localweek","LOCALDOW":"localdow","本地DOW":"localdow","REVISIONSIZE":"revisionsize","修订大小":"revisionsize","目前版本":"currentversion","当前版本":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","当前时间戳":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","本地时间戳":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","方向标记":"directionmark","內容語言":"contentlanguage","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^()(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-an.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-an.json new file mode 100644 index 0000000..947dba1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-an.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__sin_tdc__":"notoc","__notdc__":"notoc","__notoc__":"notoc","__sin_galería__":"nogallery","__nogalería__":"nogallery","__nogaleria__":"nogallery","__nogallery__":"nogallery","__forzar_tdc__":"forcetoc","__forzartdc__":"forcetoc","__forzartoc__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc","__toc__":"toc", +"__no_editar_sección__":"noeditsection","__noeditarsección__":"noeditsection","__noeditarseccion__":"noeditsection","__noeditsection__":"noeditsection","__noconvertirtitulo__":"notitleconvert","__noconvertirtítulo__":"notitleconvert","__noct___":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__noconvertircontenido__":"nocontentconvert","__nocc___":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__VINCULARANUEVASECCION__":"newsectionlink","__ENLACECREARSECCIÓN__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NOVINCULARANUEVASECCION__":"nonewsectionlink","__SINENLACECREARSECCIÓN__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATEGORÍAOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECCIÓNESTÁTICA__":"staticredirect", +"__REDIRECCIONESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIGUACION__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"en":"ns","edn":"ns","ns":"ns","nse":"nse","codificarurl":"urlencode","urlencode":"urlencode","primerominus":"lcfirst","primerominús":"lcfirst","lcfirst":"lcfirst","primeromayus":"ucfirst","primeromayús":"ucfirst","ucfirst":"ucfirst","minus":"lc","minús":"lc","lc":"lc","mayus":"uc","mayús":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","urllocalc":"localurle","localurle":"localurle","urlcompleta":"fullurl","fullurl":"fullurl","urlcompletac":"fullurle","fullurle":"fullurle","urlcanonica":"canonicalurl","canonicalurl":"canonicalurl","urlcanonicac":"canonicalurle","canonicalurle":"canonicalurle","formatonúmero":"formatnum","formatonumero":"formatnum","formatnum":"formatnum","gramatica":"grammar","gramática":"grammar", +"grammar":"grammar","género":"gender","genero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#luenga":"language","#idioma":"language","#language":"language","rellenarizquierda":"padleft","rellenarizq":"padleft","padleft":"padleft","rellenarderecha":"padright","rellenarder":"padright","padright":"padright","anchorencode":"anchorencode","rutaarchivo":"filepath","rutarchivo":"filepath","rutadearchivo":"filepath","filepath":"filepath","iddepágina":"pageid","idpágina":"pageid","iddepagina":"pageid","idpagina":"pageid","pageid":"pageid","int":"int","#especial":"special","#espezial":"special","#special":"special","#especialc":"speciale","#speciale":"speciale","#etiqueta":"tag","#tag":"tag","#formatodefecha":"formatdate","#formatearfecha":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#destino":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#invocar":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if", +"#siigual":"ifeq","#ifeq":"ifeq","#según":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierror":"iferror","#iferror":"iferror","#tiempo":"time","#time":"time","#tiempol":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#árboldecategorías":"categorytree","#arboldecategorias":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","nointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propiedad":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","rutaartículo":"articlepath","rutaarticulo":"articlepath","articlepath":"articlepath","servidor":"server","server":"server","nombreservidor":"servername","servername":"servername","rutascript":"scriptpath","rutadescript":"scriptpath","scriptpath": +"scriptpath","rutaestilo":"stylepath","rutadeestilo":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NÚMEROENGRUPO":"numberingroup","NUMEROENGRUPO":"numberingroup","NUMENGRUPO":"numberingroup","NÚMENGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENAR":"defaultsort","CLAVEDEORDENPREDETERMINADO":"defaultsort","ORDENDECATEGORIAPREDETERMINADO":"defaultsort","ORDENDECATEGORÍAPREDETERMINADO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PÁGINASENCATEGORÍA":"pagesincategory","PÁGINASENCAT":"pagesincategory","PAGSENCAT":"pagesincategory","PAGINASENCATEGORIA":"pagesincategory","PAGINASENCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMAÑOPÁGINA":"pagesize","TAMAÑODEPÁGINA":"pagesize","TAMAÑOPAGINA":"pagesize","TAMAÑODEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECCIÓN": +"protectionlevel","NIVELDEPROTECCION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMBREDEPAGINA":"pagename","NOMBREDEPÁGINA":"pagename","PAGENAME":"pagename","NOMBREDEPAGINAC":"pagenamee","NOMBREDEPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMBRECOMPLETODEPÁGINA":"fullpagename","NOMBRECOMPLETODEPAGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMBRECOMPLETODEPAGINAC":"fullpagenamee","NOMBRECOMPLETODEPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMBREDESUBPAGINA":"subpagename","NOMBREDESUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMBREDESUBPAGINAC":"subpagenamee","NOMBREDESUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMBREDEPAGINARAIZ":"rootpagename","NOMBREDEPÁGINARAÍZ":"rootpagename","ROOTPAGENAME":"rootpagename","NOMBREDEPAGINARAIZC":"rootpagenamee","NOMBREDEPÁGINARAÍZC":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBREDEPAGINABASE":"basepagename","NOMBREDEPÁGINABASE": +"basepagename","BASEPAGENAME":"basepagename","NOMBREDEPAGINABASEC":"basepagenamee","NOMBREDEPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMBREDEPÁGINADEDISCUSIÓN":"talkpagename","NOMBREDEPAGINADEDISCUSION":"talkpagename","NOMBREDEPAGINADISCUSION":"talkpagename","NOMBREDEPÁGINADISCUSIÓN":"talkpagename","TALKPAGENAME":"talkpagename","NOMBREDEPÁGINADEDISCUSIÓNC":"talkpagenamee","NOMBREDEPAGINADEDISCUSIONC":"talkpagenamee","NOMBREDEPAGINADISCUSIONC":"talkpagenamee","NOMBREDEPÁGINADISCUSIÓNC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMBREDEPAGINADETEMA":"subjectpagename","NOMBREDEPÁGINADETEMA":"subjectpagename","NOMBREDEPÁGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEARTICULO":"subjectpagename","NOMBREDEPÁGINADEARTÍCULO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMBREDEPAGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADETEMAC":"subjectpagenamee", +"NOMBREDEPÁGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEARTICULOC":"subjectpagenamee","NOMBREDEPÁGINADEARTÍCULOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDEREVISION":"revisionid","IDREVISION":"revisionid","IDDEREVISIÓN":"revisionid","IDREVISIÓN":"revisionid","REVISIONID":"revisionid","DIADEREVISION":"revisionday","DIAREVISION":"revisionday","DÍADEREVISIÓN":"revisionday","DÍAREVISIÓN":"revisionday","REVISIONDAY":"revisionday","DIADEREVISION2":"revisionday2","DIAREVISION2":"revisionday2","DÍADEREVISIÓN2":"revisionday2","DÍAREVISIÓN2":"revisionday2","REVISIONDAY2":"revisionday2","MESDEREVISION":"revisionmonth","MESDEREVISIÓN":"revisionmonth","MESREVISION":"revisionmonth","MESREVISIÓN":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDEREVISION1":"revisionmonth1","MESDEREVISIÓN1":"revisionmonth1","MESREVISION1":"revisionmonth1","MESREVISIÓN1":"revisionmonth1", +"REVISIONMONTH1":"revisionmonth1","AÑODEREVISION":"revisionyear","AÑODEREVISIÓN":"revisionyear","AÑOREVISION":"revisionyear","AÑOREVISIÓN":"revisionyear","REVISIONYEAR":"revisionyear","MARCADEHORADEREVISION":"revisiontimestamp","MARCADEHORADEREVISIÓN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODEREVISION":"revisionuser","USUARIODEREVISIÓN":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACIODENOMBRES":"namespace","ESPACIODENOMBRE":"namespace","NAMESPACE":"namespace","ESPACIODENOMBRESE":"namespacee","ESPACIODENOMBREC":"namespacee","NAMESPACEE":"namespacee","NÚMERODELESPACIO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACIODEDISCUSION":"talkspace","ESPACIODEDISCUSIÓN":"talkspace","TALKSPACE":"talkspace","ESPACIODEDISCUSIONC":"talkspacee","TALKSPACEE":"talkspacee","ESPACIODEASUNTO":"subjectspace","ESPACIODETEMA":"subjectspace","ESPACIODEARTÍCULO":"subjectspace","ESPACIODEARTICULO":"subjectspace", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACIODETEMAC":"subjectspacee","ESPACIODEASUNTOC":"subjectspacee","ESPACIODEARTICULOC":"subjectspacee","ESPACIODEARTÍCULOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NÚMERODEARTÍCULOS":"numberofarticles","NUMERODEARTICULOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NÚMERODEARCHIVOS":"numberoffiles","NUMERODEARCHIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NÚMERODEPÁGINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NÚMEROADMINIISTRADORES":"numberofadmins","NÚMEROADMINS":"numberofadmins","NUMEROADMINS":"numberofadmins","NUMEROADMINISTRADORES":"numberofadmins", +"NUMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINS":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NÚMERODEEDICIONES":"numberofedits","NUMERODEEDICIONES":"numberofedits","NUMBEROFEDITS":"numberofedits","TÍTOL":"displaytitle","MOSTRARTÍTULO":"displaytitle","MOSTRARTITULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESACTUAL":"currentmonth","MESACTUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MESACTUALCOMPLETO":"currentmonthname","NOMBREMESACTUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MESACTUALGENITIVO":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESACTUALABREVIADO":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DÍAACTUAL":"currentday","DIAACTUAL":"currentday","DÍA_ACTUAL":"currentday","DIA_ACTUAL":"currentday", +"CURRENTDAY":"currentday","DÍAACTUAL2":"currentday2","DIAACTUAL2":"currentday2","DÍA_ACTUAL2":"currentday2","DIA_ACTUAL2":"currentday2","CURRENTDAY2":"currentday2","NOMBREDÍAACTUAL":"currentdayname","NOMBREDIAACTUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","AÑOACTUAL":"currentyear","AÑO_ACTUAL":"currentyear","CURRENTYEAR":"currentyear","HORA_MINUTOS_ACTUAL":"currenttime","HORAMINUTOSACTUAL":"currenttime","TIEMPOACTUAL":"currenttime","CURRENTTIME":"currenttime","HORAACTUAL":"currenthour","HORA_ACTUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","MESLOCALCOMPLETO":"localmonthname","NOMBREMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","MESLOCALGENITIVO":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREVIADO":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DÍALOCAL": +"localday","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","DÍALOCAL2":"localday2","LOCALDAY2":"localday2","NOMBREDIALOCAL":"localdayname","NOMBREDÍALOCAL":"localdayname","LOCALDAYNAME":"localdayname","AÑOLOCAL":"localyear","LOCALYEAR":"localyear","HORAMINUTOSLOCAL":"localtime","TIEMPOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMBREDELSITIO":"sitename","SITENAME":"sitename","SEMANAACTUAL":"currentweek","CURRENTWEEK":"currentweek","DDSACTUAL":"currentdow","DIADESEMANAACTUAL":"currentdow","DÍADESEMANAACTUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DDSLOCAL":"localdow","DIADESEMANALOCAL":"localdow","DÍADESEMANALOCAL":"localdow","LOCALDOW":"localdow","TAMAÑODEREVISIÓN":"revisionsize","TAMAÑODEREVISION":"revisionsize","REVISIONSIZE":"revisionsize","BERSIÓNAUTUAL":"currentversion","BERSIONAUTUAL":"currentversion","REVISIÓNACTUAL":"currentversion","VERSIONACTUAL": +"currentversion","VERSIÓNACTUAL":"currentversion","CURRENTVERSION":"currentversion","MARCADEHORAACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MARCADEHORALOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","IDIOMADELCONTENIDO":"contentlanguage","IDIOMADELCONT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záéíóúñ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ang.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ang.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ang.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ar.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ar.json new file mode 100644 index 0000000..296813f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ar.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__لافهرس__":"notoc","__notoc__":"notoc","__لامعرض__":"nogallery","__nogallery__":"nogallery","__لصق_فهرس__":"forcetoc","__forcetoc__":"forcetoc","__فهرس__":"toc","__toc__":"toc","__لاتحريرقسم__":"noeditsection","__noeditsection__":"noeditsection","__لاتحويل_عنوان__": +"notitleconvert","__لاتع__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__لاتحويل_محتوى__":"nocontentconvert","__لاتم__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__وصلة_قسم_جديد__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","لا_وصلة_قسم_جديد__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__تصنيف_مخفي__":"hiddencat","__HIDDENCAT__":"hiddencat","__توقع_تصنيف_غير_مستخدم__":"expectunusedcategory","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__فهرسة__":"index","__INDEX__":"index","__لافهرسة__":"noindex","__NOINDEX__":"noindex","__تحويلة_إستاتيكية__":"staticredirect","__تحويلة_ساكنة__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}], +"functionSynonyms":[{"نط":"ns","ns":"ns","نطم":"nse","nse":"nse","كود_المسار":"urlencode","urlencode":"urlencode","عنوان_كبير":"lcfirst","lcfirst":"lcfirst","عنوان_صغير":"ucfirst","ucfirst":"ucfirst","صغير":"lc","lc":"lc","كبير":"uc","uc":"uc","مسار_محلي":"localurl","localurl":"localurl","عنوان_المسار_المحلي":"localurle","localurle":"localurle","عنوان_كامل":"fullurl","fullurl":"fullurl","مسار_كامل":"fullurle","fullurle":"fullurle","عنوان_قاعدة":"canonicalurl","canonicalurl":"canonicalurl","مسار_قاعدة":"canonicalurle","canonicalurle":"canonicalurle","صيغة_رقم":"formatnum","formatnum":"formatnum","قواعد_اللغة":"grammar","grammar":"grammar","نوع":"gender","gender":"gender","جمع":"plural","plural":"plural","ثا":"bidi","bidi":"bidi","#لغة":"language","#language":"language","باد_يسار":"padleft","padleft":"padleft","باد_يمين":"padright","padright": +"padright","كود_الأنكور":"anchorencode","anchorencode":"anchorencode","مسار_الملف":"filepath","filepath":"filepath","رقم_صفحة":"pageid","pageid":"pageid","محتوى":"int","int":"int","#خاص":"special","#special":"special","#عنوان_خاص":"speciale","#speciale":"speciale","#وسم":"tag","#tag":"tag","#تهيئة_التاريخ":"formatdate","#تهيئة_تاريخ":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#هدف":"target","#target":"target","#بابل":"babel","#babel":"babel","#coordinates":"coordinates","#استدعاء":"invoke","#invoke":"invoke","#related":"related","#assessment":"assessment","#لو":"if","#if":"if","#لومعادلة":"ifeq","#ifeq":"ifeq","#تبديل":"switch","#switch":"switch","#لوموجود":"ifexist","#ifexist":"ifexist","#لوتعبير":"ifexpr","#ifexpr":"ifexpr","#لوخطأ":"iferror","#iferror":"iferror","#وقت":"time","#time":"time","#تيمل":"timel","#timel":"timel","#تعبير": +"expr","#expr":"expr","#ريلتوآبس":"rel2abs","#rel2abs":"rel2abs","#أجزاء_العنوان":"titleparts","#titleparts":"titleparts","#شجرة_تصنيف":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","لا_وصلات_لغة_خارجية":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#خاصية":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#مرشد":"mentor","#mentor":"mentor","مسار_المقالة":"articlepath","articlepath":"articlepath","خادم":"server","server":"server","اسم_الخادم":"servername","servername":"servername","مسار_السكريبت":"scriptpath","مسار_سكريبت":"scriptpath","scriptpath":"scriptpath","مسار_الهيئة":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","اسم_مستودع_وب":"wbreponame","wbreponame": +"wbreponame"},{"عدد_في_المجموعة":"numberingroup","عدد_في_مجموعة":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ترتيب_افتراضي":"defaultsort","مفتاح_ترتيب_افتراضي":"defaultsort","ترتيب_تصنيف_افتراضي":"defaultsort","ترتيب_غيابي":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","صفحات_في_التصنيف":"pagesincategory","صفحات_في_تصنيف":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","حجم_الصفحة":"pagesize","PAGESIZE":"pagesize","مستوى_الحماية":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","انتهاء_الحماية":"protectionexpiry","PROTECTIONEXPIRY":"protectionexpiry","اسم_الصفحة":"pagename","PAGENAME":"pagename","عنوان_الصفحة":"pagenamee","PAGENAMEE":"pagenamee","اسم_الصفحة_الكامل": +"fullpagename","اسم_صفحة_كامل":"fullpagename","اسم_كامل":"fullpagename","FULLPAGENAME":"fullpagename","عنوان_الصفحة_الكامل":"fullpagenamee","عنوان_صفحة_كامل":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","اسم_الصفحة_الفرعي":"subpagename","SUBPAGENAME":"subpagename","عنوان_الصفحة_الفرعي":"subpagenamee","SUBPAGENAMEE":"subpagenamee","جذر_اسم_الصفحة":"rootpagename","ROOTPAGENAME":"rootpagename","عنوان_جذر_الصفحة":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","اسم_الصفحة_الأساسي":"basepagename","BASEPAGENAME":"basepagename","عنوان_الصفحة_الأساسي":"basepagenamee","BASEPAGENAMEE":"basepagenamee","اسم_صفحة_النقاش":"talkpagename","TALKPAGENAME":"talkpagename","عنوان_صفحة_النقاش":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","اسم_صفحة_الموضوع":"subjectpagename","اسم_صفحة_المقالة": +"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","عنوان_صفحة_الموضوع":"subjectpagenamee","عنوان_صفحة_المقالة":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","رقم_المراجعة":"revisionid","REVISIONID":"revisionid","يوم_المراجعة":"revisionday","REVISIONDAY":"revisionday","يوم_المراجعة2":"revisionday2","REVISIONDAY2":"revisionday2","شهر_المراجعة":"revisionmonth","REVISIONMONTH":"revisionmonth","شهر_المراجعة1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","عام_المراجعة":"revisionyear","REVISIONYEAR":"revisionyear","طابع_وقت_المراجعة":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","مستخدم_المراجعة":"revisionuser","REVISIONUSER":"revisionuser","مصادر_مضمنة":"cascadingsources","CASCADINGSOURCES":"cascadingsources","نطاق":"namespace","NAMESPACE": +"namespace","عنوان_نطاق":"namespacee","NAMESPACEE":"namespacee","عدد_نطاق":"namespacenumber","NAMESPACENUMBER":"namespacenumber","نطاق_النقاش":"talkspace","TALKSPACE":"talkspace","عنوان_النقاش":"talkspacee","TALKSPACEE":"talkspacee","نطاق_الموضوع":"subjectspace","نطاق_المقالة":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","عنوان_نطاق_الموضوع":"subjectspacee","عنوان_نطاق_المقالة":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","عدد_المقالات":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","عدد_الملفات":"numberoffiles","NUMBEROFFILES":"numberoffiles","عدد_المستخدمين":"numberofusers","NUMBEROFUSERS":"numberofusers","عدد_المستخدمين_النشطين":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","عدد_الصفحات":"numberofpages","NUMBEROFPAGES":"numberofpages", +"عدد_الإداريين":"numberofadmins","NUMBEROFADMINS":"numberofadmins","عدد_التعديلات":"numberofedits","NUMBEROFEDITS":"numberofedits","عرض_العنوان":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","شهر_حالي":"currentmonth","شهر_حالي2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","شهر_حالي1":"currentmonth1","CURRENTMONTH1":"currentmonth1","اسم_الشهر_الحالي":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","اسم_الشهر_الحالي_المولد":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","اختصار_الشهر_الحالي":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","يوم_حالي":"currentday","CURRENTDAY":"currentday","يوم_حالي2":"currentday2","CURRENTDAY2":"currentday2","اسم_اليوم_الحالي":"currentdayname","CURRENTDAYNAME":"currentdayname","عام_حالي":"currentyear","CURRENTYEAR":"currentyear" +,"وقت_حالي":"currenttime","CURRENTTIME":"currenttime","ساعة_حالية":"currenthour","CURRENTHOUR":"currenthour","شهر_محلي":"localmonth","شهر_محلي2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","شهر_محلي1":"localmonth1","LOCALMONTH1":"localmonth1","اسم_الشهر_المحلي":"localmonthname","LOCALMONTHNAME":"localmonthname","اسم_الشهر_المحلي_المولد":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","اختصار_الشهر_المحلي":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","يوم_محلي":"localday","LOCALDAY":"localday","يوم_محلي2":"localday2","LOCALDAY2":"localday2","اسم_اليوم_المحلي":"localdayname","LOCALDAYNAME":"localdayname","عام_محلي":"localyear","LOCALYEAR":"localyear","وقت_محلي":"localtime","LOCALTIME":"localtime","ساعة_محلية":"localhour","LOCALHOUR":"localhour","اسم_الموقع":"sitename","SITENAME":"sitename", +"أسبوع_حالي":"currentweek","CURRENTWEEK":"currentweek","يوم_حالي_مأ":"currentdow","CURRENTDOW":"currentdow","أسبوع_محلي":"localweek","LOCALWEEK":"localweek","يوم_محلي_مأ":"localdow","LOCALDOW":"localdow","حجم_المراجعة":"revisionsize","REVISIONSIZE":"revisionsize","نسخة_حالية":"currentversion","CURRENTVERSION":"currentversion","طابع_الوقت_الحالي":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","طابع_الوقت_المحلي":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","علامة_الاتجاه":"directionmark","علامة_اتجاه":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","لغة_المحتوى":"contentlanguage","لغة_محتوى":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","لغة_الصفحة":"pagelanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zء-ي\\x{0610}-\\x{061A}\\x{064B}-\\x{065F}\\x{0670}\\x{06D6}-\\x{06DC}\\x{06DF}-\\x{06E4}\\x{06E7}\\x{06E8}\\x{06EA}-\\x{06ED}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-arc.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-arc.json new file mode 100644 index 0000000..bacb42f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-arc.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","ܬܘܪܨ_ܡܡܠܠܐ":"grammar","grammar":"grammar","ܓܢܣܐ":"gender","gender":"gender","plural":"plural","bidi":"bidi","#ܠܫܢܐ":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#ܕܝܠܢܝܐ":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate", +"#dateformat":"formatdate","#target":"target","#ܒܒܠ":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ܫܡܐ_ܕܦܐܬܐ":"pagename","PAGENAME":"pagename","ܟܘܢܝܐ_ܕܦܐܬܐ":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources", +"ܚܩܠܐ":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ܡܢܝܢܐ_ܕܡܠܘܐ̈ܐ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ܡܢܝܢܐ_ܕܠܦܦ̈ܐ":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ܡܢܝܢܐ_ܕܦܐܬܬ̈ܐ":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME": +"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ary.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ary.json new file mode 100644 index 0000000..e124f3b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ary.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__لافهرس__":"notoc","__notoc__":"notoc","__لامعرض__":"nogallery","__nogallery__":"nogallery","__لصق_فهرس__":"forcetoc","__forcetoc__":"forcetoc","__فهرس__":"toc","__toc__":"toc","__لاتحريرقسم__":"noeditsection","__noeditsection__":"noeditsection","__لاتحويل_عنوان__": +"notitleconvert","__لاتع__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__لاتحويل_محتوى__":"nocontentconvert","__لاتم__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__وصلة_قسم_جديد__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","لا_وصلة_قسم_جديد__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__تصنيف_مخفي__":"hiddencat","__HIDDENCAT__":"hiddencat","__توقع_تصنيف_غير_مستخدم__":"expectunusedcategory","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__فهرسة__":"index","__INDEX__":"index","__لافهرسة__":"noindex","__NOINDEX__":"noindex","__تحويلة_إستاتيكية__":"staticredirect","__تحويلة_ساكنة__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}], +"functionSynonyms":[{"نط":"ns","ns":"ns","نطم":"nse","nse":"nse","كود_المسار":"urlencode","urlencode":"urlencode","عنوان_كبير":"lcfirst","lcfirst":"lcfirst","عنوان_صغير":"ucfirst","ucfirst":"ucfirst","صغير":"lc","lc":"lc","كبير":"uc","uc":"uc","مسار_محلي":"localurl","localurl":"localurl","عنوان_المسار_المحلي":"localurle","localurle":"localurle","عنوان_كامل":"fullurl","fullurl":"fullurl","مسار_كامل":"fullurle","fullurle":"fullurle","عنوان_قاعدة":"canonicalurl","canonicalurl":"canonicalurl","مسار_قاعدة":"canonicalurle","canonicalurle":"canonicalurle","صيغة_رقم":"formatnum","formatnum":"formatnum","قواعد_اللغة":"grammar","grammar":"grammar","نوع":"gender","gender":"gender","جمع":"plural","plural":"plural","ثا":"bidi","bidi":"bidi","#لغة":"language","#language":"language","باد_يسار":"padleft","padleft":"padleft","باد_يمين":"padright","padright": +"padright","كود_الأنكور":"anchorencode","anchorencode":"anchorencode","مسار_الملف":"filepath","filepath":"filepath","رقم_صفحة":"pageid","pageid":"pageid","محتوى":"int","int":"int","#خاص":"special","#special":"special","#عنوان_خاص":"speciale","#speciale":"speciale","#وسم":"tag","#tag":"tag","#تهيئة_التاريخ":"formatdate","#تهيئة_تاريخ":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#هدف":"target","#target":"target","#بابل":"babel","#babel":"babel","#coordinates":"coordinates","#استدعاء":"invoke","#invoke":"invoke","#related":"related","#لو":"if","#if":"if","#لومعادلة":"ifeq","#ifeq":"ifeq","#تبديل":"switch","#switch":"switch","#لوموجود":"ifexist","#ifexist":"ifexist","#لوتعبير":"ifexpr","#ifexpr":"ifexpr","#لوخطأ":"iferror","#iferror":"iferror","#وقت":"time","#time":"time","#تيمل":"timel","#timel":"timel","#تعبير":"expr","#expr":"expr", +"#ريلتوآبس":"rel2abs","#rel2abs":"rel2abs","#أجزاء_العنوان":"titleparts","#titleparts":"titleparts","#شجرة_تصنيف":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","لا_وصلات_لغة_خارجية":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#خاصية":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#مرشد":"mentor","#mentor":"mentor","مسار_المقالة":"articlepath","articlepath":"articlepath","خادم":"server","server":"server","اسم_الخادم":"servername","servername":"servername","مسار_السكريبت":"scriptpath","مسار_سكريبت":"scriptpath","scriptpath":"scriptpath","مسار_الهيئة":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","اسم_مستودع_وب":"wbreponame","wbreponame":"wbreponame"},{ +"عدد_في_المجموعة":"numberingroup","عدد_في_مجموعة":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ترتيب_افتراضي":"defaultsort","مفتاح_ترتيب_افتراضي":"defaultsort","ترتيب_تصنيف_افتراضي":"defaultsort","ترتيب_غيابي":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","صفحات_في_التصنيف":"pagesincategory","صفحات_في_تصنيف":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","حجم_الصفحة":"pagesize","PAGESIZE":"pagesize","مستوى_الحماية":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","انتهاء_الحماية":"protectionexpiry","PROTECTIONEXPIRY":"protectionexpiry","اسم_الصفحة":"pagename","PAGENAME":"pagename","عنوان_الصفحة":"pagenamee","PAGENAMEE":"pagenamee","اسم_الصفحة_الكامل":"fullpagename", +"اسم_صفحة_كامل":"fullpagename","اسم_كامل":"fullpagename","FULLPAGENAME":"fullpagename","عنوان_الصفحة_الكامل":"fullpagenamee","عنوان_صفحة_كامل":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","اسم_الصفحة_الفرعي":"subpagename","SUBPAGENAME":"subpagename","عنوان_الصفحة_الفرعي":"subpagenamee","SUBPAGENAMEE":"subpagenamee","جذر_اسم_الصفحة":"rootpagename","ROOTPAGENAME":"rootpagename","عنوان_جذر_الصفحة":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","اسم_الصفحة_الأساسي":"basepagename","BASEPAGENAME":"basepagename","عنوان_الصفحة_الأساسي":"basepagenamee","BASEPAGENAMEE":"basepagenamee","اسم_صفحة_النقاش":"talkpagename","TALKPAGENAME":"talkpagename","عنوان_صفحة_النقاش":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","اسم_صفحة_الموضوع":"subjectpagename","اسم_صفحة_المقالة":"subjectpagename", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","عنوان_صفحة_الموضوع":"subjectpagenamee","عنوان_صفحة_المقالة":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","رقم_المراجعة":"revisionid","REVISIONID":"revisionid","يوم_المراجعة":"revisionday","REVISIONDAY":"revisionday","يوم_المراجعة2":"revisionday2","REVISIONDAY2":"revisionday2","شهر_المراجعة":"revisionmonth","REVISIONMONTH":"revisionmonth","شهر_المراجعة1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","عام_المراجعة":"revisionyear","REVISIONYEAR":"revisionyear","طابع_وقت_المراجعة":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","مستخدم_المراجعة":"revisionuser","REVISIONUSER":"revisionuser","مصادر_مضمنة":"cascadingsources","CASCADINGSOURCES":"cascadingsources","نطاق":"namespace","NAMESPACE":"namespace", +"عنوان_نطاق":"namespacee","NAMESPACEE":"namespacee","عدد_نطاق":"namespacenumber","NAMESPACENUMBER":"namespacenumber","نطاق_النقاش":"talkspace","TALKSPACE":"talkspace","عنوان_النقاش":"talkspacee","TALKSPACEE":"talkspacee","نطاق_الموضوع":"subjectspace","نطاق_المقالة":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","عنوان_نطاق_الموضوع":"subjectspacee","عنوان_نطاق_المقالة":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","عدد_المقالات":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","عدد_الملفات":"numberoffiles","NUMBEROFFILES":"numberoffiles","عدد_المستخدمين":"numberofusers","NUMBEROFUSERS":"numberofusers","عدد_المستخدمين_النشطين":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","عدد_الصفحات":"numberofpages","NUMBEROFPAGES":"numberofpages", +"عدد_الإداريين":"numberofadmins","NUMBEROFADMINS":"numberofadmins","عدد_التعديلات":"numberofedits","NUMBEROFEDITS":"numberofedits","عرض_العنوان":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","شهر_حالي":"currentmonth","شهر_حالي2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","شهر_حالي1":"currentmonth1","CURRENTMONTH1":"currentmonth1","اسم_الشهر_الحالي":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","اسم_الشهر_الحالي_المولد":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","اختصار_الشهر_الحالي":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","يوم_حالي":"currentday","CURRENTDAY":"currentday","يوم_حالي2":"currentday2","CURRENTDAY2":"currentday2","اسم_اليوم_الحالي":"currentdayname","CURRENTDAYNAME":"currentdayname","عام_حالي":"currentyear","CURRENTYEAR":"currentyear" +,"وقت_حالي":"currenttime","CURRENTTIME":"currenttime","ساعة_حالية":"currenthour","CURRENTHOUR":"currenthour","شهر_محلي":"localmonth","شهر_محلي2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","شهر_محلي1":"localmonth1","LOCALMONTH1":"localmonth1","اسم_الشهر_المحلي":"localmonthname","LOCALMONTHNAME":"localmonthname","اسم_الشهر_المحلي_المولد":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","اختصار_الشهر_المحلي":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","يوم_محلي":"localday","LOCALDAY":"localday","يوم_محلي2":"localday2","LOCALDAY2":"localday2","اسم_اليوم_المحلي":"localdayname","LOCALDAYNAME":"localdayname","عام_محلي":"localyear","LOCALYEAR":"localyear","وقت_محلي":"localtime","LOCALTIME":"localtime","ساعة_محلية":"localhour","LOCALHOUR":"localhour","اسم_الموقع":"sitename","SITENAME":"sitename", +"أسبوع_حالي":"currentweek","CURRENTWEEK":"currentweek","يوم_حالي_مأ":"currentdow","CURRENTDOW":"currentdow","أسبوع_محلي":"localweek","LOCALWEEK":"localweek","يوم_محلي_مأ":"localdow","LOCALDOW":"localdow","حجم_المراجعة":"revisionsize","REVISIONSIZE":"revisionsize","نسخة_حالية":"currentversion","CURRENTVERSION":"currentversion","طابع_الوقت_الحالي":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","طابع_الوقت_المحلي":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","علامة_الاتجاه":"directionmark","علامة_اتجاه":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","لغة_المحتوى":"contentlanguage","لغة_محتوى":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","لغة_الصفحة":"pagelanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zء-ي\\x{0610}-\\x{061A}\\x{064B}-\\x{065F}\\x{0670}\\x{06D6}-\\x{06DC}\\x{06DF}-\\x{06E4}\\x{06E7}\\x{06E8}\\x{06EA}-\\x{06ED}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-arz.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-arz.json new file mode 100644 index 0000000..9b6ca93 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-arz.json @@ -0,0 +1,18 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__من_غير_فهرس__":"notoc","__لافهرس__":"notoc","__notoc__":"notoc","__من_غير_معرض__":"nogallery","__لامعرض__":"nogallery","__nogallery__":"nogallery","__لصق_فهرس__":"forcetoc","__forcetoc__":"forcetoc","__فهرس__":"toc","__toc__":"toc","__من_غير_تحريرقسم__":"noeditsection", +"__لاتحريرقسم__":"noeditsection","__noeditsection__":"noeditsection","__من_غيرتحويل_عنوان__":"notitleconvert","__لاتع__":"notitleconvert","__لاتحويل_عنوان__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__من_غير_تحويل_محتوى__":"nocontentconvert","__لاتم__":"nocontentconvert","__لاتحويل_محتوى__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__وصلة_قسم_جديد__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__من_غير_وصلة_قسم_جديد__":"nonewsectionlink","من_غير_وصلة_قسم_جديد__":"nonewsectionlink","لا_وصلة_قسم_جديد__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__تصنيف_مخفي__":"hiddencat","__HIDDENCAT__":"hiddencat","__اتوقع_تصنيف_مش_مستخدم__":"expectunusedcategory","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory", +"__توقع_تصنيف_غير_مستخدم__":"expectunusedcategory","__فهرسة__":"index","__INDEX__":"index","__لافهرسة__":"noindex","__NOINDEX__":"noindex","__تحويله_إستاتيكيه__":"staticredirect","__تحويله_ساكنه__":"staticredirect","__تحويلة_إستاتيكية__":"staticredirect","__تحويلة_ساكنة__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"نط":"ns","ns":"ns","نطم":"nse","nse":"nse","كود_المسار":"urlencode","urlencode":"urlencode","عنوان_كبير":"lcfirst","lcfirst":"lcfirst","عنوان_صغير":"ucfirst","ucfirst":"ucfirst","صغير":"lc","lc":"lc","كبير":"uc","uc":"uc","مسار_محلى":"localurl","مسار_محلي":"localurl","localurl":"localurl","عنوان_المسار_المحلى":"localurle","عنوان_المسار_المحلي":"localurle", +"localurle":"localurle","عنوان_كامل":"fullurl","fullurl":"fullurl","مسار_كامل":"fullurle","fullurle":"fullurle","عنوان_قاعده":"canonicalurl","عنوان_قاعدة":"canonicalurl","canonicalurl":"canonicalurl","مسار_قاعده":"canonicalurle","مسار_قاعدة":"canonicalurle","canonicalurle":"canonicalurle","صيغة_رقم":"formatnum","formatnum":"formatnum","قواعد_اللغة":"grammar","grammar":"grammar","نوع":"gender","gender":"gender","جمع":"plural","plural":"plural","بيدى":"bidi","bidi":"bidi","ثا":"bidi","#لغة":"language","#language":"language","باد_يسار":"padleft","padleft":"padleft","باد_يمين":"padright","padright":"padright","كود_الأنكور":"anchorencode","anchorencode":"anchorencode","مسار_الملف":"filepath","filepath":"filepath","رقم_صفحه":"pageid","رقم_صفحة":"pageid","pageid":"pageid","محتوى":"int","int":"int","#خاص":"special","#special":"special", +"#عنوان_خاص":"speciale","#speciale":"speciale","#وسم":"tag","#tag":"tag","#تهيئه_التاريخ":"formatdate","#تهيئه_تاريخ":"formatdate","#تهيئة_التاريخ":"formatdate","#تهيئة_تاريخ":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#هدف":"target","#target":"target","#بابل":"babel","#babel":"babel","#coordinates":"coordinates","#استدعا":"invoke","#استدعاء":"invoke","#invoke":"invoke","#related":"related","#لو":"if","#if":"if","#لومعادلة":"ifeq","#ifeq":"ifeq","#تبديل":"switch","#switch":"switch","#لوموجود":"ifexist","#ifexist":"ifexist","#لوتعبير":"ifexpr","#ifexpr":"ifexpr","#لوخطأ":"iferror","#iferror":"iferror","#وقت":"time","#time":"time","#تيمل":"timel","#timel":"timel","#تعبير":"expr","#expr":"expr","#ريلتوآبس":"rel2abs","#rel2abs":"rel2abs","#أجزاء_العنوان":"titleparts","#titleparts":"titleparts","#شجرة_تصنيف": +"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","من_غير_وصلات_لغه_خارجيه":"noexternallanglinks","لا_وصلات_لغة_خارجية":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#خاصيه":"property","#خاصية":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#مرشد":"mentor","#mentor":"mentor","مسار_المقاله":"articlepath","مسار_المقالة":"articlepath","articlepath":"articlepath","خادم":"server","server":"server","اسم_الخادم":"servername","servername":"servername","مسار_السكريبت":"scriptpath","مسار_سكريبت":"scriptpath","scriptpath":"scriptpath","مسار_الهيئة":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","اسم_ريبو_وب":"wbreponame","اسم_مستودع_وب":"wbreponame","wbreponame": +"wbreponame"},{"عدد_فى_المجموعه":"numberingroup","عدد_فى_مجموعه":"numberingroup","عدد_في_المجموعه":"numberingroup","عدد_في_مجموعة":"numberingroup","عدد_في_المجموعة":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ترتيب_قياسى":"defaultsort","ترتيب_افتراضى":"defaultsort","مفتاح_ترتيب_قياسى":"defaultsort","مفتاح_ترتيب_افتراضى":"defaultsort","ترتيب_تصنيف_قياسى":"defaultsort","ترتيب_تصنيف_افتراضى":"defaultsort","ترتيب_قياسي":"defaultsort","ترتيب_افتراضي":"defaultsort","مفتاح_ترتيب_قياسي":"defaultsort","مفتاح_ترتيب_افتراضي":"defaultsort","ترتيب_تصنيف_قياسي":"defaultsort","ترتيب_تصنيف_افتراضي":"defaultsort","ترتيب_غيابي":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT": +"defaultsort","صفحات_في_التصنيف":"pagesincategory","صفحات_في_تصنيف":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","حجم_الصفحة":"pagesize","PAGESIZE":"pagesize","مستوى_الحمايه":"protectionlevel","مستوى_الحماية":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","نهايه_الحمايه":"protectionexpiry","PROTECTIONEXPIRY":"protectionexpiry","انتهاء_الحماية":"protectionexpiry","اسم_الصفحه":"pagename","اسم_الصفحة":"pagename","PAGENAME":"pagename","عنوان_الصفحه":"pagenamee","عنوان_الصفحة":"pagenamee","PAGENAMEE":"pagenamee","اسم_الصفحة_الكامل":"fullpagename","اسم_صفحة_كامل":"fullpagename","اسم_كامل":"fullpagename","FULLPAGENAME":"fullpagename","عنوان_الصفحه_الكامل":"fullpagenamee","عنوان_صفحة_كامل":"fullpagenamee","عنوان_كامل":"fullpagenamee", +"عنوان_الصفحة_الكامل":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","اسم_الصفحه_الفرعي":"subpagename","اسم_الصفحة_الفرعي":"subpagename","SUBPAGENAME":"subpagename","عنوان_الصفحه_الفرعى":"subpagenamee","عنوان_الصفحه_الفرعي":"subpagenamee","عنوان_الصفحة_الفرعي":"subpagenamee","SUBPAGENAMEE":"subpagenamee","جدر_اسم_الصفحه":"rootpagename","جذر_اسم_الصفحة":"rootpagename","ROOTPAGENAME":"rootpagename","عنوان_جدر_الصفحه":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","عنوان_جذر_الصفحة":"rootpagenamee","اسم_الصفحه_الأساسى":"basepagename","اسم_الصفحة_الأساسي":"basepagename","BASEPAGENAME":"basepagename","عنوان_الصفحه_الأساسى":"basepagenamee","عنوان_الصفحة_الأساسي":"basepagenamee","BASEPAGENAMEE":"basepagenamee","اسم_صفحه_المناقشه":"talkpagename", +"اسم_صفحة_النقاش":"talkpagename","TALKPAGENAME":"talkpagename","عنوان_صفحه_المناقشه":"talkpagenamee","عنوان_صفحة_النقاش":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","اسم_صفحه_الموضوع":"subjectpagename","اسم_صفحه_المقاله":"subjectpagename","اسم_صفحة_الموضوع":"subjectpagename","اسم_صفحة_المقالة":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","عنوان_صفحه_الموضوع":"subjectpagenamee","عنوان_صفحه_المقاله":"subjectpagenamee","عنوان_صفحة_الموضوع":"subjectpagenamee","عنوان_صفحة_المقالة":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","رقم_المراجعه":"revisionid","رقم_المراجعة":"revisionid","REVISIONID":"revisionid","يوم_المراجعه":"revisionday","يوم_المراجعة":"revisionday","REVISIONDAY": +"revisionday","يوم_المراجعه2":"revisionday2","يوم_المراجعة2":"revisionday2","REVISIONDAY2":"revisionday2","شهر_المراجعه":"revisionmonth","شهر_المراجعة":"revisionmonth","REVISIONMONTH":"revisionmonth","شهر_المراجعه1":"revisionmonth1","شهر_المراجعة1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","عام_المراجعه":"revisionyear","عام_المراجعة":"revisionyear","REVISIONYEAR":"revisionyear","طابع_وقت_المراجعه":"revisiontimestamp","طابع_وقت_المراجعة":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","مستخدم_المراجعه":"revisionuser","مستخدم_المراجعة":"revisionuser","REVISIONUSER":"revisionuser","مصادر_متضمنه":"cascadingsources","CASCADINGSOURCES":"cascadingsources","مصادر_مضمنة":"cascadingsources","نطاق":"namespace","NAMESPACE":"namespace","عنوان_نطاق":"namespacee","NAMESPACEE":"namespacee", +"عدد_نطاق":"namespacenumber","NAMESPACENUMBER":"namespacenumber","نطاق_المناقشه":"talkspace","نطاق_النقاش":"talkspace","TALKSPACE":"talkspace","عنوان_المناقشه":"talkspacee","عنوان_النقاش":"talkspacee","TALKSPACEE":"talkspacee","نطاق_الموضوع":"subjectspace","نطاق_المقاله":"subjectspace","نطاق_المقالة":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","عنوان_نطاق_الموضوع":"subjectspacee","عنوان_نطاق_المقاله":"subjectspacee","عنوان_نطاق_المقالة":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","عدد_المقالات":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","عدد_الملفات":"numberoffiles","NUMBEROFFILES":"numberoffiles","عدد_اليوزرات":"numberofusers","عدد_المستخدمين":"numberofusers","NUMBEROFUSERS":"numberofusers", +"عدد_اليوزرات_النشطين":"numberofactiveusers","عدد_المستخدمين_النشطين":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","عدد_الصفحات":"numberofpages","NUMBEROFPAGES":"numberofpages","عدد_الإداريين":"numberofadmins","NUMBEROFADMINS":"numberofadmins","عدد_التعديلات":"numberofedits","NUMBEROFEDITS":"numberofedits","عرض_العنوان":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","شهر_حالى":"currentmonth","شهر_حالي2":"currentmonth","شهر_حالي":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","شهر_حالي1":"currentmonth1","CURRENTMONTH1":"currentmonth1","اسم_الشهر_الحالى":"currentmonthname","اسم_الشهر_الحالي":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","اسم_الشهر_الحالى_المولد":"currentmonthnamegen","اسم_الشهر_الحالي_المولد":"currentmonthnamegen", +"CURRENTMONTHNAMEGEN":"currentmonthnamegen","اختصار_الشهر_الحالى":"currentmonthabbrev","اختصار_الشهر_الحالي":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","يوم_حالى":"currentday","يوم_حالي":"currentday","CURRENTDAY":"currentday","يوم_حالى2":"currentday2","يوم_حالي2":"currentday2","CURRENTDAY2":"currentday2","اسم_اليوم_الحالى":"currentdayname","اسم_اليوم_الحالي":"currentdayname","CURRENTDAYNAME":"currentdayname","عام_حالى":"currentyear","عام_حالي":"currentyear","CURRENTYEAR":"currentyear","وقت_حالى":"currenttime","وقت_حالي":"currenttime","CURRENTTIME":"currenttime","ساعه_حاليه":"currenthour","ساعة_حالية":"currenthour","CURRENTHOUR":"currenthour","شهر_محلى":"localmonth","شهر_محلي2":"localmonth","شهر_محلي":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","شهر_محلى1":"localmonth1", +"شهر_محلي1":"localmonth1","LOCALMONTH1":"localmonth1","اسم_الشهر_المحلى":"localmonthname","اسم_شهر_محلى":"localmonthname","اسم_الشهر_المحلي":"localmonthname","اسم_شهر_محلي":"localmonthname","LOCALMONTHNAME":"localmonthname","اسم_الشهر_المحلى_المولد":"localmonthnamegen","اسم_شهر_محلى_مولد":"localmonthnamegen","اسم_الشهر_المحلي_المولد":"localmonthnamegen","اسم_شهر_محلي_مولد":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","اختصار_الشهر_المحلى":"localmonthabbrev","اختصار_شهر_محلى":"localmonthabbrev","اختصار_الشهر_المحلي":"localmonthabbrev","اختصار_شهر_محلي":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","يوم_محلى":"localday","يوم_محلي":"localday","LOCALDAY":"localday","يوم_محلى2":"localday2","يوم_محلي2":"localday2","LOCALDAY2":"localday2", +"اسم_اليوم_المحلى":"localdayname","اسم_يوم_محلى":"localdayname","اسم_اليوم_المحلي":"localdayname","اسم_يوم_محلي":"localdayname","LOCALDAYNAME":"localdayname","عام_محلى":"localyear","عام_محلي":"localyear","LOCALYEAR":"localyear","وقت_محلى":"localtime","وقت_محلي":"localtime","LOCALTIME":"localtime","ساعه_محليه":"localhour","ساعة_محلية":"localhour","LOCALHOUR":"localhour","اسم_الموقع":"sitename","اسم_موقع":"sitename","SITENAME":"sitename","أسبوع_حالى":"currentweek","أسبوع_حالي":"currentweek","CURRENTWEEK":"currentweek","يوم_حالى_مأ":"currentdow","يوم_حالي_مأ":"currentdow","CURRENTDOW":"currentdow","أسبوع_محلى":"localweek","أسبوع_محلي":"localweek","LOCALWEEK":"localweek","يوم_محلى_مأ":"localdow","يوم_محلي_مأ":"localdow","LOCALDOW":"localdow","حجم_المراجعه":"revisionsize","حجم_المراجعة": +"revisionsize","REVISIONSIZE":"revisionsize","نسخه_حاليه":"currentversion","نسخة_حالية":"currentversion","CURRENTVERSION":"currentversion","طابع_الوقت_الحالي":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","طابع_الوقت_المحلى":"localtimestamp","طابع_الوقت_المحلي":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","علامة_الاتجاه":"directionmark","علامة_اتجاه":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","لغة_المحتوى":"contentlanguage","لغة_محتوى":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","اللغه_بتاعه_الصفحه":"pagelanguage","PAGELANGUAGE":"pagelanguage","لغة_الصفحة":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zء-ي\\x{0610}-\\x{061A}\\x{064B}-\\x{065F}\\x{0670}\\x{06D6}-\\x{06DC}\\x{06DF}-\\x{06E4}\\x{06E7}\\x{06E8}\\x{06EA}-\\x{06ED}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-as.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-as.json new file mode 100644 index 0000000..222fde1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-as.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__গোপন_শ্ৰেণী__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__সূচী__":"index","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#বিশেষ":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate": +"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","পৃষ্ঠাৰ_আকাৰ":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE": +"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1": +"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ast.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ast.json new file mode 100644 index 0000000..968d03e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ast.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__sin_tdc__":"notoc","__notdc__":"notoc","__notoc__":"notoc","__sin_galería__":"nogallery","__nogalería__":"nogallery","__nogaleria__":"nogallery","__nogallery__":"nogallery","__forzar_tdc__":"forcetoc","__forzartdc__":"forcetoc","__forzartoc__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc","__toc__":"toc", +"__no_editar_sección__":"noeditsection","__noeditarsección__":"noeditsection","__noeditarseccion__":"noeditsection","__noeditsection__":"noeditsection","__noconvertirtitulo__":"notitleconvert","__noconvertirtítulo__":"notitleconvert","__noct___":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__noconvertircontenido__":"nocontentconvert","__nocc___":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__VINCULARANUEVASECCION__":"newsectionlink","__ENLACECREARSECCIÓN__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NOVINCULARANUEVASECCION__":"nonewsectionlink","__SINENLACECREARSECCIÓN__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATEGORÍAOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECCIÓNESTÁTICA__":"staticredirect", +"__REDIRECCIONESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIGUACION__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"en":"ns","ns":"ns","nse":"nse","codificarurl":"urlencode","urlencode":"urlencode","primerominus":"lcfirst","primerominús":"lcfirst","lcfirst":"lcfirst","primeromayus":"ucfirst","primeromayús":"ucfirst","ucfirst":"ucfirst","minus":"lc","minús":"lc","lc":"lc","mayus":"uc","mayús":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","urllocalc":"localurle","localurle":"localurle","urlcompleta":"fullurl","fullurl":"fullurl","urlcompletac":"fullurle","fullurle":"fullurle","urlcanonica":"canonicalurl","canonicalurl":"canonicalurl","urlcanonicac":"canonicalurle","canonicalurle":"canonicalurle","formatonúmero":"formatnum","formatonumero":"formatnum","formatnum":"formatnum","gramatica":"grammar","gramática":"grammar","grammar": +"grammar","género":"gender","genero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#idioma":"language","#language":"language","rellenarizquierda":"padleft","rellenarizq":"padleft","padleft":"padleft","rellenarderecha":"padright","rellenarder":"padright","padright":"padright","anchorencode":"anchorencode","rutaarchivo":"filepath","rutarchivo":"filepath","rutadearchivo":"filepath","filepath":"filepath","iddepágina":"pageid","idpágina":"pageid","iddepagina":"pageid","idpagina":"pageid","pageid":"pageid","int":"int","#especial":"special","#special":"special","#especialc":"speciale","#speciale":"speciale","#etiqueta":"tag","#tag":"tag","#formatodefecha":"formatdate","#formatearfecha":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#destino":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#invocar":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#siigual":"ifeq","#ifeq":"ifeq","#según":"switch","#switch" +:"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierror":"iferror","#iferror":"iferror","#tiempo":"time","#time":"time","#tiempol":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#árboldecategorías":"categorytree","#arboldecategorias":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","nointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propiedad":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","rutaartículo":"articlepath","rutaarticulo":"articlepath","articlepath":"articlepath","servidor":"server","server":"server","nombreservidor":"servername","servername":"servername","rutascript":"scriptpath","rutadescript":"scriptpath","scriptpath":"scriptpath","rutaestilo":"stylepath","rutadeestilo":"stylepath", +"stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NÚMEROENGRUPO":"numberingroup","NUMEROENGRUPO":"numberingroup","NUMENGRUPO":"numberingroup","NÚMENGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENAR":"defaultsort","CLAVEDEORDENPREDETERMINADO":"defaultsort","ORDENDECATEGORIAPREDETERMINADO":"defaultsort","ORDENDECATEGORÍAPREDETERMINADO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PÁGINASENCATEGORÍA":"pagesincategory","PÁGINASENCAT":"pagesincategory","PAGSENCAT":"pagesincategory","PAGINASENCATEGORIA":"pagesincategory","PAGINASENCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMAÑOPÁGINA":"pagesize","TAMAÑODEPÁGINA":"pagesize","TAMAÑOPAGINA":"pagesize","TAMAÑODEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECCIÓN":"protectionlevel","NIVELDEPROTECCION":"protectionlevel","PROTECTIONLEVEL" +:"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMBREDEPAGINA":"pagename","NOMBREDEPÁGINA":"pagename","PAGENAME":"pagename","NOMBREDEPAGINAC":"pagenamee","NOMBREDEPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMBRECOMPLETODEPÁGINA":"fullpagename","NOMBRECOMPLETODEPAGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMBRECOMPLETODEPAGINAC":"fullpagenamee","NOMBRECOMPLETODEPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMBREDESUBPAGINA":"subpagename","NOMBREDESUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMBREDESUBPAGINAC":"subpagenamee","NOMBREDESUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMBREDEPAGINARAIZ":"rootpagename","NOMBREDEPÁGINARAÍZ":"rootpagename","ROOTPAGENAME":"rootpagename","NOMBREDEPAGINARAIZC":"rootpagenamee","NOMBREDEPÁGINARAÍZC":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBREDEPAGINABASE":"basepagename","NOMBREDEPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMBREDEPAGINABASEC": +"basepagenamee","NOMBREDEPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMBREDEPÁGINADEDISCUSIÓN":"talkpagename","NOMBREDEPAGINADEDISCUSION":"talkpagename","NOMBREDEPAGINADISCUSION":"talkpagename","NOMBREDEPÁGINADISCUSIÓN":"talkpagename","TALKPAGENAME":"talkpagename","NOMBREDEPÁGINADEDISCUSIÓNC":"talkpagenamee","NOMBREDEPAGINADEDISCUSIONC":"talkpagenamee","NOMBREDEPAGINADISCUSIONC":"talkpagenamee","NOMBREDEPÁGINADISCUSIÓNC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMBREDEPAGINADETEMA":"subjectpagename","NOMBREDEPÁGINADETEMA":"subjectpagename","NOMBREDEPÁGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEARTICULO":"subjectpagename","NOMBREDEPÁGINADEARTÍCULO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMBREDEPAGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEASUNTOC": +"subjectpagenamee","NOMBREDEPAGINADEARTICULOC":"subjectpagenamee","NOMBREDEPÁGINADEARTÍCULOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDEREVISION":"revisionid","IDREVISION":"revisionid","IDDEREVISIÓN":"revisionid","IDREVISIÓN":"revisionid","REVISIONID":"revisionid","DIADEREVISION":"revisionday","DIAREVISION":"revisionday","DÍADEREVISIÓN":"revisionday","DÍAREVISIÓN":"revisionday","REVISIONDAY":"revisionday","DIADEREVISION2":"revisionday2","DIAREVISION2":"revisionday2","DÍADEREVISIÓN2":"revisionday2","DÍAREVISIÓN2":"revisionday2","REVISIONDAY2":"revisionday2","MESDEREVISION":"revisionmonth","MESDEREVISIÓN":"revisionmonth","MESREVISION":"revisionmonth","MESREVISIÓN":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDEREVISION1":"revisionmonth1","MESDEREVISIÓN1":"revisionmonth1","MESREVISION1":"revisionmonth1","MESREVISIÓN1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","AÑODEREVISION":"revisionyear", +"AÑODEREVISIÓN":"revisionyear","AÑOREVISION":"revisionyear","AÑOREVISIÓN":"revisionyear","REVISIONYEAR":"revisionyear","MARCADEHORADEREVISION":"revisiontimestamp","MARCADEHORADEREVISIÓN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODEREVISION":"revisionuser","USUARIODEREVISIÓN":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACIODENOMBRE":"namespace","NAMESPACE":"namespace","ESPACIODENOMBREC":"namespacee","NAMESPACEE":"namespacee","NÚMERODELESPACIO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACIODEDISCUSION":"talkspace","ESPACIODEDISCUSIÓN":"talkspace","TALKSPACE":"talkspace","ESPACIODEDISCUSIONC":"talkspacee","TALKSPACEE":"talkspacee","ESPACIODEASUNTO":"subjectspace","ESPACIODETEMA":"subjectspace","ESPACIODEARTÍCULO":"subjectspace","ESPACIODEARTICULO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACIODETEMAC":"subjectspacee","ESPACIODEASUNTOC":"subjectspacee", +"ESPACIODEARTICULOC":"subjectspacee","ESPACIODEARTÍCULOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NÚMERODEARTÍCULOS":"numberofarticles","NUMERODEARTICULOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NÚMERODEARCHIVOS":"numberoffiles","NUMERODEARCHIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NÚMERODEPÁGINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NÚMEROADMINIISTRADORES":"numberofadmins","NÚMEROADMINS":"numberofadmins","NUMEROADMINS":"numberofadmins","NUMEROADMINISTRADORES":"numberofadmins","NUMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINS":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINS": +"numberofadmins","NUMBEROFADMINS":"numberofadmins","NÚMERODEEDICIONES":"numberofedits","NUMERODEEDICIONES":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRARTÍTULO":"displaytitle","MOSTRARTITULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESACTUAL":"currentmonth","MESACTUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MESACTUALCOMPLETO":"currentmonthname","NOMBREMESACTUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MESACTUALGENITIVO":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESACTUALABREVIADO":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DÍAACTUAL":"currentday","DIAACTUAL":"currentday","DÍA_ACTUAL":"currentday","DIA_ACTUAL":"currentday","CURRENTDAY":"currentday","DÍAACTUAL2":"currentday2","DIAACTUAL2":"currentday2","DÍA_ACTUAL2":"currentday2","DIA_ACTUAL2":"currentday2","CURRENTDAY2":"currentday2", +"NOMBREDÍAACTUAL":"currentdayname","NOMBREDIAACTUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","AÑOACTUAL":"currentyear","AÑO_ACTUAL":"currentyear","CURRENTYEAR":"currentyear","HORA_MINUTOS_ACTUAL":"currenttime","HORAMINUTOSACTUAL":"currenttime","TIEMPOACTUAL":"currenttime","CURRENTTIME":"currenttime","HORAACTUAL":"currenthour","HORA_ACTUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","MESLOCALCOMPLETO":"localmonthname","NOMBREMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","MESLOCALGENITIVO":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREVIADO":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DÍALOCAL":"localday","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","DÍALOCAL2":"localday2","LOCALDAY2":"localday2","NOMBREDIALOCAL":"localdayname", +"NOMBREDÍALOCAL":"localdayname","LOCALDAYNAME":"localdayname","AÑOLOCAL":"localyear","LOCALYEAR":"localyear","HORAMINUTOSLOCAL":"localtime","TIEMPOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMBREDELSITIO":"sitename","SITENAME":"sitename","SEMANAACTUAL":"currentweek","CURRENTWEEK":"currentweek","DDSACTUAL":"currentdow","DIADESEMANAACTUAL":"currentdow","DÍADESEMANAACTUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DDSLOCAL":"localdow","DIADESEMANALOCAL":"localdow","DÍADESEMANALOCAL":"localdow","LOCALDOW":"localdow","TAMAÑODEREVISIÓN":"revisionsize","TAMAÑODEREVISION":"revisionsize","REVISIONSIZE":"revisionsize","VERSIONACTUAL":"currentversion","VERSIÓNACTUAL":"currentversion","CURRENTVERSION":"currentversion","MARCADEHORAACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MARCADEHORALOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark" +,"DIRMARK":"directionmark","IDIOMADELCONTENIDO":"contentlanguage","IDIOMADELCONT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záéíóúñ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-atj.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-atj.json new file mode 100644 index 0000000..620e0f2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-atj.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode","initminus":"lcfirst","lcfirst":"lcfirst","initmajus": +"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int","#spécial":"special","#special":"special","#spéciale": +"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property","#property":"property","#statements":"statements", +"#commaseparatedlist":"commaSeparatedList","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","CLEFDETRI":"defaultsort", +"CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee","ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMPAGE":"pagename","PAGENAME":"pagename","NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename", +"FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid","REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday", +"REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev", +"JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL":"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL": +"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-av.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-av.json new file mode 100644 index 0000000..161678a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-av.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender", +"множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist": +"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ": +"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY": +"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour", +"МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename", +"ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюяӀ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-avk.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-avk.json new file mode 100644 index 0000000..0dc0f60 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-avk.json @@ -0,0 +1,26 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__sin_tdc__":"notoc","__notdc__":"notoc","__без_оглавления__":"notoc","__без_огл__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__sin_galería__":"nogallery","__nogalería__":"nogallery","__nogaleria__": +"nogallery","__без_галереи__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__forzar_tdc__":"forcetoc","__forzartdc__":"forcetoc","__forzartoc__":"forcetoc","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__tdc__":"toc","__оглавление__":"toc","__огл__":"toc","__sectionnoneditable__":"noeditsection","__noeditsection__":"noeditsection","__no_editar_sección__":"noeditsection","__noeditarsección__":"noeditsection","__noeditarseccion__":"noeditsection","__без_редактирования_раздела__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__noconvertirtitulo__":"notitleconvert","__noconvertirtítulo__":"notitleconvert","__noct___":"notitleconvert", +"__без_преобразования_заголовка__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert","__noconvertircontenido__":"nocontentconvert","__nocc___":"nocontentconvert","__без_преобразования_текста__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__VINCULARANUEVASECCION__":"newsectionlink","__ENLACECREARSECCIÓN__":"newsectionlink","__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__NOVINCULARANUEVASECCION__":"nonewsectionlink","__SINENLACECREARSECCIÓN__":"nonewsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__CATEGORÍAOCULTA__":"hiddencat", +"__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__ИНДЕКС__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__NOINDEXAR__":"noindex","__БЕЗ_ИНДЕКСА__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__REDIRECCIÓNESTÁTICA__":"staticredirect","__REDIRECCIONESTATICA__":"staticredirect","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIGUACION__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","en":"ns","пи":"ns","espacenx":"nse","nse":"nse","пик":"nse","encodeurl":"urlencode","urlencode":"urlencode","codificarurl":"urlencode","закодированный_адрес":"urlencode","initminus":"lcfirst","lcfirst":"lcfirst","primerominus":"lcfirst", +"primerominús":"lcfirst","первая_буква_маленькая":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","primeromayus":"ucfirst","primeromayús":"ucfirst","первая_буква_большая":"ucfirst","minus":"lc","lc":"lc","minús":"lc","маленькими_буквами":"lc","majus":"uc","capit":"uc","uc":"uc","mayus":"uc","mayús":"uc","большими_буквами":"uc","urllocale":"localurl","localurl":"localurl","urllocal":"localurl","локальный_адрес":"localurl","urllocalex":"localurle","localurle":"localurle","urllocalc":"localurle","локальный_адрес_2":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompleta":"fullurl","полный_адрес":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcompletac":"fullurle","полный_адрес_2":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanonica":"canonicalurl","urlcanoniquex":"canonicalurle", +"canonicalurle":"canonicalurle","urlcanonicac":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","formatonúmero":"formatnum","formatonumero":"formatnum","форматировать_число":"formatnum","grammaire":"grammar","grammar":"grammar","gramatica":"grammar","gramática":"grammar","падеж":"grammar","genre":"gender","gender":"gender","género":"gender","genero":"gender","пол":"gender","pluriel":"plural","plural":"plural","множественное_число":"plural","bidi":"bidi","#langue":"language","#language":"language","#idioma":"language","#язык":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","rellenarizquierda":"padleft","rellenarizq":"padleft","заполнить_слева":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","rellenarderecha":"padright","rellenarder":"padright","заполнить_справа":"padright","encodeancre":"anchorencode","anchorencode": +"anchorencode","кодировать_метку":"anchorencode","chemin":"filepath","filepath":"filepath","rutaarchivo":"filepath","rutarchivo":"filepath","rutadearchivo":"filepath","путь_к_файлу":"filepath","idpage":"pageid","pageid":"pageid","iddepágina":"pageid","idpágina":"pageid","iddepagina":"pageid","idpagina":"pageid","идентификатор_страницы":"pageid","внутр":"int","int":"int","#spécial":"special","#special":"special","#especial":"special","#служебная":"special","#spéciale":"speciale","#speciale":"speciale","#especialc":"speciale","#balise":"tag","#tag":"tag","#etiqueta":"tag","#метка":"tag","#тег":"tag","#тэг":"tag","#formatodefecha":"formatdate","#formatearfecha":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#форматдаты":"formatdate","#destino":"target","#target":"target","#babel":"babel","#вавилон":"babel","#coordinates":"coordinates","#invoque":"invoke","#invocar":"invoke", +"#вызвать":"invoke","#invoke":"invoke","#related":"related","#si":"if","#если":"if","#if":"if","#si=":"ifeq","#siigual":"ifeq","#ifeq":"ifeq","#selon":"switch","#según":"switch","#переключатель":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#sierror":"iferror","#еслиошибка":"iferror","#iferror":"iferror","#heure":"time","#tiempo":"time","#время":"time","#time":"time","#heurel":"timel","#tiempol":"timel","#мвремя":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#árboldecategorías":"categorytree","#arboldecategorias":"categorytree","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h": +"lsth","sanslienexterne":"noexternallanglinks","nointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property","#propiedad":"property","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","rutaartículo":"articlepath","rutaarticulo":"articlepath","путь_к_статье":"articlepath","serveur":"server","server":"server","servidor":"server","сервер":"server","nomserveur":"servername","servername":"servername","nombreservidor":"servername","название_сервера":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","rutascript":"scriptpath","rutadescript":"scriptpath","путь_к_скрипту":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","rutaestilo":"stylepath","rutadeestilo":"stylepath","путь_к_стилю": +"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NÚMEROENGRUPO":"numberingroup","NUMEROENGRUPO":"numberingroup","NUMENGRUPO":"numberingroup","NÚMENGRUPO":"numberingroup","ЧИСЛО_В_ГРУППЕ":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","ORDENAR":"defaultsort","CLAVEDEORDENPREDETERMINADO":"defaultsort","ORDENDECATEGORIAPREDETERMINADO":"defaultsort","ORDENDECATEGORÍAPREDETERMINADO":"defaultsort","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PÁGINASENCATEGORÍA":"pagesincategory","PÁGINASENCAT":"pagesincategory","PAGSENCAT":"pagesincategory", +"PAGINASENCATEGORIA":"pagesincategory","PAGINASENCAT":"pagesincategory","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","TAMAÑOPÁGINA":"pagesize","TAMAÑODEPÁGINA":"pagesize","TAMAÑOPAGINA":"pagesize","TAMAÑODEPAGINA":"pagesize","РАЗМЕР_СТРАНИЦЫ":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","NIVELDEPROTECCIÓN":"protectionlevel","NIVELDEPROTECCION":"protectionlevel","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGE":"pagename","PAGENAME":"pagename","NOMBREDEPAGINA":"pagename","NOMBREDEPÁGINA":"pagename","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMBREDEPAGINAC":"pagenamee","NOMBREDEPÁGINAC":"pagenamee","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMBRECOMPLETODEPÁGINA":"fullpagename", +"NOMBRECOMPLETODEPAGINA":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMBRECOMPLETODEPAGINAC":"fullpagenamee","NOMBRECOMPLETODEPÁGINAC":"fullpagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMBREDESUBPAGINA":"subpagename","NOMBREDESUBPÁGINA":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMBREDESUBPAGINAC":"subpagenamee","NOMBREDESUBPÁGINAC":"subpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMBREDEPAGINARAIZ":"rootpagename","NOMBREDEPÁGINARAÍZ":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBREDEPAGINARAIZC":"rootpagenamee","NOMBREDEPÁGINARAÍZC":"rootpagenamee","NOMBASEDEPAGE": +"basepagename","BASEPAGENAME":"basepagename","NOMBREDEPAGINABASE":"basepagename","NOMBREDEPÁGINABASE":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMBREDEPAGINABASEC":"basepagenamee","NOMBREDEPÁGINABASEC":"basepagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMBREDEPÁGINADEDISCUSIÓN":"talkpagename","NOMBREDEPAGINADEDISCUSION":"talkpagename","NOMBREDEPAGINADISCUSION":"talkpagename","NOMBREDEPÁGINADISCUSIÓN":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMBREDEPÁGINADEDISCUSIÓNC":"talkpagenamee","NOMBREDEPAGINADEDISCUSIONC":"talkpagenamee","NOMBREDEPAGINADISCUSIONC":"talkpagenamee","NOMBREDEPÁGINADISCUSIÓNC":"talkpagenamee", +"НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMBREDEPAGINADETEMA":"subjectpagename","NOMBREDEPÁGINADETEMA":"subjectpagename","NOMBREDEPÁGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEARTICULO":"subjectpagename","NOMBREDEPÁGINADEARTÍCULO":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","NOMBREDEPAGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEARTICULOC":"subjectpagenamee","NOMBREDEPÁGINADEARTÍCULOC":"subjectpagenamee", +"НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","IDVERSION":"revisionid","REVISIONID":"revisionid","IDDEREVISION":"revisionid","IDREVISION":"revisionid","IDDEREVISIÓN":"revisionid","IDREVISIÓN":"revisionid","ИД_ВЕРСИИ":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","DIADEREVISION":"revisionday","DIAREVISION":"revisionday","DÍADEREVISIÓN":"revisionday","DÍAREVISIÓN":"revisionday","ДЕНЬ_ВЕРСИИ":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","DIADEREVISION2":"revisionday2","DIAREVISION2":"revisionday2","DÍADEREVISIÓN2":"revisionday2","DÍAREVISIÓN2":"revisionday2","ДЕНЬ_ВЕРСИИ_2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDEREVISION":"revisionmonth","MESDEREVISIÓN":"revisionmonth","MESREVISION":"revisionmonth","MESREVISIÓN":"revisionmonth","МЕСЯЦ_ВЕРСИИ":"revisionmonth","MOISVERSION1":"revisionmonth1", +"REVISIONMONTH1":"revisionmonth1","MESDEREVISION1":"revisionmonth1","MESDEREVISIÓN1":"revisionmonth1","MESREVISION1":"revisionmonth1","MESREVISIÓN1":"revisionmonth1","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","AÑODEREVISION":"revisionyear","AÑODEREVISIÓN":"revisionyear","AÑOREVISION":"revisionyear","AÑOREVISIÓN":"revisionyear","ГОД_ВЕРСИИ":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","MARCADEHORADEREVISION":"revisiontimestamp","MARCADEHORADEREVISIÓN":"revisiontimestamp","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","USUARIODEREVISION":"revisionuser","USUARIODEREVISIÓN":"revisionuser","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ESPACIODENOMBRE":"namespace", +"ПРОСТРАНСТВО_ИМЁН":"namespace","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","ESPACIODENOMBREC":"namespacee","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","NÚMERODELESPACIO":"namespacenumber","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACIODEDISCUSION":"talkspace","ESPACIODEDISCUSIÓN":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACIODEDISCUSIONC":"talkspacee","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACIODEASUNTO":"subjectspace","ESPACIODETEMA":"subjectspace","ESPACIODEARTÍCULO":"subjectspace","ESPACIODEARTICULO":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","ESPACESUJETX":"subjectspacee","ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ESPACIODETEMAC":"subjectspacee","ESPACIODEASUNTOC":"subjectspacee","ESPACIODEARTICULOC":"subjectspacee","ESPACIODEARTÍCULOC":"subjectspacee","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NÚMERODEARTÍCULOS":"numberofarticles","NUMERODEARTICULOS":"numberofarticles","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NÚMERODEARCHIVOS":"numberoffiles","NUMERODEARCHIVOS":"numberoffiles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers", +"NUMBEROFACTIVEUSERS":"numberofactiveusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSACTIVOS":"numberofactiveusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NÚMERODEPÁGINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NÚMEROADMINIISTRADORES":"numberofadmins","NÚMEROADMINS":"numberofadmins","NUMEROADMINS":"numberofadmins","NUMEROADMINISTRADORES":"numberofadmins","NUMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINS":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINS":"numberofadmins","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","NÚMERODEEDICIONES":"numberofedits","NUMERODEEDICIONES":"numberofedits", +"КОЛИЧЕСТВО_ПРАВОК":"numberofedits","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","MOSTRARTÍTULO":"displaytitle","MOSTRARTITULO":"displaytitle","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","!":"!","=":"=","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL":"currentmonth","MESACTUAL2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","MESACTUAL1":"currentmonth1","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MESACTUALCOMPLETO":"currentmonthname","NOMBREMESACTUAL":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESACTUALGENITIVO":"currentmonthnamegen", +"НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","MESACTUALABREVIADO":"currentmonthabbrev","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","DÍAACTUAL":"currentday","DIAACTUAL":"currentday","DÍA_ACTUAL":"currentday","DIA_ACTUAL":"currentday","ТЕКУЩИЙ_ДЕНЬ":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","DÍAACTUAL2":"currentday2","DIAACTUAL2":"currentday2","DÍA_ACTUAL2":"currentday2","DIA_ACTUAL2":"currentday2","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","NOMBREDÍAACTUAL":"currentdayname","NOMBREDIAACTUAL":"currentdayname","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","AÑOACTUAL":"currentyear", +"AÑO_ACTUAL":"currentyear","ТЕКУЩИЙ_ГОД":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HORA_MINUTOS_ACTUAL":"currenttime","HORAMINUTOSACTUAL":"currenttime","TIEMPOACTUAL":"currenttime","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","HORAACTUAL":"currenthour","HORA_ACTUAL":"currenthour","ТЕКУЩИЙ_ЧАС":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","MESLOCAL1":"localmonth1","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","MESLOCALCOMPLETO":"localmonthname","NOMBREMESLOCAL":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","NOMGENMOISLOCAL": +"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALGENITIVO":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","MESLOCALABREVIADO":"localmonthabbrev","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","DÍALOCAL":"localday","DIALOCAL":"localday","МЕСТНЫЙ_ДЕНЬ":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","DIALOCAL2":"localday2","DÍALOCAL2":"localday2","МЕСТНЫЙ_ДЕНЬ_2":"localday2","NOMJOURLOCAL":"localdayname","LOCALDAYNAME":"localdayname","NOMBREDIALOCAL":"localdayname","NOMBREDÍALOCAL":"localdayname","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","AÑOLOCAL":"localyear","МЕСТНЫЙ_ГОД":"localyear","HORAIRELOCAL":"localtime","LOCALTIME":"localtime", +"HORAMINUTOSLOCAL":"localtime","TIEMPOLOCAL":"localtime","МЕСТНОЕ_ВРЕМЯ":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","HORALOCAL":"localhour","МЕСТНЫЙ_ЧАС":"localhour","NOMSITE":"sitename","SITENAME":"sitename","NOMBREDELSITIO":"sitename","НАЗВАНИЕ_САЙТА":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","SEMANAACTUAL":"currentweek","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","DDSACTUAL":"currentdow","DIADESEMANAACTUAL":"currentdow","DÍADESEMANAACTUAL":"currentdow","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","SEMANALOCAL":"localweek","МЕСТНАЯ_НЕДЕЛЯ":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","DDSLOCAL":"localdow","DIADESEMANALOCAL":"localdow","DÍADESEMANALOCAL":"localdow","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","TAMAÑODEREVISIÓN":"revisionsize","TAMAÑODEREVISION": +"revisionsize","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","VERSIONACTUAL":"currentversion","VERSIÓNACTUAL":"currentversion","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MARCADEHORAACTUAL":"currenttimestamp","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARCADEHORALOCAL":"localtimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","IDIOMADELCONTENIDO":"contentlanguage","IDIOMADELCONT":"contentlanguage","ЯЗЫК_СОДЕРЖАНИЯ": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-awa.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-awa.json new file mode 100644 index 0000000..de053a3 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-awa.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__बिना_अनुक्रम__":"notoc","__विषय_सूची_हीन__":"notoc","__notoc__":"notoc","__गैलरी_नहीं__":"nogallery","__nogallery__":"nogallery","__अनुक्रम_दिखाएँ__":"forcetoc","__विषय_सूची_दिखाएँ__":"forcetoc", +"__विषय_सूची_दिखायें__":"forcetoc","__forcetoc__":"forcetoc","__अनुक्रम__":"toc","__विषय_सूची__":"toc","__toc__":"toc","__अनुभाग_सम्पादन_नहीं__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__विषय_जोड़ें_कड़ी__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__विषय_जोड़े_कड़ी_रहित__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__छुपी_श्रेणी__":"hiddencat","__छिपी_श्रेणी__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__सूचीबद्ध__":"index","__INDEX__":"index","__असूचीबद्ध__":"noindex","__NOINDEX__":"noindex", +"__स्थिर_पुनर्प्रेषण__":"staticredirect","__स्थिर_अनुप्रेषण__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"नामस्थान":"ns","ns":"ns","नामस्थान_कोड":"nse","nse":"nse","यू_आर_एल_कोड":"urlencode","urlencode":"urlencode","छोटे_अक्षर_से_शुरू":"lcfirst","lcfirst":"lcfirst","बड़े_अक्षर_से_शुरू":"ucfirst","ucfirst":"ucfirst","छोटे_अक्षर":"lc","lc":"lc","बड़े_अक्षर":"uc","uc":"uc","स्थानीय_यू_आर_एल":"localurl","localurl":"localurl","स्थानीय_यू_आर_एल_कोड":"localurle","localurle":"localurle","पूर्ण_यू_आर_एल":"fullurl","fullurl":"fullurl", +"पूर्ण_यू_आर_एल_कोड":"fullurle","fullurle":"fullurle","मानक_यू_आर_एल":"canonicalurl","canonicalurl":"canonicalurl","मानक_यू_आर_एल_कोड":"canonicalurle","canonicalurle":"canonicalurle","संख्या_रूप":"formatnum","formatnum":"formatnum","व्याकरण":"grammar","grammar":"grammar","लिंग":"gender","gender":"gender","वचन":"plural","plural":"plural","bidi":"bidi","#भाषा":"language","#language":"language","बाएँ_जोड़ें":"padleft","बायें_जोड़ें":"padleft","padleft":"padleft","दाएँ_जोड़ें":"padright","दायें_जोड़ें":"padright","padright":"padright","ऐंकर_कोड":"anchorencode","anchorencode":"anchorencode","फ़ाइल_पथ":"filepath","filepath":"filepath","पृष्ठ_आइ_डी":"pageid","pageid":"pageid","विश्व":"int","int":"int","#विशेष":"special" +,"#special":"special","#विशेष_कोड":"speciale","#speciale":"speciale","#टैग":"tag","#tag":"tag","#तिथि_रूप":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#लक्ष्य":"target","#target":"target","#बेबल":"babel","#babel":"babel","#coordinates":"coordinates","#बुलाएँ":"invoke","#बुलायें":"invoke","#invoke":"invoke","#related":"related","#यदि":"if","#if":"if","#यदिसम":"ifeq","#यदि_समान":"ifeq","#यदि_बराबर":"ifeq","#ifeq":"ifeq","#बदलें":"switch","#switch":"switch","#यदि_मौजूद":"ifexist","#ifexist":"ifexist","#यदि_सूत्र":"ifexpr","#ifexpr":"ifexpr","#यदि_त्रुटि":"iferror","#iferror":"iferror","#समय":"time","#time":"time","#समय_स्थानीय":"timel","#timel":"timel","#सूत्र":"expr","#expr":"expr","#सम्बन्धित_से_पूर्ण":"rel2abs" +,"#संबंधित_से_पूर्ण":"rel2abs","#rel2abs":"rel2abs","#शीर्षक_भाग":"titleparts","#titleparts":"titleparts","#श्रेणी_वृक्ष":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","कोई_अंतरविकी_कड़ियाँ_नहीं":"noexternallanglinks","कोई_अंतरविकि_कड़ियाँ_नहीं":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#गुण":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","लेख_पथ":"articlepath","articlepath":"articlepath","सर्वर":"server","server":"server","सर्वर_नाम":"servername","servername":"servername","स्क्रिप्ट_पथ":"scriptpath","scriptpath":"scriptpath","स्टाइल_पथ":"stylepath", +"stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"समूह_संख्या":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","मूल_सॉर्ट":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","श्रेणी_में_पृष्ठ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","पृष्ठ_आकार":"pagesize","PAGESIZE":"pagesize","सुरक्षा_स्तर":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","पृष्ठ_नाम":"pagename","PAGENAME":"pagename","पृष्ठ_नाम_कोड":"pagenamee","PAGENAMEE":"pagenamee","पूर्ण_पृष्ठ_नाम":"fullpagename","FULLPAGENAME":"fullpagename","पूर्ण_पृष्ठ_नाम_कोड":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee", +"उपपृष्ठ_नाम":"subpagename","SUBPAGENAME":"subpagename","उपपृष्ठ_नाम_कोड":"subpagenamee","SUBPAGENAMEE":"subpagenamee","मूल_पृष्ठ_नाम":"rootpagename","ROOTPAGENAME":"rootpagename","मूल_पृष्ठ_नाम_कोड":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","तल_पृष्ठ_नाम":"basepagename","BASEPAGENAME":"basepagename","तल_पृष्ठ_नाम_कोड":"basepagenamee","BASEPAGENAMEE":"basepagenamee","चर्चा_पृष्ठ_नाम":"talkpagename","TALKPAGENAME":"talkpagename","चर्चा_पृष्ठ_नाम_कोड":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","सामग्री_पृष्ठ_नाम":"subjectpagename","लेख_पृष्ठ_नाम":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","सामग्री_पृष्ठ_नाम_कोड":"subjectpagenamee", +"लेख_पृष्ठ_नाम_कोड":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","अवतरण_संख्या":"revisionid","REVISIONID":"revisionid","अवतरण_दिन":"revisionday","REVISIONDAY":"revisionday","अवतरण_दिन2":"revisionday2","अवतरण_दिन२":"revisionday2","REVISIONDAY2":"revisionday2","अवतरण_माह":"revisionmonth","REVISIONMONTH":"revisionmonth","अवतरण_माह1":"revisionmonth1","अवतरण_माह१":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","अवतरण_वर्ष":"revisionyear","REVISIONYEAR":"revisionyear","अवतरण_समय":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","अवतरण_सदस्य":"revisionuser","REVISIONUSER":"revisionuser","सीढ़ी_सुरक्षा_स्रोत":"cascadingsources","CASCADINGSOURCES":"cascadingsources","नामस्थान": +"namespace","NAMESPACE":"namespace","नामस्थान_कोड":"namespacee","NAMESPACEE":"namespacee","नामस्थान_संख्या":"namespacenumber","NAMESPACENUMBER":"namespacenumber","चर्चा_स्थान":"talkspace","TALKSPACE":"talkspace","चर्चा_स्थान_कोड":"talkspacee","TALKSPACEE":"talkspacee","सामग्री_स्थान":"subjectspace","लेख_स्थान":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","सामग्री_स्थान_कोड":"subjectspacee","लेख_स्थान_कोड":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","लेख_संख्या":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","फ़ाइल_संख्या":"numberoffiles","फाइल_संख्या":"numberoffiles","NUMBEROFFILES":"numberoffiles","सदस्य_संख्या":"numberofusers","NUMBEROFUSERS": +"numberofusers","सक्रिय_सदस्य_संख्या":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","पृष्ठ_संख्या":"numberofpages","NUMBEROFPAGES":"numberofpages","प्रबन्धक_संख्या":"numberofadmins","प्रबंधक_संख्या":"numberofadmins","NUMBEROFADMINS":"numberofadmins","सम्पादन_संख्या":"numberofedits","NUMBEROFEDITS":"numberofedits","दृश्य_शीर्षक":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","वर्तमान_माह":"currentmonth","वर्तमान_माह2":"currentmonth","वर्तमान_माह२":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","वर्तमान_माह1":"currentmonth1","वर्तमान_माह१":"currentmonth1","CURRENTMONTH1":"currentmonth1","वर्तमान_माह_नाम":"currentmonthname","CURRENTMONTHNAME": +"currentmonthname","वर्तमान_माह_सम्बन्ध":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","वर्तमान_माह_संक्षेप":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","वर्तमान_दिन":"currentday","CURRENTDAY":"currentday","वर्तमान_दिन2":"currentday2","वर्तमान_दिन२":"currentday2","CURRENTDAY2":"currentday2","वर्तमान_दिन_नाम":"currentdayname","CURRENTDAYNAME":"currentdayname","वर्तमान_वर्ष":"currentyear","CURRENTYEAR":"currentyear","वर्तमान_समय":"currenttime","CURRENTTIME":"currenttime","वर्तमान_घंटा":"currenthour","CURRENTHOUR":"currenthour","स्थानीय_माह":"localmonth","स्थानीय_माह2":"localmonth","स्थानीय_माह२":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth", +"स्थानीय_माह1":"localmonth1","स्थानीय_माह१":"localmonth1","LOCALMONTH1":"localmonth1","स्थानीय_माह_नाम":"localmonthname","LOCALMONTHNAME":"localmonthname","स्थानीय_माह_सम्बन्ध":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","स्थानीय_माह_संक्षेप":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","स्थानीय_दिन":"localday","LOCALDAY":"localday","स्थानीय_दिन2":"localday2","स्थानीय_दिन२":"localday2","LOCALDAY2":"localday2","स्थानीय_दिन_नाम":"localdayname","LOCALDAYNAME":"localdayname","स्थानीय_वर्ष":"localyear","LOCALYEAR":"localyear","स्थानीय_समय":"localtime","LOCALTIME":"localtime","स्थानीय_घंटा":"localhour","LOCALHOUR":"localhour","साइट_नाम":"sitename","SITENAME": +"sitename","वर्तमान_सप्ताह":"currentweek","CURRENTWEEK":"currentweek","वर्तमान_सप्ताह_का_दिन":"currentdow","CURRENTDOW":"currentdow","स्थानीय_सप्ताह":"localweek","LOCALWEEK":"localweek","स्थानीय_सप्ताह_का_दिन":"localdow","LOCALDOW":"localdow","अवतरण_आकार":"revisionsize","REVISIONSIZE":"revisionsize","वर्तमान_अवतरण":"currentversion","CURRENTVERSION":"currentversion","वर्तमान_समय_मुहर":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","स्थानीय_समय_मुहर":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","दिशा_चिन्ह":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","सामग्री_भाषा":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}], +"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z\\x{0900}-\\x{0963}\\x{0966}-\\x{A8E0}-\\x{A8FF}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ay.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ay.json new file mode 100644 index 0000000..968d03e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ay.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__sin_tdc__":"notoc","__notdc__":"notoc","__notoc__":"notoc","__sin_galería__":"nogallery","__nogalería__":"nogallery","__nogaleria__":"nogallery","__nogallery__":"nogallery","__forzar_tdc__":"forcetoc","__forzartdc__":"forcetoc","__forzartoc__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc","__toc__":"toc", +"__no_editar_sección__":"noeditsection","__noeditarsección__":"noeditsection","__noeditarseccion__":"noeditsection","__noeditsection__":"noeditsection","__noconvertirtitulo__":"notitleconvert","__noconvertirtítulo__":"notitleconvert","__noct___":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__noconvertircontenido__":"nocontentconvert","__nocc___":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__VINCULARANUEVASECCION__":"newsectionlink","__ENLACECREARSECCIÓN__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NOVINCULARANUEVASECCION__":"nonewsectionlink","__SINENLACECREARSECCIÓN__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATEGORÍAOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECCIÓNESTÁTICA__":"staticredirect", +"__REDIRECCIONESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIGUACION__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"en":"ns","ns":"ns","nse":"nse","codificarurl":"urlencode","urlencode":"urlencode","primerominus":"lcfirst","primerominús":"lcfirst","lcfirst":"lcfirst","primeromayus":"ucfirst","primeromayús":"ucfirst","ucfirst":"ucfirst","minus":"lc","minús":"lc","lc":"lc","mayus":"uc","mayús":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","urllocalc":"localurle","localurle":"localurle","urlcompleta":"fullurl","fullurl":"fullurl","urlcompletac":"fullurle","fullurle":"fullurle","urlcanonica":"canonicalurl","canonicalurl":"canonicalurl","urlcanonicac":"canonicalurle","canonicalurle":"canonicalurle","formatonúmero":"formatnum","formatonumero":"formatnum","formatnum":"formatnum","gramatica":"grammar","gramática":"grammar","grammar": +"grammar","género":"gender","genero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#idioma":"language","#language":"language","rellenarizquierda":"padleft","rellenarizq":"padleft","padleft":"padleft","rellenarderecha":"padright","rellenarder":"padright","padright":"padright","anchorencode":"anchorencode","rutaarchivo":"filepath","rutarchivo":"filepath","rutadearchivo":"filepath","filepath":"filepath","iddepágina":"pageid","idpágina":"pageid","iddepagina":"pageid","idpagina":"pageid","pageid":"pageid","int":"int","#especial":"special","#special":"special","#especialc":"speciale","#speciale":"speciale","#etiqueta":"tag","#tag":"tag","#formatodefecha":"formatdate","#formatearfecha":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#destino":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#invocar":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#siigual":"ifeq","#ifeq":"ifeq","#según":"switch","#switch" +:"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierror":"iferror","#iferror":"iferror","#tiempo":"time","#time":"time","#tiempol":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#árboldecategorías":"categorytree","#arboldecategorias":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","nointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propiedad":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","rutaartículo":"articlepath","rutaarticulo":"articlepath","articlepath":"articlepath","servidor":"server","server":"server","nombreservidor":"servername","servername":"servername","rutascript":"scriptpath","rutadescript":"scriptpath","scriptpath":"scriptpath","rutaestilo":"stylepath","rutadeestilo":"stylepath", +"stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NÚMEROENGRUPO":"numberingroup","NUMEROENGRUPO":"numberingroup","NUMENGRUPO":"numberingroup","NÚMENGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENAR":"defaultsort","CLAVEDEORDENPREDETERMINADO":"defaultsort","ORDENDECATEGORIAPREDETERMINADO":"defaultsort","ORDENDECATEGORÍAPREDETERMINADO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PÁGINASENCATEGORÍA":"pagesincategory","PÁGINASENCAT":"pagesincategory","PAGSENCAT":"pagesincategory","PAGINASENCATEGORIA":"pagesincategory","PAGINASENCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMAÑOPÁGINA":"pagesize","TAMAÑODEPÁGINA":"pagesize","TAMAÑOPAGINA":"pagesize","TAMAÑODEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECCIÓN":"protectionlevel","NIVELDEPROTECCION":"protectionlevel","PROTECTIONLEVEL" +:"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMBREDEPAGINA":"pagename","NOMBREDEPÁGINA":"pagename","PAGENAME":"pagename","NOMBREDEPAGINAC":"pagenamee","NOMBREDEPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMBRECOMPLETODEPÁGINA":"fullpagename","NOMBRECOMPLETODEPAGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMBRECOMPLETODEPAGINAC":"fullpagenamee","NOMBRECOMPLETODEPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMBREDESUBPAGINA":"subpagename","NOMBREDESUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMBREDESUBPAGINAC":"subpagenamee","NOMBREDESUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMBREDEPAGINARAIZ":"rootpagename","NOMBREDEPÁGINARAÍZ":"rootpagename","ROOTPAGENAME":"rootpagename","NOMBREDEPAGINARAIZC":"rootpagenamee","NOMBREDEPÁGINARAÍZC":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBREDEPAGINABASE":"basepagename","NOMBREDEPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMBREDEPAGINABASEC": +"basepagenamee","NOMBREDEPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMBREDEPÁGINADEDISCUSIÓN":"talkpagename","NOMBREDEPAGINADEDISCUSION":"talkpagename","NOMBREDEPAGINADISCUSION":"talkpagename","NOMBREDEPÁGINADISCUSIÓN":"talkpagename","TALKPAGENAME":"talkpagename","NOMBREDEPÁGINADEDISCUSIÓNC":"talkpagenamee","NOMBREDEPAGINADEDISCUSIONC":"talkpagenamee","NOMBREDEPAGINADISCUSIONC":"talkpagenamee","NOMBREDEPÁGINADISCUSIÓNC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMBREDEPAGINADETEMA":"subjectpagename","NOMBREDEPÁGINADETEMA":"subjectpagename","NOMBREDEPÁGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEARTICULO":"subjectpagename","NOMBREDEPÁGINADEARTÍCULO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMBREDEPAGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEASUNTOC": +"subjectpagenamee","NOMBREDEPAGINADEARTICULOC":"subjectpagenamee","NOMBREDEPÁGINADEARTÍCULOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDEREVISION":"revisionid","IDREVISION":"revisionid","IDDEREVISIÓN":"revisionid","IDREVISIÓN":"revisionid","REVISIONID":"revisionid","DIADEREVISION":"revisionday","DIAREVISION":"revisionday","DÍADEREVISIÓN":"revisionday","DÍAREVISIÓN":"revisionday","REVISIONDAY":"revisionday","DIADEREVISION2":"revisionday2","DIAREVISION2":"revisionday2","DÍADEREVISIÓN2":"revisionday2","DÍAREVISIÓN2":"revisionday2","REVISIONDAY2":"revisionday2","MESDEREVISION":"revisionmonth","MESDEREVISIÓN":"revisionmonth","MESREVISION":"revisionmonth","MESREVISIÓN":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDEREVISION1":"revisionmonth1","MESDEREVISIÓN1":"revisionmonth1","MESREVISION1":"revisionmonth1","MESREVISIÓN1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","AÑODEREVISION":"revisionyear", +"AÑODEREVISIÓN":"revisionyear","AÑOREVISION":"revisionyear","AÑOREVISIÓN":"revisionyear","REVISIONYEAR":"revisionyear","MARCADEHORADEREVISION":"revisiontimestamp","MARCADEHORADEREVISIÓN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODEREVISION":"revisionuser","USUARIODEREVISIÓN":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACIODENOMBRE":"namespace","NAMESPACE":"namespace","ESPACIODENOMBREC":"namespacee","NAMESPACEE":"namespacee","NÚMERODELESPACIO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACIODEDISCUSION":"talkspace","ESPACIODEDISCUSIÓN":"talkspace","TALKSPACE":"talkspace","ESPACIODEDISCUSIONC":"talkspacee","TALKSPACEE":"talkspacee","ESPACIODEASUNTO":"subjectspace","ESPACIODETEMA":"subjectspace","ESPACIODEARTÍCULO":"subjectspace","ESPACIODEARTICULO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACIODETEMAC":"subjectspacee","ESPACIODEASUNTOC":"subjectspacee", +"ESPACIODEARTICULOC":"subjectspacee","ESPACIODEARTÍCULOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NÚMERODEARTÍCULOS":"numberofarticles","NUMERODEARTICULOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NÚMERODEARCHIVOS":"numberoffiles","NUMERODEARCHIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NÚMERODEPÁGINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NÚMEROADMINIISTRADORES":"numberofadmins","NÚMEROADMINS":"numberofadmins","NUMEROADMINS":"numberofadmins","NUMEROADMINISTRADORES":"numberofadmins","NUMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINS":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINS": +"numberofadmins","NUMBEROFADMINS":"numberofadmins","NÚMERODEEDICIONES":"numberofedits","NUMERODEEDICIONES":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRARTÍTULO":"displaytitle","MOSTRARTITULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESACTUAL":"currentmonth","MESACTUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MESACTUALCOMPLETO":"currentmonthname","NOMBREMESACTUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MESACTUALGENITIVO":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESACTUALABREVIADO":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DÍAACTUAL":"currentday","DIAACTUAL":"currentday","DÍA_ACTUAL":"currentday","DIA_ACTUAL":"currentday","CURRENTDAY":"currentday","DÍAACTUAL2":"currentday2","DIAACTUAL2":"currentday2","DÍA_ACTUAL2":"currentday2","DIA_ACTUAL2":"currentday2","CURRENTDAY2":"currentday2", +"NOMBREDÍAACTUAL":"currentdayname","NOMBREDIAACTUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","AÑOACTUAL":"currentyear","AÑO_ACTUAL":"currentyear","CURRENTYEAR":"currentyear","HORA_MINUTOS_ACTUAL":"currenttime","HORAMINUTOSACTUAL":"currenttime","TIEMPOACTUAL":"currenttime","CURRENTTIME":"currenttime","HORAACTUAL":"currenthour","HORA_ACTUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","MESLOCALCOMPLETO":"localmonthname","NOMBREMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","MESLOCALGENITIVO":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREVIADO":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DÍALOCAL":"localday","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","DÍALOCAL2":"localday2","LOCALDAY2":"localday2","NOMBREDIALOCAL":"localdayname", +"NOMBREDÍALOCAL":"localdayname","LOCALDAYNAME":"localdayname","AÑOLOCAL":"localyear","LOCALYEAR":"localyear","HORAMINUTOSLOCAL":"localtime","TIEMPOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMBREDELSITIO":"sitename","SITENAME":"sitename","SEMANAACTUAL":"currentweek","CURRENTWEEK":"currentweek","DDSACTUAL":"currentdow","DIADESEMANAACTUAL":"currentdow","DÍADESEMANAACTUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DDSLOCAL":"localdow","DIADESEMANALOCAL":"localdow","DÍADESEMANALOCAL":"localdow","LOCALDOW":"localdow","TAMAÑODEREVISIÓN":"revisionsize","TAMAÑODEREVISION":"revisionsize","REVISIONSIZE":"revisionsize","VERSIONACTUAL":"currentversion","VERSIÓNACTUAL":"currentversion","CURRENTVERSION":"currentversion","MARCADEHORAACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MARCADEHORALOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark" +,"DIRMARK":"directionmark","IDIOMADELCONTENIDO":"contentlanguage","IDIOMADELCONT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záéíóúñ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-az.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-az.json new file mode 100644 index 0000000..92b528f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-az.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__mündəri̇catyox__":"notoc","__notoc__":"notoc","__qalereyayox__":"nogallery","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"}, +{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target": +"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE": +"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE": +"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV": +"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zçəğıöşü]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-azb.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-azb.json new file mode 100644 index 0000000..ce99ecc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-azb.json @@ -0,0 +1,16 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__بی‌فهرست__":"notoc","__notoc__":"notoc","__بی‌نگارخانه__":"nogallery","__nogallery__":"nogallery","__بافهرست__":"forcetoc","__forcetoc__":"forcetoc","__فهرست__":"toc","__toc__":"toc","__بی‌بخش__":"noeditsection","__noeditsection__":"noeditsection", +"__عنوان‌تبدیل‌نشده__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__محتواتبدیل‌نشده__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__بخش‌جدید__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__بی‌پیوندبخش__":"nonewsectionlink","__بی‌پیوند‌بخش‌جدید__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__رده‌پنهان__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__نمایه__":"index","__INDEX__":"index","__بی‌نمایه__":"noindex","__NOINDEX__":"noindex","__تغییرمسیرثابت__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"فن":"ns","ns":"ns","فنک":"nse","nse":"nse","کدنشانی": +"urlencode","urlencode":"urlencode","ابتداکوچک":"lcfirst","ابتدا_کوچک":"lcfirst","lcfirst":"lcfirst","ابتدابزرگ":"ucfirst","ابتدا_بزرگ":"ucfirst","ucfirst":"ucfirst","ک":"lc","lc":"lc","ب":"uc","uc":"uc","نشانی":"localurl","localurl":"localurl","نشانی‌کد":"localurle","نشانی_کد":"localurle","localurle":"localurle","نشانی‌کامل":"fullurl","نشانی_کامل":"fullurl","fullurl":"fullurl","نشانی‌کامل‌کد":"fullurle","نشانی_کامل_کد":"fullurle","fullurle":"fullurle","نشانی_استاندارد":"canonicalurl","نشانی‌استاندارد":"canonicalurl","canonicalurl":"canonicalurl","نشانی_استاندارد_کد":"canonicalurle","نشانی‌استانداردکد":"canonicalurle","canonicalurle":"canonicalurle","آرایش‌عدد":"formatnum","آرایش_عدد":"formatnum","formatnum":"formatnum","دستورزبان":"grammar","دستور_زبان":"grammar","grammar":"grammar", +"جنسیت":"gender","جنس":"gender","gender":"gender","جمع":"plural","plural":"plural","bidi":"bidi","#زبان":"language","#language":"language","لبه‌چپ":"padleft","لبه_چپ":"padleft","padleft":"padleft","لبه‌راست":"padright","لبه_راست":"padright","padright":"padright","کدلنگر":"anchorencode","anchorencode":"anchorencode","مسیرپرونده":"filepath","مسیر_پرونده":"filepath","filepath":"filepath","شناسه_صفحه":"pageid","pageid":"pageid","ترجمه":"int","int":"int","#ویژه":"special","#special":"special","#ویژه_ای":"speciale","#speciale":"speciale","#برچسب":"tag","#tag":"tag","#آرایش‌تاریخ":"formatdate","#آرایش_تاریخ":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#هدف":"target","#target":"target","#بابل":"babel","#babel":"babel","#coordinates":"coordinates","#درخواست":"invoke","#invoke":"invoke","#related":"related","#اگر":"if","#if":"if", +"#ایربیر":"ifeq","#اگرمساوی":"ifeq","#ifeq":"ifeq","#گزینه":"switch","#switch":"switch","#ایراولسا":"ifexist","#اگرموجود":"ifexist","#ifexist":"ifexist","#ایرحساب":"ifexpr","#اگرحساب":"ifexpr","#ifexpr":"ifexpr","#ایریالنیش":"iferror","#اگرخطا":"iferror","#iferror":"iferror","#زمان":"time","#time":"time","#زمان‌بلند":"timel","#timel":"timel","#حساب":"expr","#expr":"expr","#نسبی‌به‌مطلق":"rel2abs","#rel2abs":"rel2abs","#پاره‌عنوان":"titleparts","#titleparts":"titleparts","#درخت‌رده":"categorytree","#درخت_رده":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#ویژگی":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","مسیرمقاله":"articlepath", +"مسیر_مقاله":"articlepath","articlepath":"articlepath","سرور":"server","کارساز":"server","server":"server","نام‌کارساز":"servername","نام_کارساز":"servername","نام‌سرور":"servername","نام_سرور":"servername","servername":"servername","مسیرسند":"scriptpath","مسیر_سند":"scriptpath","scriptpath":"scriptpath","مسیرسبک":"stylepath","مسیر_سبک":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"تعداددرگروه":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ترتیب":"defaultsort","ترتیب‌پیش‌فرض":"defaultsort","ترتیب_پیش_فرض":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","صفحه‌دررده":"pagesincategory","صفحه_در_رده":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory", +"اندازه‌صفحه":"pagesize","اندازه_صفحه":"pagesize","PAGESIZE":"pagesize","سطح‌حفاطت":"protectionlevel","سطح_حفاظت":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","صفحه‌نین_آدی":"pagename","نام‌صفحه":"pagename","نام_صفحه":"pagename","PAGENAME":"pagename","نام‌صفحه‌کد":"pagenamee","نام_صفحه_کد":"pagenamee","PAGENAMEE":"pagenamee","نام‌کامل‌صفحه":"fullpagename","نام_کامل_صفحه":"fullpagename","FULLPAGENAME":"fullpagename","نام‌کامل‌صفحه‌کد":"fullpagenamee","نام_کامل_صفحه_کد":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","نام‌زیرصفحه":"subpagename","نام_زیرصفحه":"subpagename","SUBPAGENAME":"subpagename","نام‌زیرصفحه‌کد":"subpagenamee","نام_زیرصفحه_کد":"subpagenamee","SUBPAGENAMEE":"subpagenamee","نام_صفحه_ریشه":"rootpagename","ROOTPAGENAME" +:"rootpagename","نام_صفحه_ریشه_ای":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","نام‌صفحه‌مبنا":"basepagename","نام_صفحه_مبنا":"basepagename","BASEPAGENAME":"basepagename","نام‌صفحه‌مبناکد":"basepagenamee","نام_صفحه_مبنا_کد":"basepagenamee","BASEPAGENAMEE":"basepagenamee","نام‌صفحه‌بحث":"talkpagename","نام_صفحه_بحث":"talkpagename","TALKPAGENAME":"talkpagename","نام‌صفحه‌بحث‌کد":"talkpagenamee","نام_صفحه_بحث_کد":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","نام‌صفحه‌موضوع":"subjectpagename","نام‌صفحه‌مقاله":"subjectpagename","نام_صفحه_موضوع":"subjectpagename","نام_صفحه_مقاله":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","نام‌صفحه‌موضوع‌کد":"subjectpagenamee","نام‌صفحه‌مقاله‌کد":"subjectpagenamee", +"نام_صفحه_موضوع_کد":"subjectpagenamee","نام_صفحه_مقاله_کد":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","نسخه":"revisionid","شماره‌نسخه":"revisionid","شماره_نسخه":"revisionid","REVISIONID":"revisionid","روزنسخه":"revisionday","روز_نسخه":"revisionday","REVISIONDAY":"revisionday","روزنسخه۲":"revisionday2","روز_نسخه۲":"revisionday2","روز_نسخه_۲":"revisionday2","REVISIONDAY2":"revisionday2","ماه‌نسخه":"revisionmonth","ماه_نسخه":"revisionmonth","REVISIONMONTH":"revisionmonth","ماه‌نسخه۱":"revisionmonth1","ماه_نسخه_۱":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","سال‌نسخه":"revisionyear","سال_نسخه":"revisionyear","REVISIONYEAR":"revisionyear","زمان‌یونیکسی‌نسخه":"revisiontimestamp","زمان‌نسخه":"revisiontimestamp","زمان_یونیکسی_نسخه":"revisiontimestamp", +"زمان_نسخه":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","کاربرنسخه":"revisionuser","کاربر_نسخه":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","فضای‌نام":"namespace","فضای_نام":"namespace","NAMESPACE":"namespace","فضای‌نام‌کد":"namespacee","فضای_نام_کد":"namespacee","NAMESPACEE":"namespacee","شماره_فضای_نام":"namespacenumber","شماره‌فضای‌نام":"namespacenumber","NAMESPACENUMBER":"namespacenumber","فضای‌بحث":"talkspace","فضای_بحث":"talkspace","TALKSPACE":"talkspace","فضای‌بحث‌کد":"talkspacee","فضای_بحث_کد":"talkspacee","TALKSPACEE":"talkspacee","فضای‌موضوع":"subjectspace","فضای‌مقاله":"subjectspace","فضای_موضوع":"subjectspace","فضای_مقاله":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","فضای‌موضوع‌کد":"subjectspacee", +"فضای‌مقاله‌کد":"subjectspacee","فضای_موضوع_کد":"subjectspacee","فضای_مقاله_کد":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","مقاله‌لر_ساییسی":"numberofarticles","تعدادمقاله‌ها":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","قایل‌لار_ساییسی":"numberoffiles","تعدادپرونده‌ها":"numberoffiles","NUMBEROFFILES":"numberoffiles","یشلدن‌لر_ساییسی":"numberofusers","تعدادکاربران":"numberofusers","NUMBEROFUSERS":"numberofusers","چالیشقان_ایشلدن‌لر":"numberofactiveusers","کاربران‌فعال":"numberofactiveusers","کاربران_فعال":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","صفحه‌لر_ساییسی":"numberofpages","تعدادصفحه‌ها":"numberofpages","NUMBEROFPAGES":"numberofpages","تعدادمدیران":"numberofadmins","NUMBEROFADMINS":"numberofadmins", +"دَییشدیرمه_ساییسی":"numberofedits","تعدادویرایش‌ها":"numberofedits","NUMBEROFEDITS":"numberofedits","عنوان‌ظاهری":"displaytitle","عنوان_ظاهری":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ماه":"currentmonth","ماه‌کنونی":"currentmonth","ماه_کنونی":"currentmonth","ماه‌کنونی۲":"currentmonth","ماه_کنونی۲":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ماه۱":"currentmonth1","ماه‌کنونی۱":"currentmonth1","ماه_کنونی۱":"currentmonth1","CURRENTMONTH1":"currentmonth1","نام‌ماه":"currentmonthname","نام_ماه":"currentmonthname","نام‌ماه‌کنونی":"currentmonthname","نام_ماه_کنونی":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","نام‌ماه‌اضافه":"currentmonthnamegen","نام_ماه_اضافه":"currentmonthnamegen","نام‌ماه‌کنونی‌اضافه": +"currentmonthnamegen","نام_ماه_کنونی_اضافه":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","مخفف‌نام‌ماه":"currentmonthabbrev","مخفف_نام_ماه":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","روز":"currentday","روزکنونی":"currentday","روز_کنونی":"currentday","CURRENTDAY":"currentday","روز۲":"currentday2","روز_۲":"currentday2","CURRENTDAY2":"currentday2","نام‌روز":"currentdayname","نام_روز":"currentdayname","CURRENTDAYNAME":"currentdayname","سال":"currentyear","سال‌کنونی":"currentyear","سال_کنونی":"currentyear","CURRENTYEAR":"currentyear","زمان‌کنونی":"currenttime","زمان_کنونی":"currenttime","CURRENTTIME":"currenttime","ساعت":"currenthour","ساعت‌کنونی":"currenthour","ساعت_کنونی":"currenthour","CURRENTHOUR":"currenthour","ماه‌محلی":"localmonth","ماه_محلی":"localmonth","ماه‌محلی۲": +"localmonth","ماه_محلی۲":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","ماه‌محلی۱":"localmonth1","ماه_محلی۱":"localmonth1","LOCALMONTH1":"localmonth1","نام‌ماه‌محلی":"localmonthname","نام_ماه_محلی":"localmonthname","LOCALMONTHNAME":"localmonthname","نام‌ماه‌محلی‌اضافه":"localmonthnamegen","نام_ماه_محلی_اضافه":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","مخفف‌ماه‌محلی":"localmonthabbrev","مخفف_ماه_محلی":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","روزمحلی":"localday","روز_محلی":"localday","LOCALDAY":"localday","روزمحلی۲":"localday2","روز_محلی_۲":"localday2","LOCALDAY2":"localday2","نام‌روزمحلی":"localdayname","نام_روز_محلی":"localdayname","LOCALDAYNAME":"localdayname","سال‌محلی":"localyear","سال_محلی":"localyear","LOCALYEAR":"localyear","زمان‌محلی": +"localtime","زمان_محلی":"localtime","LOCALTIME":"localtime","ساعت‌محلی":"localhour","ساعت_محلی":"localhour","LOCALHOUR":"localhour","نام‌وبگاه":"sitename","نام_وبگاه":"sitename","SITENAME":"sitename","هفته":"currentweek","CURRENTWEEK":"currentweek","روزهفته":"currentdow","روز_هفته":"currentdow","CURRENTDOW":"currentdow","هفته‌محلی":"localweek","هفته_محلی":"localweek","LOCALWEEK":"localweek","روزهفته‌محلی":"localdow","روز_هفته_محلی":"localdow","LOCALDOW":"localdow","اندازهٔ‌نسخه":"revisionsize","اندازهٔ_نسخه":"revisionsize","REVISIONSIZE":"revisionsize","نسخه‌کنونی":"currentversion","نسخه_کنونی":"currentversion","CURRENTVERSION":"currentversion","زمان‌یونیکسی":"currenttimestamp","زمان_یونیکسی":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","زمان‌یونیکسی‌محلی":"localtimestamp", +"زمان_یونیکسی_محلی":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","علامت‌جهت":"directionmark","علامت_جهت":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","زبان‌محتوا":"contentlanguage","زبان_محتوا":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیآأئؤة‌]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ba.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ba.json new file mode 100644 index 0000000..74e7c78 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ba.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender","множественное_число":"plural","plural":"plural","bidi": +"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#деревокатегорий":"categorytree","#categorytree":"categorytree","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch", +"#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages", +"КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT": +"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename", +"FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE": +"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2": +"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow", +"CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^((?:[a-z]|а|б|в|г|д|е|ё|ж|з|и|й|к|л|м|н|о|п|р|с|т|у|ф|х|ц|ч|ш|щ|ъ|ы|ь|э|ю|я|ә|ө|ү|ғ|ҡ|ң|ҙ|ҫ|һ|“|»)+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ban.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ban.json new file mode 100644 index 0000000..3099c04 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ban.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa": +"language","#bhs":"language","#language":"language","isikiri":"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#nunas":"invoke","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1":"timel","#timel":"timel", +"#hitung":"expr","#expr":"expr","#rel2abs":"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","HALAMANDIKATEGORI": +"pagesincategory","HALDIKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename", +"ROOTPAGENAMEE":"rootpagenamee","NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE":"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid","HARIREVISI": +"revisionday","HAREV":"revisionday","REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace","RUANGNAMAE":"namespacee","RUNAME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace","RUTAMA": +"subjectspace","RUTIKEL":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","JUMLAHARTIKEL":"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits","NUMBEROFEDITS": +"numberofedits","JUDULTAMPILAN":"displaytitle","JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME":"currentdayname", +"TAHUNKINI":"currentyear","TAKIN":"currentyear","CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN":"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname","LOCALDAYNAME": +"localdayname","TAHUNLOKAL":"localyear","TALOK":"localyear","LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK":"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK":"directionmark" +,"DIRMARK":"directionmark","BAHASAISI":"contentlanguage","BHSISI":"contentlanguage","BASI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bar.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bar.json new file mode 100644 index 0000000..d777619 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bar.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__":"forcetoc","__inhaltsverzeichnis__":"toc","__toc__":"toc", +"__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__":"noindex","__NOINDEX__":"noindex","__PERMANENTE_WEITERLEITUNG__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","lokale_url":"localurl","localurl":"localurl","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zahlenformat":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#sprache":"language","#language":"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright": +"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorienbaum":"categorytree","#kategoriebaum":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx", +"#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE":"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SEITENNAME":"pagename","PAGENAME":"pagename","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSSIONSSEITE":"talkpagename","DISK":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","HAUPTSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR":"revisionyear","VERSIONSJAHR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp", +"REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","NAMENSRAUM":"namespace","NAMESPACE":"namespace","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DATEIANZAHL":"numberoffiles","NUMBEROFFILES":"numberoffiles","BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTIVE_BENUTZER": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JETZIGER_MONATSNAME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JETZIGER_KALENDERTAG":"currentday","JETZIGER_TAG":"currentday","CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2","JETZIGER_TAG_2": +"currentday2","CURRENTDAY2":"currentday2","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV":"localmonthnamegen","LOKALER_MONATSNAME_GEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2","LOKALER_TAG_2":"localday2","LOCALDAY2":"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME":"localdayname","LOKALES_JAHR" +:"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize","JETZIGE_VERSION":"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHALTSSPRACHE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE": +"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöüßa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bat-smg.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bat-smg.json new file mode 100644 index 0000000..c9fe6cf --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bat-smg.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__beturin__":"notoc","__notoc__":"notoc","__begalerijos__":"nogallery","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__turinys__":"toc","__toc__":"toc","__beredagsekc__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__": +"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate": +"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER" +:"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","STRAIPSNIŲSKAIČIUS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","FAILŲSKAIČIUS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NAUDOTOJŲSKAIČIUS":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","PUSLAPIŲSKAIČIUS":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","KEITIMŲSKAIČIUS":"numberofedits","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","DABARTINISMĖNESIS":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","DABARTINIOMĖNESIOPAVADINIMAS":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","DABARTINĖDIENA": +"currentday","CURRENTDAY":"currentday","DABARTINĖDIENA2":"currentday2","CURRENTDAY2":"currentday2","DABARTINĖSDIENOSPAVADINIMAS":"currentdayname","CURRENTDAYNAME":"currentdayname","DABARTINIAIMETAI":"currentyear","CURRENTYEAR":"currentyear","DABARTINISLAIKAS":"currenttime","CURRENTTIME":"currenttime","DABARTINĖVALANDA":"currenthour","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark", +"DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-ząčęėįšųūž]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bcl.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bcl.json new file mode 100644 index 0000000..eec0fa2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bcl.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__NAKATAGONGKAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","lokalurl":"localurl","localurl":"localurl","lokalurle":"localurle","localurle":"localurle","todongurl":"fullurl","fullurl":"fullurl","todongurle":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","dakol":"plural","plural":"plural","bidi":"bidi","#tataramon":"language","#language":"language","padwala":"padleft","padleft":"padleft","padtoo":"padright","padright":"padright","anchorencode":"anchorencode","filedalan":"filepath", +"filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup", +"DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAHINASAKATEGORYA":"pagesincategory","PAHINASAKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAHINASOKOL":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NGARANKANPAHINA":"pagename","PAGENAME":"pagename","KAGNGARANKANPAHINA":"pagenamee","PAGENAMEE":"pagenamee","TODONGNGARANKANPAHINA":"fullpagename","FULLPAGENAME":"fullpagename","KAGNGARANKANTODONGNGARANKANPAHINA":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","NGARANKANPAHINANINOLAY":"talkpagename","TALKPAGENAME":"talkpagename","KAGNGARANKANPAHINANINOLAY":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME": +"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NGARANESPASYO":"namespace","NAMESPACE":"namespace","KAGNGARANESPASYO":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","OLAYESPASYO":"talkspace","TALKSPACE":"talkspace","KAGOLAYESPASYO":"talkspacee","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMEROKANARTIKULO":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROKANDOKUMENTO":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROKANPARAGAMIT":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","NUMEROKANPAHINA":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROKANTAGAMATO":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMEROKANLIGWAT":"numberofedits","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","BULANNGONYAN":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NGARANBULANNGONYAN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","ALDAWNGONYAN":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","TAONNGONYAN":"currentyear","CURRENTYEAR":"currentyear","PANAHONNGONYAN":"currenttime","CURRENTTIME":"currenttime","ORASNGONYAN":"currenthour","CURRENTHOUR":"currenthour","LOKALBULAN":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","NGARANLOKALBULAN":"localmonthname", +"LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOKALALDAW":"localday","LOCALDAY":"localday","LOKALALDAW2":"localday2","LOCALDAY2":"localday2","NGARANLOKALALDAW":"localdayname","LOCALDAYNAME":"localdayname","LOKALTAON":"localyear","LOCALYEAR":"localyear","LOKALPANAHON":"localtime","LOCALTIME":"localtime","LOKALORAS":"localhour","LOCALHOUR":"localhour","SITENAME":"sitename","SEMANANGONYAN":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOKALSEMANA":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","TATARAMONKANLAOG":"contentlanguage","TATARAMONLAOG":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-be-tarask.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-be-tarask.json new file mode 100644 index 0000000..946d93c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-be-tarask.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__бязь_зьместу__":"notoc","__notoc__":"notoc","__без_галерэі__":"nogallery","__nogallery__":"nogallery","__зьмест_прымусам__":"forcetoc","__forcetoc__":"forcetoc","__зьмест__":"toc","__toc__":"toc","__без_рэдагаваньня_сэкцыі__":"noeditsection","__noeditsection__": +"noeditsection","__не_канвэртаваць_назву__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__не_канвэртаваць_тэкст__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__СПАСЫЛКА_НА_НОВУЮ_СЭКЦЫЮ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СХАВАЦЬ_КАТЭГОРЫЮ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__СТАТЫЧНАЕ_ПЕРАНАКІРАВАНЬНЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"пн":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","першая_літара_малая":"lcfirst","lcfirst":"lcfirst","першая_літара_вялікая":"ucfirst","ucfirst": +"ucfirst","малымі_літарамі":"lc","lc":"lc","вялікімі_літарамі":"uc","uc":"uc","лякальны_адрас":"localurl","localurl":"localurl","лякальны_адрас_2":"localurle","localurle":"localurle","поўны_адрас":"fullurl","fullurl":"fullurl","поўны_адрас_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","фарматаваць_лік":"formatnum","formatnum":"formatnum","граматыка":"grammar","grammar":"grammar","пол":"gender","gender":"gender","множны_лік":"plural","plural":"plural","bidi":"bidi","#мова":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","шлях_да_файла":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#тэг":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree", +"#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","сэрвэр":"server","server":"server","назва_сэрвэра":"servername","servername":"servername","шлях_да_скрыпта":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"КОЛЬКАСЬЦЬ_СТАРОНАК":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛЬКАСЬЦЬ_УДЗЕЛЬНІКАЎ":"numberofusers","NUMBEROFUSERS": +"numberofusers","КОЛЬКАСЬЦЬ_АКТЫЎНЫХ_УДЗЕЛЬНІКАЎ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛЬКАСЬЦЬ_АРТЫКУЛАЎ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛЬКАСЬЦЬ_ФАЙЛАЎ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛЬКАСЬЦЬ_АДМІНІСТРАТАРАЎ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","КОЛЬКАСЬЦЬ_РЭДАГАВАНЬНЯЎ":"numberofedits","NUMBEROFEDITS":"numberofedits","САРТЫРОЎКА_ПА_ЗМОЎЧВАНЬНІ":"defaultsort","КЛЮЧ_САРТЫРОЎКІ_ПА_ЗМОЎЧВАНЬНІ":"defaultsort","САРТЫРОЎКА_Ў_КАТЭГОРЫІ_ПА_ЗМОЎЧВАНЬНІ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","КОЛЬКАСЬЦЬ_СТАРОНАК_У_КАТЭГОРЫІ":"pagesincategory", +"PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ПАМЕР_СТАРОНКІ":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ПРАСТОРА_НАЗВАЎ_2":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","ПРАСТОРА_НАЗВАЎ_АБМЕРКАВАНЬНЯ":"talkspace","TALKSPACE":"talkspace","ПРАСТОРА_НАЗВАЎ_АБМЕРКАВАНЬНЯ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРАСТОРА_НАЗВАЎ_ПРАДМЕТУ":"subjectspace","ПРАСТОРА_НАЗВАЎ_АРТЫКУЛА":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРАСТОРА_НАЗВАЎ_ПРАДМЕТУ_2":"subjectspacee","ПРАСТОРА_НАЗВАЎ_АРТЫКУЛА_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","НАЗВА_СТАРОНКІ":"pagename","PAGENAME":"pagename","НАЗВА_СТАРОНКІ_2":"pagenamee" +,"PAGENAMEE":"pagenamee","ПОЎНАЯ_НАЗВА_СТАРОНКІ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЎНАЯ_НАЗВА_СТАРОНКІ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","НАЗВА_БАЗАВАЙ_СТАРОНКІ":"basepagename","BASEPAGENAME":"basepagename","НАЗВА_БАЗАВАЙ_СТАРОНКІ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВА_ПАДСТАРОНКІ":"subpagename","SUBPAGENAME":"subpagename","НАЗВА_ПАДСТАРОНКІ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","НАЗВА_СТАРОНКІ_АБМЕРКАВАНЬНЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВА_СТАРОНКІ_АБМЕРКАВАНЬНЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВА_СТАРОНКІ_ПРАДМЕТУ":"subjectpagename","НАЗВА_СТАРОНКІ_АРТЫКУЛА":"subjectpagename","SUBJECTPAGENAME":"subjectpagename", +"ARTICLEPAGENAME":"subjectpagename","НАЗВА_СТАРОНКІ_ПРАДМЕТУ_2":"subjectpagenamee","НАЗВА_СТАРОНКІ_АРТЫКУЛА_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ID_ВЭРСІІ":"revisionid","REVISIONID":"revisionid","ДЗЕНЬ_ВЭРСІІ":"revisionday","REVISIONDAY":"revisionday","ДЗЕНЬ_ВЭРСІІ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЭРСІІ":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ГОД_ВЭРСІІ":"revisionyear","REVISIONYEAR":"revisionyear","МОМАНТ_ЧАСУ_ВЭРСІІ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРАСТОРА_НАЗВАЎ":"namespace","NAMESPACE":"namespace","ПАКАЗВАЦЬ_НАЗВУ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","БЯГУЧЫ_МЕСЯЦ":"currentmonth","CURRENTMONTH": +"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","НАЗВА_БЯГУЧАГА_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВА_БЯГУЧАГА_МЕСЯЦА_Ў_РОДНЫМ_СКЛОНЕ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","СКАРОЧАНАЯ_НАЗВА_БЯГУЧАГА_МЕСЯЦА":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","БЯГУЧЫ_ДЗЕНЬ":"currentday","CURRENTDAY":"currentday","БЯГУЧЫ_ДЗЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВА_БЯГУЧАГА_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","БЯГУЧЫ_ГОД":"currentyear","CURRENTYEAR":"currentyear","БЯГУЧЫ_ЧАС":"currenttime","CURRENTTIME":"currenttime","БЯГУЧАЯ_ГАДЗІНА":"currenthour","CURRENTHOUR":"currenthour","ЛЯКАЛЬНЫ_МЕСЯЦ":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1", +"НАЗВА_ЛЯКАЛЬНАГА_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВА_ЛЯКАЛЬНАГА_МЕСЯЦА_Ў_РОДНЫМ_СКЛОНЕ":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","СКАРОЧАНАЯ_НАЗВА_ЛЯКАЛЬНАГА_МЕСЯЦА":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ЛЯКАЛЬНЫ_ДЗЕНЬ":"localday","LOCALDAY":"localday","ЛЯКАЛЬНЫ_ДЗЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВА_ЛЯКАЛЬНАГА_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","ЛЯКАЛЬНЫ_ГОД":"localyear","LOCALYEAR":"localyear","ЛЯКАЛЬНЫ_ЧАС":"localtime","LOCALTIME":"localtime","ЛЯКАЛЬНАЯ_ГАДЗІНА":"localhour","LOCALHOUR":"localhour","НАЗВА_САЙТУ":"sitename","SITENAME":"sitename","БЯГУЧЫ_ТЫДЗЕНЬ":"currentweek","CURRENTWEEK":"currentweek","БЯГУЧЫ_ДЗЕНЬ_ТЫДНЯ":"currentdow","CURRENTDOW":"currentdow", +"ЛЯКАЛЬНЫ_ТЫДЗЕНЬ":"localweek","LOCALWEEK":"localweek","ЛЯКАЛЬНЫ_ДЗЕНЬ_ТЫДНЯ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","БЯГУЧАЯ_ВЭРСІЯ":"currentversion","CURRENTVERSION":"currentversion","МОМАНТ_ЧАСУ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ЛЯКАЛЬНЫ_МОМАНТ_ЧАСУ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","СЫМБАЛЬ_НАПРАМКУ_ПІСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","МОВА_ЗЬМЕСТУ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([абвгґджзеёжзійклмнопрстуўфхцчшыьэюяćčłńśšŭźža-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-be-x-old.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-be-x-old.json new file mode 100644 index 0000000..946d93c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-be-x-old.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__бязь_зьместу__":"notoc","__notoc__":"notoc","__без_галерэі__":"nogallery","__nogallery__":"nogallery","__зьмест_прымусам__":"forcetoc","__forcetoc__":"forcetoc","__зьмест__":"toc","__toc__":"toc","__без_рэдагаваньня_сэкцыі__":"noeditsection","__noeditsection__": +"noeditsection","__не_канвэртаваць_назву__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__не_канвэртаваць_тэкст__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__СПАСЫЛКА_НА_НОВУЮ_СЭКЦЫЮ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СХАВАЦЬ_КАТЭГОРЫЮ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__СТАТЫЧНАЕ_ПЕРАНАКІРАВАНЬНЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"пн":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","першая_літара_малая":"lcfirst","lcfirst":"lcfirst","першая_літара_вялікая":"ucfirst","ucfirst": +"ucfirst","малымі_літарамі":"lc","lc":"lc","вялікімі_літарамі":"uc","uc":"uc","лякальны_адрас":"localurl","localurl":"localurl","лякальны_адрас_2":"localurle","localurle":"localurle","поўны_адрас":"fullurl","fullurl":"fullurl","поўны_адрас_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","фарматаваць_лік":"formatnum","formatnum":"formatnum","граматыка":"grammar","grammar":"grammar","пол":"gender","gender":"gender","множны_лік":"plural","plural":"plural","bidi":"bidi","#мова":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","шлях_да_файла":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#тэг":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree", +"#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","сэрвэр":"server","server":"server","назва_сэрвэра":"servername","servername":"servername","шлях_да_скрыпта":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"КОЛЬКАСЬЦЬ_СТАРОНАК":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛЬКАСЬЦЬ_УДЗЕЛЬНІКАЎ":"numberofusers","NUMBEROFUSERS": +"numberofusers","КОЛЬКАСЬЦЬ_АКТЫЎНЫХ_УДЗЕЛЬНІКАЎ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛЬКАСЬЦЬ_АРТЫКУЛАЎ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛЬКАСЬЦЬ_ФАЙЛАЎ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛЬКАСЬЦЬ_АДМІНІСТРАТАРАЎ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","КОЛЬКАСЬЦЬ_РЭДАГАВАНЬНЯЎ":"numberofedits","NUMBEROFEDITS":"numberofedits","САРТЫРОЎКА_ПА_ЗМОЎЧВАНЬНІ":"defaultsort","КЛЮЧ_САРТЫРОЎКІ_ПА_ЗМОЎЧВАНЬНІ":"defaultsort","САРТЫРОЎКА_Ў_КАТЭГОРЫІ_ПА_ЗМОЎЧВАНЬНІ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","КОЛЬКАСЬЦЬ_СТАРОНАК_У_КАТЭГОРЫІ":"pagesincategory", +"PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ПАМЕР_СТАРОНКІ":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ПРАСТОРА_НАЗВАЎ_2":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","ПРАСТОРА_НАЗВАЎ_АБМЕРКАВАНЬНЯ":"talkspace","TALKSPACE":"talkspace","ПРАСТОРА_НАЗВАЎ_АБМЕРКАВАНЬНЯ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРАСТОРА_НАЗВАЎ_ПРАДМЕТУ":"subjectspace","ПРАСТОРА_НАЗВАЎ_АРТЫКУЛА":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРАСТОРА_НАЗВАЎ_ПРАДМЕТУ_2":"subjectspacee","ПРАСТОРА_НАЗВАЎ_АРТЫКУЛА_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","НАЗВА_СТАРОНКІ":"pagename","PAGENAME":"pagename","НАЗВА_СТАРОНКІ_2":"pagenamee" +,"PAGENAMEE":"pagenamee","ПОЎНАЯ_НАЗВА_СТАРОНКІ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЎНАЯ_НАЗВА_СТАРОНКІ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","НАЗВА_БАЗАВАЙ_СТАРОНКІ":"basepagename","BASEPAGENAME":"basepagename","НАЗВА_БАЗАВАЙ_СТАРОНКІ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВА_ПАДСТАРОНКІ":"subpagename","SUBPAGENAME":"subpagename","НАЗВА_ПАДСТАРОНКІ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","НАЗВА_СТАРОНКІ_АБМЕРКАВАНЬНЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВА_СТАРОНКІ_АБМЕРКАВАНЬНЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВА_СТАРОНКІ_ПРАДМЕТУ":"subjectpagename","НАЗВА_СТАРОНКІ_АРТЫКУЛА":"subjectpagename","SUBJECTPAGENAME":"subjectpagename", +"ARTICLEPAGENAME":"subjectpagename","НАЗВА_СТАРОНКІ_ПРАДМЕТУ_2":"subjectpagenamee","НАЗВА_СТАРОНКІ_АРТЫКУЛА_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ID_ВЭРСІІ":"revisionid","REVISIONID":"revisionid","ДЗЕНЬ_ВЭРСІІ":"revisionday","REVISIONDAY":"revisionday","ДЗЕНЬ_ВЭРСІІ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЭРСІІ":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ГОД_ВЭРСІІ":"revisionyear","REVISIONYEAR":"revisionyear","МОМАНТ_ЧАСУ_ВЭРСІІ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРАСТОРА_НАЗВАЎ":"namespace","NAMESPACE":"namespace","ПАКАЗВАЦЬ_НАЗВУ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","БЯГУЧЫ_МЕСЯЦ":"currentmonth","CURRENTMONTH": +"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","НАЗВА_БЯГУЧАГА_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВА_БЯГУЧАГА_МЕСЯЦА_Ў_РОДНЫМ_СКЛОНЕ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","СКАРОЧАНАЯ_НАЗВА_БЯГУЧАГА_МЕСЯЦА":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","БЯГУЧЫ_ДЗЕНЬ":"currentday","CURRENTDAY":"currentday","БЯГУЧЫ_ДЗЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВА_БЯГУЧАГА_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","БЯГУЧЫ_ГОД":"currentyear","CURRENTYEAR":"currentyear","БЯГУЧЫ_ЧАС":"currenttime","CURRENTTIME":"currenttime","БЯГУЧАЯ_ГАДЗІНА":"currenthour","CURRENTHOUR":"currenthour","ЛЯКАЛЬНЫ_МЕСЯЦ":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1", +"НАЗВА_ЛЯКАЛЬНАГА_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВА_ЛЯКАЛЬНАГА_МЕСЯЦА_Ў_РОДНЫМ_СКЛОНЕ":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","СКАРОЧАНАЯ_НАЗВА_ЛЯКАЛЬНАГА_МЕСЯЦА":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ЛЯКАЛЬНЫ_ДЗЕНЬ":"localday","LOCALDAY":"localday","ЛЯКАЛЬНЫ_ДЗЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВА_ЛЯКАЛЬНАГА_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","ЛЯКАЛЬНЫ_ГОД":"localyear","LOCALYEAR":"localyear","ЛЯКАЛЬНЫ_ЧАС":"localtime","LOCALTIME":"localtime","ЛЯКАЛЬНАЯ_ГАДЗІНА":"localhour","LOCALHOUR":"localhour","НАЗВА_САЙТУ":"sitename","SITENAME":"sitename","БЯГУЧЫ_ТЫДЗЕНЬ":"currentweek","CURRENTWEEK":"currentweek","БЯГУЧЫ_ДЗЕНЬ_ТЫДНЯ":"currentdow","CURRENTDOW":"currentdow", +"ЛЯКАЛЬНЫ_ТЫДЗЕНЬ":"localweek","LOCALWEEK":"localweek","ЛЯКАЛЬНЫ_ДЗЕНЬ_ТЫДНЯ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","БЯГУЧАЯ_ВЭРСІЯ":"currentversion","CURRENTVERSION":"currentversion","МОМАНТ_ЧАСУ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ЛЯКАЛЬНЫ_МОМАНТ_ЧАСУ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","СЫМБАЛЬ_НАПРАМКУ_ПІСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","МОВА_ЗЬМЕСТУ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([абвгґджзеёжзійклмнопрстуўфхцчшыьэюяćčłńśšŭźža-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-be.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-be.json new file mode 100644 index 0000000..bdba288 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-be.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([абвгґджзеёжзійклмнопрстуўфхцчшыьэюяćčłńśšŭźža-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bg.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bg.json new file mode 100644 index 0000000..971dd9a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bg.json @@ -0,0 +1,9 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__безсъдържание__":"notoc","__notoc__":"notoc","__безгалерия__":"nogallery","__nogallery__":"nogallery","__съссъдържание__":"forcetoc","__forcetoc__":"forcetoc","__съдържание__":"toc","__toc__":"toc","__без_редактиране_на_раздели__":"noeditsection", +"__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ВРЪЗКА_ЗА_НОВ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРИТАКАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКСИРАНЕ__":"index","__INDEX__":"index","__БЕЗИНДЕКСИРАНЕ__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ип":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","мбпърва":"lcfirst","lcfirst":"lcfirst","гбпърва":"ucfirst","ucfirst":"ucfirst","мб":"lc","lc":"lc","гб":"uc","uc":"uc","локаленадрес":"localurl","localurl": +"localurl","локаленадреси":"localurle","localurle":"localurle","пълен_адрес":"fullurl","fullurl":"fullurl","пълен_адреси":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","граматика":"grammar","grammar":"grammar","пол":"gender","gender":"gender","мн_число":"plural","plural":"plural","bidi":"bidi","#език":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","вътр":"int","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs", +"#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","сървър":"server","server":"server","именасървъра":"servername","servername":"servername","пътдоскрипта":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТКАТ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","СТРАНИЦА":"pagename","PAGENAME": +"pagename","СТРАНИЦАИ":"pagenamee","PAGENAMEE":"pagenamee","ПЪЛНОИМЕ_СТРАНИЦА":"fullpagename","FULLPAGENAME":"fullpagename","ПЪЛНОИМЕ_СТРАНИЦАИ":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ИМЕ_ПОДСТРАНИЦА":"subpagename","SUBPAGENAME":"subpagename","ИМЕ_ПОДСТРАНИЦАИ":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","ИМЕ_БЕСЕДА":"talkpagename","TALKPAGENAME":"talkpagename","ИМЕ_БЕСЕДАИ":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_НА_ВЕРСИЯТА":"revisionid","REVISIONID":"revisionid","ДЕН_НА_ВЕРСИЯТА":"revisionday","REVISIONDAY":"revisionday","ДЕН_НА_ВЕРСИЯТА2":"revisionday2", +"REVISIONDAY2":"revisionday2","МЕСЕЦ_НА_ВЕРСИЯТА":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ГОДИНА_НА_ВЕРСИЯТА":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ИМЕННОПРОСТРАНСТВО":"namespace","NAMESPACE":"namespace","ИМЕННОПРОСТРАНСТВОИ":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","БРОЙСТАТИИ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","БРОЙФАЙЛОВЕ":"numberoffiles","NUMBEROFFILES":"numberoffiles","БРОЙПОТРЕБИТЕЛИ":"numberofusers","NUMBEROFUSERS":"numberofusers","БРОЙАКТИВНИПОТРЕБИТЕЛИ": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","БРОЙСТРАНИЦИ":"numberofpages","NUMBEROFPAGES":"numberofpages","БРОЙАДМИНИСТРАТОРИ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","БРОЙРЕДАКЦИИ":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗВ_ЗАГЛАВИЕ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩМЕСЕЦ":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩМЕСЕЦ1":"currentmonth1","CURRENTMONTH1":"currentmonth1","ТЕКУЩМЕСЕЦИМЕ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","ТЕКУЩМЕСЕЦИМЕРОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ТЕКУЩМЕСЕЦСЪКР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩДЕН":"currentday","CURRENTDAY":"currentday","ТЕКУЩДЕН2":"currentday2","CURRENTDAY2":"currentday2","ТЕКУЩДЕНИМЕ": +"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩАГОДИНА":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩОВРЕМЕ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩЧАС":"currenthour","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","ИМЕНАСАЙТА":"sitename","SITENAME":"sitename","ТЕКУЩАСЕДМИЦА":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩ_ДЕН_ОТ_СЕДМИЦАТА":"currentdow","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK": +"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдежзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bh.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bh.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bh.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bi.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bi.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bi.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bjn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bjn.json new file mode 100644 index 0000000..16771f6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bjn.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa":"language","#bhs":"language","#language":"language","isikiri": +"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1":"timel","#timel":"timel","#hitung":"expr","#expr":"expr","#rel2abs": +"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHARTIKEL":"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES": +"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits","NUMBEROFEDITS":"numberofedits","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","HALAMANDIKATEGORI":"pagesincategory","HALDIKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","RUANGNAMAE":"namespacee","RUNAME": +"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace","RUTAMA":"subjectspace","RUTIKEL":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename", +"ROOTPAGENAMEE":"rootpagenamee","NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE": +"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid","HARIREVISI":"revisionday","HAREV":"revisionday","REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace","JUDULTAMPILAN":"displaytitle","JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle", +"!":"!","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME":"currentdayname","TAHUNKINI":"currentyear","TAKIN":"currentyear","CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN": +"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname","LOCALDAYNAME":"localdayname","TAHUNLOKAL":"localyear","TALOK":"localyear","LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK": +"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","BAHASAISI":"contentlanguage","BHSISI":"contentlanguage","BASI":"contentlanguage", +"CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-blk.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-blk.json new file mode 100644 index 0000000..4340453 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-blk.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates", +"#ဖော်ပြ":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE": +"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY": +"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bm.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bm.json new file mode 100644 index 0000000..620e0f2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bm.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode","initminus":"lcfirst","lcfirst":"lcfirst","initmajus": +"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int","#spécial":"special","#special":"special","#spéciale": +"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property","#property":"property","#statements":"statements", +"#commaseparatedlist":"commaSeparatedList","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","CLEFDETRI":"defaultsort", +"CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee","ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMPAGE":"pagename","PAGENAME":"pagename","NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename", +"FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid","REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday", +"REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev", +"JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL":"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL": +"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bn.json new file mode 100644 index 0000000..09f7c53 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bn.json @@ -0,0 +1,26 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__কোন_বিষয়বস্তুর_ছক_নয়__":"notoc","__কোনবিষয়বস্তুরছকনয়__":"notoc","__কোন_বিষয়বস্তুর_টেবিল_নয়__":"notoc","__কোনবিষয়বস্তুরটেবিলনয়__":"notoc","__notoc__": +"notoc","__কোনগ্যালারিনয়__":"nogallery","__কোনগ্যালারীনয়__":"nogallery","__কোন_গ্যালারি_নয়__":"nogallery","__কোন_গ্যালারী_নয়__":"nogallery","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__বিষয়বস্তুর_ছক__":"toc","__বিষয়বস্তুরছক__":"toc","__বিষয়বস্তুর_টেবিল__":"toc","__বিষয়বস্তুরটেবিল__":"toc","__toc__":"toc","__কোনসম্পাদনাঅনুচ্ছেদনয়__":"noeditsection","__কোন_সম্পাদনা_অনুচ্ছেদ_নয়__":"noeditsection","__noeditsection__":"noeditsection","__কোন_শিরোনাম_রূপান্তরকারী_নয়__":"notitleconvert","__কোনশিরোনামরূপান্তরকারীনয়__":"notitleconvert","__notitleconvert__": +"notitleconvert","__notc__":"notitleconvert","__কোন_বিষয়বস্তু_রূপান্তরকারী_নয়__":"nocontentconvert","__কোনবিষয়বস্তুরূপান্তরকারীনয়__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__নতুন_অনুচ্ছেদের_সংযোগ__":"newsectionlink","__নতুন_অনুচ্ছেদের_লিঙ্ক__":"newsectionlink","__নতুনঅনুচ্ছেদেরসংযোগ__":"newsectionlink","__নতুনঅনুচ্ছেদেরলিঙ্ক__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__কোন_নতুন_অনুচ্ছেদের_সংযোগ_নয়__":"nonewsectionlink","__কোন_নতুন_অনুচ্ছেদের_লিঙ্ক_নয়__":"nonewsectionlink","__কোননতুনঅনুচ্ছেদেরসংযোগনয়__": +"nonewsectionlink","__কোননতুনঅনুচ্ছেদেরলিঙ্কনয়__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__লুকানো_বিষয়শ্রেণী__":"hiddencat","__লুকানোবিষয়শ্রেণী__":"hiddencat","__লুকায়িতবিষয়শ্রেণী__":"hiddencat","__লুক্কায়িতবিষয়শ্রেণী__":"hiddencat","__লুক্কায়িত_বিষয়শ্রেণী__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__নির্ঘণ্ট__":"index","__INDEX__":"index","__কোননির্ঘণ্টনয়__":"noindex","__কোন_নির্ঘণ্ট_নয়__":"noindex","__নির্ঘণ্টনয়__":"noindex","__NOINDEX__":"noindex","__স্থির_পুনর্নির্দেশ__":"staticredirect", +"__স্থিরপুনর্নির্দেশ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","স্থানীয়_ইউআরএল":"localurl","স্থানীয়ইউআরএল":"localurl","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","নম্বর_বিন্যাস":"formatnum","নম্বরবিন্যাস":"formatnum","formatnum":"formatnum","ব্যাকরণ":"grammar","grammar":"grammar","লিঙ্গ":"gender","gender":"gender","বহুবচন":"plural","plural":"plural","বিআইডিআই":"bidi","bidi":"bidi","#ভাষা":"language","#language":"language", +"padleft":"padleft","padright":"padright","anchorencode":"anchorencode","ফাইলের_পথ":"filepath","ফাইলেরপথ":"filepath","filepath":"filepath","পাতার_আইডি":"pageid","পাতারআইডি":"pageid","পৃষ্ঠার_আইডি":"pageid","পৃষ্ঠারআইডি":"pageid","pageid":"pageid","int":"int","#বিশেষ":"special","#special":"special","#speciale":"speciale","#ট্যাগ":"tag","#tag":"tag","#তারিখ_বিন্যাস":"formatdate","#তারিখবিন্যাস":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","pendingchangelevel":"pendingchangelevel","#লক্ষ্য":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs", +"#titleparts":"titleparts","#বিষয়শ্রেণী_বৃক্ষ":"categorytree","#বিষয়শ্রেণীবৃক্ষ":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#মেন্টর":"mentor","#mentor":"mentor","নিবন্ধের_পথ":"articlepath","নিবন্ধেরপথ":"articlepath","articlepath":"articlepath","সার্ভার":"server","server":"server","সার্ভারের_নাম":"servername","সার্ভারেরনাম":"servername","servername":"servername","স্ক্রিপ্টের_পথ":"scriptpath","স্ক্রিপ্টেরপথ":"scriptpath","scriptpath":"scriptpath","শৈলীর_পথ":"stylepath","শৈলীরপথ":"stylepath", +"stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"দলে_সংখ্যা":"numberingroup","দলেসংখ্যা":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","পূর্বনির্ধারিত_বাছাই":"defaultsort","পূর্বনির্ধারিতবাছাই":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","বিষয়শ্রেণীতেপাতা":"pagesincategory","বিষয়শ্রেণীতেপৃষ্ঠা":"pagesincategory","বিষয়শ্রেণীতে_পাতা":"pagesincategory","বিষয়শ্রেণীতে_পৃষ্ঠা":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","পাতার_আকার":"pagesize","পাতারআকার":"pagesize","পৃষ্ঠার_আকার":"pagesize", +"পৃষ্ঠারআকার":"pagesize","PAGESIZE":"pagesize","সুরক্ষার_স্তর":"protectionlevel","সুরক্ষারস্তর":"protectionlevel","সুরক্ষা_স্তর":"protectionlevel","সুরক্ষাস্তর":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","সুরক্ষার_মেয়াদোত্তীর্ণ":"protectionexpiry","সুরক্ষারমেয়াদোত্তীর্ণ":"protectionexpiry","সুরক্ষা_মেয়াদোত্তীর্ণ":"protectionexpiry","সুরক্ষামেয়াদোত্তীর্ণ":"protectionexpiry","PROTECTIONEXPIRY":"protectionexpiry","পাতার_নাম":"pagename","পাতারনাম":"pagename","পৃষ্ঠার_নাম":"pagename","পৃষ্ঠারনাম":"pagename","PAGENAME":"pagename","পাতার_নামম":"pagenamee","পাতারনামম":"pagenamee", +"পৃষ্ঠার_নামম":"pagenamee","পৃষ্ঠারনামম":"pagenamee","PAGENAMEE":"pagenamee","পূর্ণ_পাতার_নাম":"fullpagename","সম্পূর্ণ_পাতার_নাম":"fullpagename","পূর্ণপাতারনাম":"fullpagename","সম্পূর্ণপাতারনাম":"fullpagename","পূর্ণ_পৃষ্ঠার_নাম":"fullpagename","সম্পূর্ণ_পৃষ্ঠার_নাম":"fullpagename","পূর্ণপৃষ্ঠারনাম":"fullpagename","সম্পূর্ণপৃষ্ঠারনাম":"fullpagename","FULLPAGENAME":"fullpagename","পূর্ণ_পাতার_নামম":"fullpagenamee","সম্পূর্ণ_পাতার_নামম":"fullpagenamee","পূর্ণপাতারনামম":"fullpagenamee","সম্পূর্ণপাতারনামম":"fullpagenamee","পূর্ণ_পৃষ্ঠার_নামম": +"fullpagenamee","সম্পূর্ণ_পৃষ্ঠার_নামম":"fullpagenamee","পূর্ণপৃষ্ঠারনামম":"fullpagenamee","সম্পূর্ণপৃষ্ঠারনামম":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","উপপাতার_নাম":"subpagename","উপপাতারনাম":"subpagename","উপপৃষ্ঠার_নাম":"subpagename","উপপৃষ্ঠারনাম":"subpagename","SUBPAGENAME":"subpagename","উপপাতার_নামম":"subpagenamee","উপপাতারনামম":"subpagenamee","উপপৃষ্ঠার_নামম":"subpagenamee","উপপৃষ্ঠারনামম":"subpagenamee","SUBPAGENAMEE":"subpagenamee","মূল_পাতার_নাম":"rootpagename","মূলপাতারনাম":"rootpagename","মূল_পৃষ্ঠার_নাম":"rootpagename","ROOTPAGENAME":"rootpagename","মূল_পাতার_নামম":"rootpagenamee", +"মূলপাতারনামম":"rootpagenamee","মূল_পৃষ্ঠার_নামম":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","ভিত্তি_পাতার_নাম":"basepagename","ভিত্তিপাতারনাম":"basepagename","ভিত্তি_পৃষ্ঠার_নাম":"basepagename","BASEPAGENAME":"basepagename","ভিত্তি_পাতার_নামম":"basepagenamee","ভিত্তিপাতারনামম":"basepagenamee","ভিত্তি_পৃষ্ঠার_নামম":"basepagenamee","BASEPAGENAMEE":"basepagenamee","আলাপ_পাতার_নাম":"talkpagename","আলাপপাতারনাম":"talkpagename","আলাপ_পৃষ্ঠার_নাম":"talkpagename","আলোচনা_পৃষ্ঠার_নাম":"talkpagename","আলোচনা_পাতার_নাম":"talkpagename","আলোচনাপাতারনাম":"talkpagename","TALKPAGENAME":"talkpagename", +"আলাপ_পাতার_নামম":"talkpagenamee","আলাপপাতারনামম":"talkpagenamee","আলাপ_পৃষ্ঠার_নামম":"talkpagenamee","আলোচনা_পৃষ্ঠার_নামম":"talkpagenamee","আলোচনা_পাতার_নামম":"talkpagenamee","আলোচনাপাতারনামম":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","বিষয়ের_পাতার_নাম":"subjectpagename","বিষয়েরপাতারনাম":"subjectpagename","বিষয়ের_পৃষ্ঠার_নাম":"subjectpagename","বিষয়েরপৃষ্ঠারনাম":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","বিষয়ের_পাতার_নামম":"subjectpagenamee","বিষয়েরপাতারনামম":"subjectpagenamee","বিষয়ের_পৃষ্ঠার_নামম":"subjectpagenamee", +"বিষয়েরপৃষ্ঠারনামম":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","সংশোধনের_আইডি":"revisionid","সংশোধনেরআইডি":"revisionid","REVISIONID":"revisionid","সংশোধনের_দিন":"revisionday","সংশোধনেরদিন":"revisionday","REVISIONDAY":"revisionday","সংশোধনের_দিন_২":"revisionday2","সংশোধনেরদিন২":"revisionday2","REVISIONDAY2":"revisionday2","সংশোধনের_মাস":"revisionmonth","সংশোধনেরমাস":"revisionmonth","REVISIONMONTH":"revisionmonth","সংশোধনের_মাস_১":"revisionmonth1","সংশোধনেরমাস১":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","সংশোধনের_বছর":"revisionyear","সংশোধনেরবছর":"revisionyear","REVISIONYEAR":"revisionyear", +"সংশোধনের_সময়তারিখ":"revisiontimestamp","সংশোধনেরসময়তারিখ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","সংশোধনের_ব্যবহারকারী":"revisionuser","সংশোধনেরব্যবহারকারী":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","নামস্থান":"namespace","NAMESPACE":"namespace","নামস্থানন":"namespacee","NAMESPACEE":"namespacee","নামস্থানের_সংখ্যা":"namespacenumber","নামস্থানেরসংখ্যা":"namespacenumber","NAMESPACENUMBER":"namespacenumber","আলাপের_স্থান":"talkspace","আলোচনার_স্থান":"talkspace","আলাপেরস্থান":"talkspace","আলোচনারস্থান":"talkspace","আলাপের_জায়গা":"talkspace", +"আলাপেরজায়গা":"talkspace","TALKSPACE":"talkspace","আলাপের_স্থানন":"talkspacee","আলোচনার_স্থানন":"talkspacee","আলাপেরস্থানন":"talkspacee","আলোচনারস্থানন":"talkspacee","TALKSPACEE":"talkspacee","বিষয়ের_স্থান":"subjectspace","নিবন্ধের_স্থান":"subjectspace","বিষয়েরস্থান":"subjectspace","নিবন্ধেরস্থান":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","বিষয়ের_স্থানন":"subjectspacee","নিবন্ধের_স্থানন":"subjectspacee","বিষয়েরস্থানন":"subjectspacee","নিবন্ধেরস্থানন":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","নিবন্ধের_সংখ্যা":"numberofarticles", +"নিবন্ধেরসংখ্যা":"numberofarticles","নিবন্ধ_সংখ্যা":"numberofarticles","নিবন্ধসংখ্যা":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ফাইলের_সংখ্যা":"numberoffiles","ফাইলেরসংখ্যা":"numberoffiles","ফাইল_সংখ্যা":"numberoffiles","ফাইলসংখ্যা":"numberoffiles","NUMBEROFFILES":"numberoffiles","ব্যবহারকারীর_সংখ্যা":"numberofusers","ব্যবহারকারীরসংখ্যা":"numberofusers","ব্যবহারকারী_সংখ্যা":"numberofusers","ব্যবহারকারীসংখ্যা":"numberofusers","NUMBEROFUSERS":"numberofusers","সক্রিয়_ব্যবহারকারীর_সংখ্যা":"numberofactiveusers","সক্রিয়ব্যবহারকারীরসংখ্যা":"numberofactiveusers", +"সক্রিয়_ব্যবহারকারী_সংখ্যা":"numberofactiveusers","সক্রিয়ব্যবহারকারীসংখ্যা":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","পাতার_সংখ্যা":"numberofpages","পাতারসংখ্যা":"numberofpages","পৃষ্ঠার_সংখ্যা":"numberofpages","পৃষ্ঠারসংখ্যা":"numberofpages","পৃষ্ঠা_সংখ্যা":"numberofpages","পৃষ্ঠাসংখ্যা":"numberofpages","NUMBEROFPAGES":"numberofpages","প্রশাসকের_সংখ্যা":"numberofadmins","প্রশাসকেরসংখ্যা":"numberofadmins","প্রশাসক_সংখ্যা":"numberofadmins","প্রশাসকসংখ্যা":"numberofadmins","NUMBEROFADMINS":"numberofadmins","সম্পাদনার_সংখ্যা":"numberofedits", +"সম্পাদনারসংখ্যা":"numberofedits","সম্পাদনা_সংখ্যা":"numberofedits","সম্পাদনাসংখ্যা":"numberofedits","NUMBEROFEDITS":"numberofedits","প্রদর্শনের_শিরোনাম":"displaytitle","প্রদর্শনেরশিরোনাম":"displaytitle","প্রদর্শিত_শিরোনাম":"displaytitle","প্রদর্শিতশিরোনাম":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","চলতি_মাস":"currentmonth","চলতিমাস":"currentmonth","বর্তমান_মাস":"currentmonth","বর্তমানমাস":"currentmonth","বর্তমান_মাস_২":"currentmonth","বর্তমানমাস২":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","চলতি_মাস_১":"currentmonth1","চলতিমাস১":"currentmonth1", +"বর্তমান_মাস_১":"currentmonth1","বর্তমানমাস১":"currentmonth1","CURRENTMONTH1":"currentmonth1","বর্তমান_মাসের_নাম":"currentmonthname","বর্তমানমাসেরনাম":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","বর্তমান_মাসের_নাম_উৎপন্ন":"currentmonthnamegen","বর্তমানমাসেরনামউৎপন্ন":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","বর্তমান_মাস_সংক্ষেপ":"currentmonthabbrev","বর্তমানমাসসংক্ষেপ":"currentmonthabbrev","বর্তমান_মাস_সংক্ষিপ্ত":"currentmonthabbrev","বর্তমানমাসসংক্ষিপ্ত":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","বর্তমান_দিন":"currentday","বর্তমানদিন":"currentday", +"আজকের_দিন":"currentday","আজকেরদিন":"currentday","CURRENTDAY":"currentday","বর্তমান_দিন_২":"currentday2","বর্তমানদিন২":"currentday2","আজকের_দিন_২":"currentday2","আজকেরদিন২":"currentday2","CURRENTDAY2":"currentday2","বর্তমান_দিনের_নাম":"currentdayname","বর্তমানদিনেরনাম":"currentdayname","আজকের_দিনের_নাম":"currentdayname","আজকেরদিনেরনাম":"currentdayname","CURRENTDAYNAME":"currentdayname","চলতি_বছর":"currentyear","চলতিবছর":"currentyear","বর্তমান_বছর":"currentyear","বর্তমানবছর":"currentyear","CURRENTYEAR":"currentyear","বর্তমান_সময়":"currenttime","বর্তমানসময়":"currenttime","এখনকার_সময়":"currenttime","এখনকারসময়": +"currenttime","এখন_সময়":"currenttime","CURRENTTIME":"currenttime","বর্তমান_ঘণ্টা":"currenthour","বর্তমানঘণ্টা":"currenthour","বর্তমান_ঘন্টা":"currenthour","বর্তমানঘন্টা":"currenthour","এখনকার_ঘণ্টা":"currenthour","এখনকারঘণ্টা":"currenthour","CURRENTHOUR":"currenthour","স্থানীয়_মাস":"localmonth","স্থানীয়মাস":"localmonth","স্থানীয়_মাস_২":"localmonth","স্থানীয়মাস২":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","স্থানীয়_মাস_১":"localmonth1","স্থানীয়মাস১":"localmonth1","LOCALMONTH1":"localmonth1","স্থানীয়_মাসের_নাম":"localmonthname","স্থানীয়মাসেরনাম":"localmonthname","LOCALMONTHNAME":"localmonthname", +"স্থানীয়_মাসের_নাম_উৎপন্ন":"localmonthnamegen","স্থানীয়মাসেরনামউৎপন্ন":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","স্থানীয়_মাস_সংক্ষেপ":"localmonthabbrev","স্থানীয়মাসসংক্ষেপ":"localmonthabbrev","স্থানীয়_মাস_সংক্ষিপ্ত":"localmonthabbrev","স্থানীয়মাসসংক্ষিপ্ত":"localmonthabbrev","সংক্ষেপিত_স্থানীয়_মাস":"localmonthabbrev","সংক্ষেপিতস্থানীয়মাস":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","স্থানীয়_দিন":"localday","স্থানীয়দিন":"localday","স্থানীয়_বার":"localday","স্থানীয়বার":"localday","LOCALDAY":"localday","স্থানীয়_দিন_২": +"localday2","স্থানীয়দিন২":"localday2","LOCALDAY2":"localday2","স্থানীয়_দিনের_নাম":"localdayname","স্থানীয়দিনেরনাম":"localdayname","LOCALDAYNAME":"localdayname","স্থানীয়_বছর":"localyear","স্থানীয়বছর":"localyear","LOCALYEAR":"localyear","স্থানীয়_সময়":"localtime","স্থানীয়সময়":"localtime","LOCALTIME":"localtime","স্থানীয়_ঘণ্টা":"localhour","স্থানীয়ঘণ্টা":"localhour","স্থানীয়_ঘন্টা":"localhour","স্থানীয়ঘন্টা":"localhour","LOCALHOUR":"localhour","সাইটের_নাম":"sitename","সাইটেরনাম":"sitename","SITENAME":"sitename","বর্তমান_সপ্তাহ":"currentweek","বর্তমানসপ্তাহ":"currentweek","চলতি_সপ্তাহ": +"currentweek","চলতিসপ্তাহ":"currentweek","CURRENTWEEK":"currentweek","বর্তমান_সপ্তাহের_দিন":"currentdow","বর্তমানসপ্তাহেরদিন":"currentdow","CURRENTDOW":"currentdow","স্থানীয়_সপ্তাহ":"localweek","স্থানীয়সপ্তাহ":"localweek","LOCALWEEK":"localweek","স্থানীয়_সপ্তাহের_দিন":"localdow","স্থানীয়সপ্তাহেরদিন":"localdow","LOCALDOW":"localdow","সংশোধনের_আকার":"revisionsize","সংশোধনেরআকার":"revisionsize","REVISIONSIZE":"revisionsize","বর্তমান_সংস্করণ":"currentversion","বর্তমানসংস্করণ":"currentversion","CURRENTVERSION":"currentversion","বর্তমান_সময়তারিখ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp", +"স্থানীয়_সময়তারিখ":"localtimestamp","স্থানীয়সময়তারিখ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","বিষয়বস্তুর_ভাষা":"contentlanguage","বিষয়বস্তুরভাষা":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([\\x{0980}-\\x{09FF}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bo.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bo.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bpy.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bpy.json new file mode 100644 index 0000000..6823df9 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bpy.json @@ -0,0 +1,26 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__কোন_বিষয়বস্তুর_ছক_নয়__":"notoc","__কোনবিষয়বস্তুরছকনয়__":"notoc","__কোন_বিষয়বস্তুর_টেবিল_নয়__":"notoc","__কোনবিষয়বস্তুরটেবিলনয়__":"notoc","__notoc__": +"notoc","__কোনগ্যালারিনয়__":"nogallery","__কোনগ্যালারীনয়__":"nogallery","__কোন_গ্যালারি_নয়__":"nogallery","__কোন_গ্যালারী_নয়__":"nogallery","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__বিষয়বস্তুর_ছক__":"toc","__বিষয়বস্তুরছক__":"toc","__বিষয়বস্তুর_টেবিল__":"toc","__বিষয়বস্তুরটেবিল__":"toc","__toc__":"toc","__কোনসম্পাদনাঅনুচ্ছেদনয়__":"noeditsection","__কোন_সম্পাদনা_অনুচ্ছেদ_নয়__":"noeditsection","__noeditsection__":"noeditsection","__কোন_শিরোনাম_রূপান্তরকারী_নয়__":"notitleconvert","__কোনশিরোনামরূপান্তরকারীনয়__":"notitleconvert","__notitleconvert__": +"notitleconvert","__notc__":"notitleconvert","__কোন_বিষয়বস্তু_রূপান্তরকারী_নয়__":"nocontentconvert","__কোনবিষয়বস্তুরূপান্তরকারীনয়__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__নতুন_অনুচ্ছেদের_সংযোগ__":"newsectionlink","__নতুন_অনুচ্ছেদের_লিঙ্ক__":"newsectionlink","__নতুনঅনুচ্ছেদেরসংযোগ__":"newsectionlink","__নতুনঅনুচ্ছেদেরলিঙ্ক__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__কোন_নতুন_অনুচ্ছেদের_সংযোগ_নয়__":"nonewsectionlink","__কোন_নতুন_অনুচ্ছেদের_লিঙ্ক_নয়__":"nonewsectionlink","__কোননতুনঅনুচ্ছেদেরসংযোগনয়__": +"nonewsectionlink","__কোননতুনঅনুচ্ছেদেরলিঙ্কনয়__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__লুকানো_বিষয়শ্রেণী__":"hiddencat","__লুকানোবিষয়শ্রেণী__":"hiddencat","__লুকায়িতবিষয়শ্রেণী__":"hiddencat","__লুক্কায়িতবিষয়শ্রেণী__":"hiddencat","__লুক্কায়িত_বিষয়শ্রেণী__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__নির্ঘণ্ট__":"index","__INDEX__":"index","__কোননির্ঘণ্টনয়__":"noindex","__কোন_নির্ঘণ্ট_নয়__":"noindex","__নির্ঘণ্টনয়__":"noindex","__NOINDEX__":"noindex","__স্থির_পুনর্নির্দেশ__":"staticredirect", +"__স্থিরপুনর্নির্দেশ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","স্থানীয়_ইউআরএল":"localurl","স্থানীয়ইউআরএল":"localurl","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","নম্বর_বিন্যাস":"formatnum","নম্বরবিন্যাস":"formatnum","formatnum":"formatnum","ব্যাকরণ":"grammar","grammar":"grammar","লিঙ্গ":"gender","gender":"gender","বহুবচন":"plural","plural":"plural","বিআইডিআই":"bidi","bidi":"bidi","#ভাষা":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode": +"anchorencode","ফাইলের_পথ":"filepath","ফাইলেরপথ":"filepath","filepath":"filepath","পাতার_আইডি":"pageid","পাতারআইডি":"pageid","পৃষ্ঠার_আইডি":"pageid","পৃষ্ঠারআইডি":"pageid","pageid":"pageid","int":"int","#বিশেষ":"special","#special":"special","#speciale":"speciale","#ট্যাগ":"tag","#tag":"tag","#তারিখ_বিন্যাস":"formatdate","#তারিখবিন্যাস":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#বিষয়শ্রেণী_বৃক্ষ":"categorytree","#বিষয়শ্রেণীবৃক্ষ":"categorytree","#categorytree":"categorytree","#লক্ষ্য":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror", +"#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","নিবন্ধের_পথ":"articlepath","নিবন্ধেরপথ":"articlepath","articlepath":"articlepath","সার্ভার":"server","server":"server","সার্ভারের_নাম":"servername","সার্ভারেরনাম":"servername","servername":"servername","স্ক্রিপ্টের_পথ":"scriptpath","স্ক্রিপ্টেরপথ":"scriptpath","scriptpath":"scriptpath","শৈলীর_পথ":"stylepath","শৈলীরপথ":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"পাতার_সংখ্যা":"numberofpages", +"পাতারসংখ্যা":"numberofpages","পৃষ্ঠার_সংখ্যা":"numberofpages","পৃষ্ঠারসংখ্যা":"numberofpages","পৃষ্ঠা_সংখ্যা":"numberofpages","পৃষ্ঠাসংখ্যা":"numberofpages","NUMBEROFPAGES":"numberofpages","ব্যবহারকারীর_সংখ্যা":"numberofusers","ব্যবহারকারীরসংখ্যা":"numberofusers","ব্যবহারকারী_সংখ্যা":"numberofusers","ব্যবহারকারীসংখ্যা":"numberofusers","NUMBEROFUSERS":"numberofusers","সক্রিয়_ব্যবহারকারীর_সংখ্যা":"numberofactiveusers","সক্রিয়ব্যবহারকারীরসংখ্যা":"numberofactiveusers","সক্রিয়_ব্যবহারকারী_সংখ্যা":"numberofactiveusers", +"সক্রিয়ব্যবহারকারীসংখ্যা":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","নিবন্ধের_সংখ্যা":"numberofarticles","নিবন্ধেরসংখ্যা":"numberofarticles","নিবন্ধ_সংখ্যা":"numberofarticles","নিবন্ধসংখ্যা":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ফাইলের_সংখ্যা":"numberoffiles","ফাইলেরসংখ্যা":"numberoffiles","ফাইল_সংখ্যা":"numberoffiles","ফাইলসংখ্যা":"numberoffiles","NUMBEROFFILES":"numberoffiles","প্রশাসকের_সংখ্যা":"numberofadmins","প্রশাসকেরসংখ্যা":"numberofadmins","প্রশাসক_সংখ্যা":"numberofadmins","প্রশাসকসংখ্যা":"numberofadmins","NUMBEROFADMINS":"numberofadmins","দলে_সংখ্যা":"numberingroup", +"দলেসংখ্যা":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","সম্পাদনার_সংখ্যা":"numberofedits","সম্পাদনারসংখ্যা":"numberofedits","সম্পাদনা_সংখ্যা":"numberofedits","সম্পাদনাসংখ্যা":"numberofedits","NUMBEROFEDITS":"numberofedits","পূর্বনির্ধারিত_বাছাই":"defaultsort","পূর্বনির্ধারিতবাছাই":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","বিষয়শ্রেণীতেপাতা":"pagesincategory","বিষয়শ্রেণীতেপৃষ্ঠা":"pagesincategory","বিষয়শ্রেণীতে_পাতা":"pagesincategory","বিষয়শ্রেণীতে_পৃষ্ঠা":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT": +"pagesincategory","পাতার_আকার":"pagesize","পাতারআকার":"pagesize","পৃষ্ঠার_আকার":"pagesize","পৃষ্ঠারআকার":"pagesize","PAGESIZE":"pagesize","সুরক্ষার_স্তর":"protectionlevel","সুরক্ষারস্তর":"protectionlevel","সুরক্ষা_স্তর":"protectionlevel","সুরক্ষাস্তর":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","সুরক্ষার_মেয়াদোত্তীর্ণ":"protectionexpiry","সুরক্ষারমেয়াদোত্তীর্ণ":"protectionexpiry","সুরক্ষা_মেয়াদোত্তীর্ণ":"protectionexpiry","সুরক্ষামেয়াদোত্তীর্ণ":"protectionexpiry","PROTECTIONEXPIRY":"protectionexpiry","নামস্থানন":"namespacee","NAMESPACEE":"namespacee","নামস্থানের_সংখ্যা": +"namespacenumber","নামস্থানেরসংখ্যা":"namespacenumber","NAMESPACENUMBER":"namespacenumber","আলাপের_স্থান":"talkspace","আলোচনার_স্থান":"talkspace","আলাপেরস্থান":"talkspace","আলোচনারস্থান":"talkspace","আলাপের_জায়গা":"talkspace","আলাপেরজায়গা":"talkspace","TALKSPACE":"talkspace","আলাপের_স্থানন":"talkspacee","আলোচনার_স্থানন":"talkspacee","আলাপেরস্থানন":"talkspacee","আলোচনারস্থানন":"talkspacee","TALKSPACEE":"talkspacee","বিষয়ের_স্থান":"subjectspace","নিবন্ধের_স্থান":"subjectspace","বিষয়েরস্থান":"subjectspace","নিবন্ধেরস্থান":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace", +"বিষয়ের_স্থানন":"subjectspacee","নিবন্ধের_স্থানন":"subjectspacee","বিষয়েরস্থানন":"subjectspacee","নিবন্ধেরস্থানন":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","পাতার_নাম":"pagename","পাতারনাম":"pagename","পৃষ্ঠার_নাম":"pagename","পৃষ্ঠারনাম":"pagename","PAGENAME":"pagename","পাতার_নামম":"pagenamee","পাতারনামম":"pagenamee","পৃষ্ঠার_নামম":"pagenamee","পৃষ্ঠারনামম":"pagenamee","PAGENAMEE":"pagenamee","পূর্ণ_পাতার_নাম":"fullpagename","সম্পূর্ণ_পাতার_নাম":"fullpagename","পূর্ণপাতারনাম":"fullpagename","সম্পূর্ণপাতারনাম":"fullpagename","পূর্ণ_পৃষ্ঠার_নাম": +"fullpagename","সম্পূর্ণ_পৃষ্ঠার_নাম":"fullpagename","পূর্ণপৃষ্ঠারনাম":"fullpagename","সম্পূর্ণপৃষ্ঠারনাম":"fullpagename","FULLPAGENAME":"fullpagename","পূর্ণ_পাতার_নামম":"fullpagenamee","সম্পূর্ণ_পাতার_নামম":"fullpagenamee","পূর্ণপাতারনামম":"fullpagenamee","সম্পূর্ণপাতারনামম":"fullpagenamee","পূর্ণ_পৃষ্ঠার_নামম":"fullpagenamee","সম্পূর্ণ_পৃষ্ঠার_নামম":"fullpagenamee","পূর্ণপৃষ্ঠারনামম":"fullpagenamee","সম্পূর্ণপৃষ্ঠারনামম":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","মূল_পাতার_নাম":"rootpagename","মূলপাতারনাম":"rootpagename","মূল_পৃষ্ঠার_নাম": +"rootpagename","ROOTPAGENAME":"rootpagename","মূল_পাতার_নামম":"rootpagenamee","মূলপাতারনামম":"rootpagenamee","মূল_পৃষ্ঠার_নামম":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","ভিত্তি_পাতার_নাম":"basepagename","ভিত্তিপাতারনাম":"basepagename","ভিত্তি_পৃষ্ঠার_নাম":"basepagename","BASEPAGENAME":"basepagename","ভিত্তি_পাতার_নামম":"basepagenamee","ভিত্তিপাতারনামম":"basepagenamee","ভিত্তি_পৃষ্ঠার_নামম":"basepagenamee","BASEPAGENAMEE":"basepagenamee","উপপাতার_নাম":"subpagename","উপপাতারনাম":"subpagename","উপপৃষ্ঠার_নাম":"subpagename","উপপৃষ্ঠারনাম":"subpagename","SUBPAGENAME":"subpagename","উপপাতার_নামম":"subpagenamee", +"উপপাতারনামম":"subpagenamee","উপপৃষ্ঠার_নামম":"subpagenamee","উপপৃষ্ঠারনামম":"subpagenamee","SUBPAGENAMEE":"subpagenamee","আলাপ_পাতার_নাম":"talkpagename","আলাপপাতারনাম":"talkpagename","আলাপ_পৃষ্ঠার_নাম":"talkpagename","আলোচনা_পৃষ্ঠার_নাম":"talkpagename","আলোচনা_পাতার_নাম":"talkpagename","আলোচনাপাতারনাম":"talkpagename","TALKPAGENAME":"talkpagename","আলাপ_পাতার_নামম":"talkpagenamee","আলাপপাতারনামম":"talkpagenamee","আলাপ_পৃষ্ঠার_নামম":"talkpagenamee","আলোচনা_পৃষ্ঠার_নামম":"talkpagenamee","আলোচনা_পাতার_নামম":"talkpagenamee","আলোচনাপাতারনামম":"talkpagenamee","TALKPAGENAMEE": +"talkpagenamee","বিষয়ের_পাতার_নাম":"subjectpagename","বিষয়েরপাতারনাম":"subjectpagename","বিষয়ের_পৃষ্ঠার_নাম":"subjectpagename","বিষয়েরপৃষ্ঠারনাম":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","বিষয়ের_পাতার_নামম":"subjectpagenamee","বিষয়েরপাতারনামম":"subjectpagenamee","বিষয়ের_পৃষ্ঠার_নামম":"subjectpagenamee","বিষয়েরপৃষ্ঠারনামম":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","সংশোধনের_আইডি":"revisionid","সংশোধনেরআইডি":"revisionid","REVISIONID":"revisionid","সংশোধনের_দিন":"revisionday","সংশোধনেরদিন":"revisionday","REVISIONDAY":"revisionday", +"সংশোধনের_দিন_২":"revisionday2","সংশোধনেরদিন২":"revisionday2","REVISIONDAY2":"revisionday2","সংশোধনের_মাস":"revisionmonth","সংশোধনেরমাস":"revisionmonth","REVISIONMONTH":"revisionmonth","সংশোধনের_মাস_১":"revisionmonth1","সংশোধনেরমাস১":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","সংশোধনের_বছর":"revisionyear","সংশোধনেরবছর":"revisionyear","REVISIONYEAR":"revisionyear","সংশোধনের_সময়তারিখ":"revisiontimestamp","সংশোধনেরসময়তারিখ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","সংশোধনের_ব্যবহারকারী":"revisionuser","সংশোধনেরব্যবহারকারী":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","নামস্থান": +"namespace","NAMESPACE":"namespace","প্রদর্শনের_শিরোনাম":"displaytitle","প্রদর্শনেরশিরোনাম":"displaytitle","প্রদর্শিত_শিরোনাম":"displaytitle","প্রদর্শিতশিরোনাম":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","চলতি_মাস":"currentmonth","চলতিমাস":"currentmonth","বর্তমান_মাস":"currentmonth","বর্তমানমাস":"currentmonth","বর্তমান_মাস_২":"currentmonth","বর্তমানমাস২":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","চলতি_মাস_১":"currentmonth1","চলতিমাস১":"currentmonth1","বর্তমান_মাস_১":"currentmonth1","বর্তমানমাস১":"currentmonth1","CURRENTMONTH1":"currentmonth1","বর্তমান_মাসের_নাম":"currentmonthname", +"বর্তমানমাসেরনাম":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","বর্তমান_মাসের_নাম_উৎপন্ন":"currentmonthnamegen","বর্তমানমাসেরনামউৎপন্ন":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","বর্তমান_মাস_সংক্ষেপ":"currentmonthabbrev","বর্তমানমাসসংক্ষেপ":"currentmonthabbrev","বর্তমান_মাস_সংক্ষিপ্ত":"currentmonthabbrev","বর্তমানমাসসংক্ষিপ্ত":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","বর্তমান_দিন":"currentday","বর্তমানদিন":"currentday","আজকের_দিন":"currentday","আজকেরদিন":"currentday","CURRENTDAY":"currentday","বর্তমান_দিন_২":"currentday2","বর্তমানদিন২":"currentday2", +"আজকের_দিন_২":"currentday2","আজকেরদিন২":"currentday2","CURRENTDAY2":"currentday2","বর্তমান_দিনের_নাম":"currentdayname","বর্তমানদিনেরনাম":"currentdayname","আজকের_দিনের_নাম":"currentdayname","আজকেরদিনেরনাম":"currentdayname","CURRENTDAYNAME":"currentdayname","চলতি_বছর":"currentyear","চলতিবছর":"currentyear","বর্তমান_বছর":"currentyear","বর্তমানবছর":"currentyear","CURRENTYEAR":"currentyear","বর্তমান_সময়":"currenttime","বর্তমানসময়":"currenttime","এখনকার_সময়":"currenttime","এখনকারসময়":"currenttime","এখন_সময়":"currenttime","CURRENTTIME":"currenttime","বর্তমান_ঘণ্টা":"currenthour","বর্তমানঘণ্টা":"currenthour", +"বর্তমান_ঘন্টা":"currenthour","বর্তমানঘন্টা":"currenthour","এখনকার_ঘণ্টা":"currenthour","এখনকারঘণ্টা":"currenthour","CURRENTHOUR":"currenthour","স্থানীয়_মাস":"localmonth","স্থানীয়মাস":"localmonth","স্থানীয়_মাস_২":"localmonth","স্থানীয়মাস২":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","স্থানীয়_মাস_১":"localmonth1","স্থানীয়মাস১":"localmonth1","LOCALMONTH1":"localmonth1","স্থানীয়_মাসের_নাম":"localmonthname","স্থানীয়মাসেরনাম":"localmonthname","LOCALMONTHNAME":"localmonthname","স্থানীয়_মাসের_নাম_উৎপন্ন":"localmonthnamegen","স্থানীয়মাসেরনামউৎপন্ন":"localmonthnamegen", +"LOCALMONTHNAMEGEN":"localmonthnamegen","স্থানীয়_মাস_সংক্ষেপ":"localmonthabbrev","স্থানীয়মাসসংক্ষেপ":"localmonthabbrev","স্থানীয়_মাস_সংক্ষিপ্ত":"localmonthabbrev","স্থানীয়মাসসংক্ষিপ্ত":"localmonthabbrev","সংক্ষেপিত_স্থানীয়_মাস":"localmonthabbrev","সংক্ষেপিতস্থানীয়মাস":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","স্থানীয়_দিন":"localday","স্থানীয়দিন":"localday","স্থানীয়_বার":"localday","স্থানীয়বার":"localday","LOCALDAY":"localday","স্থানীয়_দিন_২":"localday2","স্থানীয়দিন২":"localday2","LOCALDAY2":"localday2","স্থানীয়_দিনের_নাম":"localdayname", +"স্থানীয়দিনেরনাম":"localdayname","LOCALDAYNAME":"localdayname","স্থানীয়_বছর":"localyear","স্থানীয়বছর":"localyear","LOCALYEAR":"localyear","স্থানীয়_সময়":"localtime","স্থানীয়সময়":"localtime","LOCALTIME":"localtime","স্থানীয়_ঘণ্টা":"localhour","স্থানীয়ঘণ্টা":"localhour","স্থানীয়_ঘন্টা":"localhour","স্থানীয়ঘন্টা":"localhour","LOCALHOUR":"localhour","সাইটের_নাম":"sitename","সাইটেরনাম":"sitename","SITENAME":"sitename","বর্তমান_সপ্তাহ":"currentweek","বর্তমানসপ্তাহ":"currentweek","চলতি_সপ্তাহ":"currentweek","চলতিসপ্তাহ":"currentweek","CURRENTWEEK":"currentweek","বর্তমান_সপ্তাহের_দিন":"currentdow", +"বর্তমানসপ্তাহেরদিন":"currentdow","CURRENTDOW":"currentdow","স্থানীয়_সপ্তাহ":"localweek","স্থানীয়সপ্তাহ":"localweek","LOCALWEEK":"localweek","স্থানীয়_সপ্তাহের_দিন":"localdow","স্থানীয়সপ্তাহেরদিন":"localdow","LOCALDOW":"localdow","সংশোধনের_আকার":"revisionsize","সংশোধনেরআকার":"revisionsize","REVISIONSIZE":"revisionsize","বর্তমান_সংস্করণ":"currentversion","বর্তমানসংস্করণ":"currentversion","CURRENTVERSION":"currentversion","বর্তমান_সময়তারিখ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","স্থানীয়_সময়তারিখ":"localtimestamp","স্থানীয়সময়তারিখ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK" +:"directionmark","DIRMARK":"directionmark","বিষয়বস্তুর_ভাষা":"contentlanguage","বিষয়বস্তুরভাষা":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([\\x{0980}-\\x{09FF}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-br.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-br.json new file mode 100644 index 0000000..2ef7a62 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-br.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","urlklok":"fullurl","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","yezhadur":"grammar","grammar":"grammar","jener":"gender","gender":"gender","liester":"plural","plural":"plural","bidi":"bidi","#yezh":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#dibar":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate" +:"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#galv":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#amzer":"time","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#gwezennadurrummad":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","servijer":"server","server":"server","anvservijer":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup", +"ALC'HWEZDIBAB":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","MENTPAJENN":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ANVPAJENN":"pagename","PAGENAME":"pagename","ANVPAJENNSK":"pagenamee","PAGENAMEE":"pagenamee","ANVPAJENNKLOK":"fullpagename","FULLPAGENAME":"fullpagename","ANVPAJENNKLOKSK":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ANVISPAJENN":"subpagename","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY": +"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESAOUENNANV":"namespace","NAMESPACE":"namespace","ESAOUENNANVSK":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NIVERABENNADOU":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NIVERARESTROU":"numberoffiles","NUMBEROFFILES":"numberoffiles","NIVERAIMPLIJERIEN":"numberofusers","NUMBEROFUSERS":"numberofusers","NIVERAIMPLIJERIENOBERIANT":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NIVERABAJENNOU":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NIVERAZEGASEDENNOU":"numberofedits", +"NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","ANVLEC'HIENN":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","STUMMRED": +"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^((?:c\\'h|C\\'H|C\\'h|c’h|C’H|C’h|[a-zA-ZàâçéèêîôûäëïöüùñÇÉÂÊÎÔÛÄËÏÖÜÀÈÙÑ])+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bs.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bs.json new file mode 100644 index 0000000..ffba136 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bs.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__bezsadržaja__":"notoc","__notoc__":"notoc","__bezgalerije__":"nogallery","__nogallery__":"nogallery","__forsirajsadržaj__":"forcetoc","__forsiranisadržaj__":"forcetoc","__forcetoc__":"forcetoc","__sadržaj__":"toc","__toc__":"toc","__bez_izmjena__":"noeditsection","__bezizmjena__":"noeditsection","__noeditsection__": +"noeditsection","__beztc__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__bezcc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LINKNOVOGODLOMKA__":"newsectionlink","__LINKNOVESEKCIJE__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__SKRIVENAKATEGORIJA__":"hiddencat","__SAKRIVENAKATEGORIJA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKSIRAJ__":"index","__INDEKSIRANJE__":"index","__SADRZAJ__":"index","__INDEX__":"index","__NEINDEKSIRAJ__":"noindex","__BEZINDEKSIRANJA__":"noindex","__BEZSADRZAJA__":"noindex","__NOINDEX__":"noindex","__STATIČNOPREUSMJERENJE__":"staticredirect","__STATISTICNOPREUSMJERENJE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}], +"functionSynonyms":[{"ip":"ns","ns":"ns","nse":"nse","dekodirajadresu":"urlencode","urlencode":"urlencode","lcprvi":"lcfirst","lcfirst":"lcfirst","ucprvi":"ucfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","lokalnaadresa":"localurl","localurl":"localurl","lokalneadrese":"localurle","localurle":"localurle","punurl":"fullurl","fullurl":"fullurl","punurle":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","numeričkiformat":"formatnum","numerickiformat":"formatnum","formatnum":"formatnum","gramatika":"grammar","grammar":"grammar","spol":"gender","pol":"gender","gender":"gender","množina":"plural","plural":"plural","bidi":"bidi","#jezik":"language","#language":"language","razmaklijevo":"padleft","jastuklijevo":"padleft","padleft":"padleft","razmakdesno":"padright","jastukdesno":"padright","padright":"padright","anchorencode":"anchorencode","putanjadodatoteke":"filepath","stazadatoteke":"filepath","filepath":"filepath","pageid":"pageid","int": +"int","#posebno":"special","#specijalno":"special","#special":"special","#speciale":"speciale","#oznaka":"tag","#tag":"tag","#formatdatuma":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","imeservera":"servername","servername":"servername","skripta":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis": +"numberofwikis","wbreponame":"wbreponame"},{"BROJUGRUPI":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","STRANICEUKATEGORIJI":"pagesincategory","STRANICEUKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","VELIČINASTRANICE":"pagesize","VELICINASTRANICE":"pagesize","PAGESIZE":"pagesize","NIVOZAŠTITE":"protectionlevel","NIVOZASTITE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","STRANICA":"pagename","PAGENAME":"pagename","STRANICE":"pagenamee","PAGENAMEE":"pagenamee","PUNOIMESTRANICE":"fullpagename","PUNOIMESTRANE":"fullpagename","FULLPAGENAME":"fullpagename","PUNOIMESTRANICEE":"fullpagenamee","PUNOIMESTRANEE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","IMEPODSTRANICE":"subpagename","SUBPAGENAME":"subpagename","IMENAPODSTRANICE":"subpagenamee","SUBPAGENAMEE":"subpagenamee", +"ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","IMEBAZNESTRANICE":"basepagename","BASEPAGENAME":"basepagename","IMENABAZNESTRANICE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","IMESTRANICERAZGOVORA":"talkpagename","TALKPAGENAME":"talkpagename","IMENASTRANICERAZGOVORA":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","IMESTRANICESUBKJEKTA":"subjectpagename","IMESTRANICECLANKA":"subjectpagename","IMESTRANICEČLANKA":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","IMENASTRANICESUBJEKTA":"subjectpagenamee","IMENASTRANICECLANAKA":"subjectpagenamee","IMENASTRANICEČLANAKA":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDIZMJENE":"revisionid","IDREVIZIJE":"revisionid","REVISIONID":"revisionid","IZMJENADAN":"revisionday","REVIZIJEDANA":"revisionday","REVISIONDAY":"revisionday","IZMJENADAN2":"revisionday2","REVIZIJEDANA2":"revisionday2","REVISIONDAY2":"revisionday2","IZMJENAMJESEC": +"revisionmonth","REVIZIJAMJESECA":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","IZMJENAGODINA":"revisionyear","REVIZIJAGODINE":"revisionyear","REVISIONYEAR":"revisionyear","IZMJENAVREMENSKIPEČAT":"revisiontimestamp","IZMJENAVREMENSKIPECAT":"revisiontimestamp","REVIZIJAVREMENSKOGPECATA":"revisiontimestamp","REVIZIJAVREMENSKOGPEČATA":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","IMENSKIPROSTOR":"namespace","NAMESPACE":"namespace","IMENSKIPROSTORI":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","PROSTORZARAZGOVOR":"talkspace","TALKSPACE":"talkspace","PROSTORIZARAZGOVOR":"talkspacee","TALKSPACEE":"talkspacee","PROSTORSUBJEKTA":"subjectspace","PROSTORCLANAKA":"subjectspace","PROSTORČLANAKA":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","PROSTORISUBJEKTA":"subjectspacee","PROSTORICLANKA":"subjectspacee", +"PROSTORIČLANKA":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","BROJČLANAKA":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","BROJDATOTEKA":"numberoffiles","BROJFAJLOVA":"numberoffiles","NUMBEROFFILES":"numberoffiles","BROJKORISNIKA":"numberofusers","NUMBEROFUSERS":"numberofusers","BROJAKTIVNIHKORISNIKA":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","BROJSTRANICA":"numberofpages","NUMBEROFPAGES":"numberofpages","BROJADMINISTRATORA":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BROJIZMJENA":"numberofedits","BROJPROMJENA":"numberofedits","NUMBEROFEDITS":"numberofedits","NASLOVZAPRIKAZ":"displaytitle","POKAŽINASLOV":"displaytitle","POKAZINASLOV":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","TRENUTNIMJESEC":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","TRENUTNIMJESEC1":"currentmonth1","CURRENTMONTH1":"currentmonth1","TRENUTNIMJESECIME":"currentmonthname","CURRENTMONTHNAME" +:"currentmonthname","TRENUTNIMJESECROD":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","TRENUTNIMJESECSKR":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","TRENUTNIDAN":"currentday","CURRENTDAY":"currentday","TRENUTNIDAN2":"currentday2","CURRENTDAY2":"currentday2","TRENUTNIDANIME":"currentdayname","CURRENTDAYNAME":"currentdayname","TRENUTNAGODINA":"currentyear","CURRENTYEAR":"currentyear","TRENUTNOVRIJEME":"currenttime","CURRENTTIME":"currenttime","TRENUTNISAT":"currenthour","CURRENTHOUR":"currenthour","LOKALNIMJESEC":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALNIMJESEC1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALNIMJESECIME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALNIMJESECIMEROD":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALNIMJESECSKR":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALNIDAN":"localday","LOCALDAY":"localday","LOKALNIDAN2":"localday2","LOCALDAY2": +"localday2","LOKALNIDANIME":"localdayname","LOCALDAYNAME":"localdayname","LOKALNAGODINA":"localyear","LOCALYEAR":"localyear","LOKALNOVRIJEME":"localtime","LOCALTIME":"localtime","LOKALNISAT":"localhour","LOCALHOUR":"localhour","IMESAJTA":"sitename","SITENAME":"sitename","TRENUTNASEDMICA":"currentweek","CURRENTWEEK":"currentweek","TRENUTNIDOV":"currentdow","CURRENTDOW":"currentdow","LOKALNASEDMICA":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","SADAŠNJAVERZIJA":"currentversion","SADASNJAVERZIJA":"currentversion","CURRENTVERSION":"currentversion","SADAŠNJIVREMENSKIPEČAT":"currenttimestamp","SADASNJIVREMENSKIPECAT":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALNIVREMENSKIPECAT":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zćčžšđž]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bug.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bug.json new file mode 100644 index 0000000..04dbf3d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bug.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa": +"language","#bhs":"language","#language":"language","isikiri":"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1":"timel","#timel":"timel","#hitung":"expr", +"#expr":"expr","#rel2abs":"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","HALAMANDIKATEGORI":"pagesincategory","HALDIKAT": +"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee", +"NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE":"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid","HARIREVISI":"revisionday","HAREV":"revisionday" +,"REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace","RUANGNAMAE":"namespacee","RUNAME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace","RUTAMA":"subjectspace","RUTIKEL":"subjectspace", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","JUMLAHARTIKEL":"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits","NUMBEROFEDITS":"numberofedits","JUDULTAMPILAN":"displaytitle" +,"JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME":"currentdayname","TAHUNKINI":"currentyear","TAKIN":"currentyear", +"CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN":"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname","LOCALDAYNAME":"localdayname","TAHUNLOKAL":"localyear","TALOK":"localyear", +"LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK":"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","BAHASAISI":"contentlanguage", +"BHSISI":"contentlanguage","BASI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bxr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bxr.json new file mode 100644 index 0000000..1cb65c6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-bxr.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender", +"множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist": +"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ": +"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY": +"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour", +"МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename", +"ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ca.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ca.json new file mode 100644 index 0000000..50952d1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ca.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__captaula__":"notoc","__notaula__":"notoc","__notoc__":"notoc","__capdetaula__":"notoc","__pascapdesomari__":"notoc","__pascapdetdm__":"notoc","__capgaleria__":"nogallery","__nogaleria__":"nogallery","__nogallery__":"nogallery","__capdegalariá__":"nogallery","__capdegalaria__":"nogallery","__pascapdedegalariá__": +"nogallery","__forçataula__":"forcetoc","__forcetoc__":"forcetoc","__forçartaula__":"forcetoc","__forçarsomari__":"forcetoc","__forçartdm__":"forcetoc","__taula__":"toc","__resum__":"toc","__tdm__":"toc","__toc__":"toc","__somari__":"toc","__secciónoeditable__":"noeditsection","__seccionoeditable__":"noeditsection","__noeditsection__":"noeditsection","__seccionnoneditabla__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIGAMSECCIONNOVÈLA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__PASCAPDELIGAMSECCIONNOVÈLA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATAMAGADA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__CAPINDEX__":"noindex","__NOINDEX__":"noindex","__PASCAPDINDÈX__":"noindex","__REDIRECCIÓESTATICA__":"staticredirect","__REDIRECCIOESTATICA__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__REDIRECCIONESTATICA__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","nse":"nse","encòdaurl":"urlencode","urlencode":"urlencode","initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocala":"localurl","localurl":"localurl","urllocalax":"localurle","localurle":"localurle","urlcompleta":"fullurl","fullurl":"fullurl","urlcompletax":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","gramatica":"grammar","grammar":"grammar","genre":"gender","gender":"gender","plural":"plural","bidi":"bidi","#idioma":"language","#llengua":"language","#language":"language","#lenga":"language","separacióesquerra":"padleft", +"separacioesquerra":"padleft","padleft":"padleft","borratgeesquèrra":"padleft","separaciódreta":"padright","separaciodreta":"padright","padright":"padright","borratgedrecha":"padright","encòdaancòra":"anchorencode","anchorencode":"anchorencode","camí":"filepath","cami":"filepath","filepath":"filepath","camin":"filepath","pageid":"pageid","int":"int","#especial":"special","#special":"special","#speciale":"speciale","#etiqueta":"tag","#marcador":"tag","#tag":"tag","#balisa":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","pagebanner":"PAGEBANNER","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h": +"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","servidor":"server","server":"server","nomservidor":"servername","servername":"servername","caminescript":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENA":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","CLAUDETRIADA":"defaultsort","PÀGINESENCATEGORIA":"pagesincategory","PAGINESENCATEGORIA":"pagesincategory","PAGINESENCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGINASDINSCAT":"pagesincategory","MIDAPÀGINA":"pagesize","MIDAPAGINA":"pagesize","MIDADELAPLANA":"pagesize","PAGESIZE":"pagesize","TALHAPAGINA":"pagesize","NIVELLPROTECCIÓ": +"protectionlevel","NIVELLPROTECCIO":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","NIVÈLDEPROTECCION":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPÀGINA":"pagename","NOMPAGINA":"pagename","NOMDELAPLANA":"pagename","PAGENAME":"pagename","NOMPAGINAX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGINACOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGINACOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMSOSPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMSOSPAGINAX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","NOMBASADEPAGINA":"basepagename","BASEPAGENAME":"basepagename","NOMBASADEPAGINAX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMPAGINADISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGINADISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGINASUBJECTE":"subjectpagename","NOMPAGINASUBJÈCTE":"subjectpagename","NOMPAGINAARTICLE": +"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGINASUBJECTEX":"subjectpagenamee","NOMPAGINASUBJÈCTEX":"subjectpagenamee","NOMPAGINAARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","NUMÈROVERSION":"revisionid","REVISIONID":"revisionid","DATAVERSION":"revisionday","REVISIONDAY":"revisionday","DATAVERSION2":"revisionday2","REVISIONDAY2":"revisionday2","MESREVISION":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ANNADAREVISION":"revisionyear","ANREVISION":"revisionyear","REVISIONYEAR":"revisionyear","ORAREVISION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACINOMENATGE":"namespace","NAMESPACE":"namespace","ESPACINOMENATGEX":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","ESPACIDISCUSSION":"talkspace","TALKSPACE":"talkspace", +"ESPACIDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACISUBJECTE":"subjectspace","ESPACISUBJÈCTE":"subjectspace","ESPACIARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACISUBJECTEX":"subjectspacee","ESPACISUBJÈCTEX":"subjectspacee","ESPACIARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREARTICLES":"numberofarticles","NOMBRED'ARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFITXERS":"numberoffiles","NOMBRED'ARXIUS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREFICHIÈRS":"numberoffiles","NOMBREUSUARIS":"numberofusers","NOMBRED'USUARIS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILIZAIRES":"numberofusers","NOMBREUTILIZAIRESACTIUS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREEDICIONS": +"numberofedits","NOMBRED'EDICIONS":"numberofedits","NUMBEROFEDITS":"numberofedits","NOMBREMODIFS":"numberofedits","TÍTOL":"displaytitle","TITOL":"displaytitle","DISPLAYTITLE":"displaytitle","AFICHARTÍTOL":"displaytitle","!":"!","=":"=","MESACTUAL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESCORRENT":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMMESACTUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMMESCORRENT":"currentmonthname","NOMGENMESACTUAL":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NOMGENMESCORRENT":"currentmonthnamegen","ABREVMESACTUAL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ABREVMESCORRENT":"currentmonthabbrev","DIAACTUAL":"currentday","CURRENTDAY":"currentday","JORNCORRENT":"currentday","JORNACTUAL":"currentday","DIAACTUAL2":"currentday2","CURRENTDAY2":"currentday2","JORNCORRENT2":"currentday2","JORNACTUAL2":"currentday2","NOMDIAACTUAL":"currentdayname", +"CURRENTDAYNAME":"currentdayname","NOMJORNCORRENT":"currentdayname","NOMJORNACTUAL":"currentdayname","ANYACTUAL":"currentyear","CURRENTYEAR":"currentyear","ANNADACORRENTA":"currentyear","ANNADAACTUALA":"currentyear","HORARICTUAL":"currenttime","CURRENTTIME":"currenttime","DATACORRENTA":"currenttime","DATAACTUALA":"currenttime","HORAACTUAL":"currenthour","CURRENTHOUR":"currenthour","ORACORRENTA":"currenthour","ORAACTUALA":"currenthour","MESLOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","NOMMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMESLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMESLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DIALOCAL":"localday","LOCALDAY":"localday","JORNLOCAL":"localday","DIALOCAL2":"localday2","LOCALDAY2":"localday2","JORNLOCAL2":"localday2","NOMDIALOCAL":"localdayname","LOCALDAYNAME":"localdayname","NOMJORNLOCAL":"localdayname","ANYLOCAL": +"localyear","LOCALYEAR":"localyear","ANNADALOCALA":"localyear","HORARILOCAL":"localtime","LOCALTIME":"localtime","ORARILOCAL":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","ORALOCALA":"localhour","NOMSIT":"sitename","NOMSITE_NOMSITI":"sitename","SITENAME":"sitename","SETMANACORRENTA":"currentweek","CURRENTWEEK":"currentweek","JDSCORRENT":"currentdow","CURRENTDOW":"currentdow","SETMANALOCALA":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIÓACTUAL":"currentversion","VERSIOACTUAL":"currentversion","CURRENTVERSION":"currentversion","VERSIONACTUALA":"currentversion","INSTANTACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARCADIRECCION":"directionmark","MARCADIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","IDIOMACONTINGUT":"contentlanguage","LLENGUACONTINGUT":"contentlanguage","CONTENTLANGUAGE": +"contentlanguage","CONTENTLANG":"contentlanguage","LENGACONTENGUT":"contentlanguage","LENGCONTENGUT":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^((?:[a-zàèéíòóúç·ïü]|'(?!'))+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cbk-zam.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cbk-zam.json new file mode 100644 index 0000000..968d03e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cbk-zam.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__sin_tdc__":"notoc","__notdc__":"notoc","__notoc__":"notoc","__sin_galería__":"nogallery","__nogalería__":"nogallery","__nogaleria__":"nogallery","__nogallery__":"nogallery","__forzar_tdc__":"forcetoc","__forzartdc__":"forcetoc","__forzartoc__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc","__toc__":"toc", +"__no_editar_sección__":"noeditsection","__noeditarsección__":"noeditsection","__noeditarseccion__":"noeditsection","__noeditsection__":"noeditsection","__noconvertirtitulo__":"notitleconvert","__noconvertirtítulo__":"notitleconvert","__noct___":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__noconvertircontenido__":"nocontentconvert","__nocc___":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__VINCULARANUEVASECCION__":"newsectionlink","__ENLACECREARSECCIÓN__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NOVINCULARANUEVASECCION__":"nonewsectionlink","__SINENLACECREARSECCIÓN__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATEGORÍAOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECCIÓNESTÁTICA__":"staticredirect", +"__REDIRECCIONESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIGUACION__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"en":"ns","ns":"ns","nse":"nse","codificarurl":"urlencode","urlencode":"urlencode","primerominus":"lcfirst","primerominús":"lcfirst","lcfirst":"lcfirst","primeromayus":"ucfirst","primeromayús":"ucfirst","ucfirst":"ucfirst","minus":"lc","minús":"lc","lc":"lc","mayus":"uc","mayús":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","urllocalc":"localurle","localurle":"localurle","urlcompleta":"fullurl","fullurl":"fullurl","urlcompletac":"fullurle","fullurle":"fullurle","urlcanonica":"canonicalurl","canonicalurl":"canonicalurl","urlcanonicac":"canonicalurle","canonicalurle":"canonicalurle","formatonúmero":"formatnum","formatonumero":"formatnum","formatnum":"formatnum","gramatica":"grammar","gramática":"grammar","grammar": +"grammar","género":"gender","genero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#idioma":"language","#language":"language","rellenarizquierda":"padleft","rellenarizq":"padleft","padleft":"padleft","rellenarderecha":"padright","rellenarder":"padright","padright":"padright","anchorencode":"anchorencode","rutaarchivo":"filepath","rutarchivo":"filepath","rutadearchivo":"filepath","filepath":"filepath","iddepágina":"pageid","idpágina":"pageid","iddepagina":"pageid","idpagina":"pageid","pageid":"pageid","int":"int","#especial":"special","#special":"special","#especialc":"speciale","#speciale":"speciale","#etiqueta":"tag","#tag":"tag","#formatodefecha":"formatdate","#formatearfecha":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#destino":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#invocar":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#siigual":"ifeq","#ifeq":"ifeq","#según":"switch","#switch" +:"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierror":"iferror","#iferror":"iferror","#tiempo":"time","#time":"time","#tiempol":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#árboldecategorías":"categorytree","#arboldecategorias":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","nointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propiedad":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","rutaartículo":"articlepath","rutaarticulo":"articlepath","articlepath":"articlepath","servidor":"server","server":"server","nombreservidor":"servername","servername":"servername","rutascript":"scriptpath","rutadescript":"scriptpath","scriptpath":"scriptpath","rutaestilo":"stylepath","rutadeestilo":"stylepath", +"stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NÚMEROENGRUPO":"numberingroup","NUMEROENGRUPO":"numberingroup","NUMENGRUPO":"numberingroup","NÚMENGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENAR":"defaultsort","CLAVEDEORDENPREDETERMINADO":"defaultsort","ORDENDECATEGORIAPREDETERMINADO":"defaultsort","ORDENDECATEGORÍAPREDETERMINADO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PÁGINASENCATEGORÍA":"pagesincategory","PÁGINASENCAT":"pagesincategory","PAGSENCAT":"pagesincategory","PAGINASENCATEGORIA":"pagesincategory","PAGINASENCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMAÑOPÁGINA":"pagesize","TAMAÑODEPÁGINA":"pagesize","TAMAÑOPAGINA":"pagesize","TAMAÑODEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECCIÓN":"protectionlevel","NIVELDEPROTECCION":"protectionlevel","PROTECTIONLEVEL" +:"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMBREDEPAGINA":"pagename","NOMBREDEPÁGINA":"pagename","PAGENAME":"pagename","NOMBREDEPAGINAC":"pagenamee","NOMBREDEPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMBRECOMPLETODEPÁGINA":"fullpagename","NOMBRECOMPLETODEPAGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMBRECOMPLETODEPAGINAC":"fullpagenamee","NOMBRECOMPLETODEPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMBREDESUBPAGINA":"subpagename","NOMBREDESUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMBREDESUBPAGINAC":"subpagenamee","NOMBREDESUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMBREDEPAGINARAIZ":"rootpagename","NOMBREDEPÁGINARAÍZ":"rootpagename","ROOTPAGENAME":"rootpagename","NOMBREDEPAGINARAIZC":"rootpagenamee","NOMBREDEPÁGINARAÍZC":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBREDEPAGINABASE":"basepagename","NOMBREDEPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMBREDEPAGINABASEC": +"basepagenamee","NOMBREDEPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMBREDEPÁGINADEDISCUSIÓN":"talkpagename","NOMBREDEPAGINADEDISCUSION":"talkpagename","NOMBREDEPAGINADISCUSION":"talkpagename","NOMBREDEPÁGINADISCUSIÓN":"talkpagename","TALKPAGENAME":"talkpagename","NOMBREDEPÁGINADEDISCUSIÓNC":"talkpagenamee","NOMBREDEPAGINADEDISCUSIONC":"talkpagenamee","NOMBREDEPAGINADISCUSIONC":"talkpagenamee","NOMBREDEPÁGINADISCUSIÓNC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMBREDEPAGINADETEMA":"subjectpagename","NOMBREDEPÁGINADETEMA":"subjectpagename","NOMBREDEPÁGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEARTICULO":"subjectpagename","NOMBREDEPÁGINADEARTÍCULO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMBREDEPAGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEASUNTOC": +"subjectpagenamee","NOMBREDEPAGINADEARTICULOC":"subjectpagenamee","NOMBREDEPÁGINADEARTÍCULOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDEREVISION":"revisionid","IDREVISION":"revisionid","IDDEREVISIÓN":"revisionid","IDREVISIÓN":"revisionid","REVISIONID":"revisionid","DIADEREVISION":"revisionday","DIAREVISION":"revisionday","DÍADEREVISIÓN":"revisionday","DÍAREVISIÓN":"revisionday","REVISIONDAY":"revisionday","DIADEREVISION2":"revisionday2","DIAREVISION2":"revisionday2","DÍADEREVISIÓN2":"revisionday2","DÍAREVISIÓN2":"revisionday2","REVISIONDAY2":"revisionday2","MESDEREVISION":"revisionmonth","MESDEREVISIÓN":"revisionmonth","MESREVISION":"revisionmonth","MESREVISIÓN":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDEREVISION1":"revisionmonth1","MESDEREVISIÓN1":"revisionmonth1","MESREVISION1":"revisionmonth1","MESREVISIÓN1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","AÑODEREVISION":"revisionyear", +"AÑODEREVISIÓN":"revisionyear","AÑOREVISION":"revisionyear","AÑOREVISIÓN":"revisionyear","REVISIONYEAR":"revisionyear","MARCADEHORADEREVISION":"revisiontimestamp","MARCADEHORADEREVISIÓN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODEREVISION":"revisionuser","USUARIODEREVISIÓN":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACIODENOMBRE":"namespace","NAMESPACE":"namespace","ESPACIODENOMBREC":"namespacee","NAMESPACEE":"namespacee","NÚMERODELESPACIO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACIODEDISCUSION":"talkspace","ESPACIODEDISCUSIÓN":"talkspace","TALKSPACE":"talkspace","ESPACIODEDISCUSIONC":"talkspacee","TALKSPACEE":"talkspacee","ESPACIODEASUNTO":"subjectspace","ESPACIODETEMA":"subjectspace","ESPACIODEARTÍCULO":"subjectspace","ESPACIODEARTICULO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACIODETEMAC":"subjectspacee","ESPACIODEASUNTOC":"subjectspacee", +"ESPACIODEARTICULOC":"subjectspacee","ESPACIODEARTÍCULOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NÚMERODEARTÍCULOS":"numberofarticles","NUMERODEARTICULOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NÚMERODEARCHIVOS":"numberoffiles","NUMERODEARCHIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NÚMERODEPÁGINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NÚMEROADMINIISTRADORES":"numberofadmins","NÚMEROADMINS":"numberofadmins","NUMEROADMINS":"numberofadmins","NUMEROADMINISTRADORES":"numberofadmins","NUMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINS":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINS": +"numberofadmins","NUMBEROFADMINS":"numberofadmins","NÚMERODEEDICIONES":"numberofedits","NUMERODEEDICIONES":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRARTÍTULO":"displaytitle","MOSTRARTITULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESACTUAL":"currentmonth","MESACTUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MESACTUALCOMPLETO":"currentmonthname","NOMBREMESACTUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MESACTUALGENITIVO":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESACTUALABREVIADO":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DÍAACTUAL":"currentday","DIAACTUAL":"currentday","DÍA_ACTUAL":"currentday","DIA_ACTUAL":"currentday","CURRENTDAY":"currentday","DÍAACTUAL2":"currentday2","DIAACTUAL2":"currentday2","DÍA_ACTUAL2":"currentday2","DIA_ACTUAL2":"currentday2","CURRENTDAY2":"currentday2", +"NOMBREDÍAACTUAL":"currentdayname","NOMBREDIAACTUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","AÑOACTUAL":"currentyear","AÑO_ACTUAL":"currentyear","CURRENTYEAR":"currentyear","HORA_MINUTOS_ACTUAL":"currenttime","HORAMINUTOSACTUAL":"currenttime","TIEMPOACTUAL":"currenttime","CURRENTTIME":"currenttime","HORAACTUAL":"currenthour","HORA_ACTUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","MESLOCALCOMPLETO":"localmonthname","NOMBREMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","MESLOCALGENITIVO":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREVIADO":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DÍALOCAL":"localday","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","DÍALOCAL2":"localday2","LOCALDAY2":"localday2","NOMBREDIALOCAL":"localdayname", +"NOMBREDÍALOCAL":"localdayname","LOCALDAYNAME":"localdayname","AÑOLOCAL":"localyear","LOCALYEAR":"localyear","HORAMINUTOSLOCAL":"localtime","TIEMPOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMBREDELSITIO":"sitename","SITENAME":"sitename","SEMANAACTUAL":"currentweek","CURRENTWEEK":"currentweek","DDSACTUAL":"currentdow","DIADESEMANAACTUAL":"currentdow","DÍADESEMANAACTUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DDSLOCAL":"localdow","DIADESEMANALOCAL":"localdow","DÍADESEMANALOCAL":"localdow","LOCALDOW":"localdow","TAMAÑODEREVISIÓN":"revisionsize","TAMAÑODEREVISION":"revisionsize","REVISIONSIZE":"revisionsize","VERSIONACTUAL":"currentversion","VERSIÓNACTUAL":"currentversion","CURRENTVERSION":"currentversion","MARCADEHORAACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MARCADEHORALOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark" +,"DIRMARK":"directionmark","IDIOMADELCONTENIDO":"contentlanguage","IDIOMADELCONT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záéíóúñ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cdo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cdo.json new file mode 100644 index 0000000..45f25bf --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cdo.json @@ -0,0 +1,9 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__無目錄__":"notoc","__无目录__":"notoc","__notoc__":"notoc","__無圖庫__":"nogallery","__无图库__":"nogallery","__nogallery__":"nogallery","__強制目錄__":"forcetoc","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__目錄__":"toc","__目录__":"toc","__toc__":"toc","__無段落編輯__": +"noeditsection","__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__不轉換標題__":"notitleconvert","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換內容__":"nocontentconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__隱藏分類__":"hiddencat","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__靜態重新導向__":"staticredirect","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"命名空間":"ns","名字空间":"ns","ns":"ns","命名空間e":"nse","名字空间e":"nse","nse":"nse", +"urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","性別":"gender","性":"gender","性别":"gender","gender":"gender","plural":"plural","bidi":"bidi","#語言":"language","#语言":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","頁面id":"pageid","页面id":"pageid","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#分類樹":"categorytree","#categorytree":"categorytree","#目標":"target","#target":"target","#巴別":"babel","#babel":"babel","#coordinates":"coordinates","#調動":"invoke","#invoke":"invoke","#related":"related","#若":"if", +"#if":"if","#ifeq":"ifeq","#轉換":"switch","#switch":"switch","#ifexist":"ifexist","#若表達式":"ifexpr","#ifexpr":"ifexpr","#如果錯誤":"iferror","#iferror":"iferror","#時間":"time","#time":"time","#時間l":"timel","#timel":"timel","#表達式":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","隱藏跨語言連結":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#屬性":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","伺服器":"server","服务器":"server","server":"server","伺服器名稱":"servername","服务器名":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wb報表名稱":"wbreponame","wbreponame":"wbreponame"},{"頁面數":"numberofpages","页面数":"numberofpages", +"NUMBEROFPAGES":"numberofpages","使用者人數量":"numberofusers","用户数":"numberofusers","NUMBEROFUSERS":"numberofusers","活躍使用者人數":"numberofactiveusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","文章數":"numberofarticles","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","檔案數":"numberoffiles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles","管理員數":"numberofadmins","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","命名空間數":"namespacenumber","名字空间编号":"namespacenumber", +"NAMESPACENUMBER":"namespacenumber","對話空間":"talkspace","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","頁面名稱":"pagename","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","根頁面名稱":"rootpagename","ROOTPAGENAME":"rootpagename","根頁面名稱E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY": +"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","修訂使用者":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","命名空間":"namespace","名字空间":"namespace","NAMESPACE":"namespace","顯示標題":"displaytitle","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月縮寫":"currentmonthabbrev","本月简称":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","目前時間":"currenttime","当前时间":"currenttime","此时": +"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","網站名稱":"sitename","站点名称":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","目前版本":"currentversion","当前版本":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","內容語言":"contentlanguage","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE": +"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ce.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ce.json new file mode 100644 index 0000000..39fc09f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ce.json @@ -0,0 +1,18 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__бац_чулацам__":"notoc","__бац_чул__":"notoc","__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__йац_уче__":"nogallery","__без_галереи__":"nogallery","__nogallery__":"nogallery","__тlедуьллу_чулацамбар__":"forcetoc", +"__тlедуьл_чул__":"forcetoc","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__чулацам__":"toc","__чул__":"toc","__оглавление__":"toc","__огл__":"toc","__toc__":"toc","__агӏо_та_ца_еш__":"noeditsection","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__хийцар_доцуш_коьрте__":"notitleconvert","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__йоза_хийцар_доцуш__":"nocontentconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ХЬАЖОРГ_ОЦ_КЕРЛАЧУ_ДЕКЪАН__":"newsectionlink","__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__": +"newsectionlink","__ЙОЦАШ_ХЬАЖОРГ_ОЦ_КЕРЛАЧУ_ДЕКЪАН__":"nonewsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__КЪАЙЛАХА_ЙОЛУ_КАТЕГОРИ__":"hiddencat","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__МЕТТИГТЕРАХЬ__":"index","__ИНДЕКС__":"index","__INDEX__":"index","__МЕТТИГТЕРАХЬ_ЙОЦАШ__":"noindex","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИСТИКИН_ДӀАСХЬАЖОРГ__":"staticredirect","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"бо":"ns","пи":"ns","ns":"ns","бохь":"nse","пик":"nse", +"nse":"nse","ишарйина_меттиг":"urlencode","закодированный_адрес":"urlencode","urlencode":"urlencode","хьалхар_элп_жима":"lcfirst","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","хьалхар_элп_доккха":"ucfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","кегийчу_элпашца":"lc","маленькими_буквами":"lc","lc":"lc","даккхийчу_элпашца":"uc","большими_буквами":"uc","uc":"uc","хӏоттаелла_меттиг":"localurl","локальный_адрес":"localurl","localurl":"localurl","хӏоттаелла_меттиг_2":"localurle","локальный_адрес_2":"localurle","localurle":"localurle","майарра_меттиг":"fullurl","полный_адрес":"fullurl","fullurl":"fullurl","майарра_меттиг_2":"fullurle","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl": +"canonicalurl","canonicalurle":"canonicalurle","терахьан_барамхlоттор":"formatnum","форматировать_число":"formatnum","formatnum":"formatnum","дожар":"grammar","падеж":"grammar","grammar":"grammar","ву_йу":"gender","gender":"gender","пол":"gender","дукхаллин_терахь":"plural","множественное_число":"plural","plural":"plural","bidi":"bidi","#мотт":"language","#язык":"language","#language":"language","йуза_харце":"padleft","заполнить_слева":"padleft","padleft":"padleft","йуза_бакъе":"padright","заполнить_справа":"padright","padright":"padright","ишарйар_меттиган":"anchorencode","кодировать_метку":"anchorencode","anchorencode":"anchorencode","файлан_тӏе_некъ":"filepath","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid", +"чуьра":"int","внутр":"int","int":"int","#белхан":"special","#гӏуллакхан":"special","#служебная":"special","#special":"special","#speciale":"speciale","#къастам":"tag","#къасто":"tag","#къаст":"tag","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#терахьибарам":"formatdate","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#хан":"time","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#категоридит":"categorytree" +,"#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","гlулкхдириг":"server","сервер":"server","server":"server","гlулкхдечуьна_цlе":"servername","название_сервера":"servername","servername":"servername","некъ_оц_меттакепа":"scriptpath","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","йоманхатӏ":"stylepath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ТЕРАХЬ_ОЦ_ТОБАНЦА":"numberingroup", +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ЛИСТАР_ЦАХЬЕХОР":"defaultsort","ДОГlА_ЛИСТАРАН":"defaultsort","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","КАТЕГОРИ_ЧОХЬ_АГӀОНАШ_":"pagesincategory","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","АГlОН_БАРАМ":"pagesize","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","ГӀАРОЛЛИ_БАРАМ":"protectionlevel","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","АГӀОН_ЦӀЕ":"pagename","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","АГӀОН_ЦӀЕ_2":"pagenamee", +"НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ЮЬЗЗИНА_АГӀОН_ЦӀЕ":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ЮЬЗЗИНА_АГӀОН_ЦӀЕ_2":"fullpagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","АГӀОН_КӀЕЛАРА_ЦӀЕ":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","АГӀОН_КӀЕЛАРА_ЦӀЕ_2":"subpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","АГӀОН_ЦӀЕРА_БУХ":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","АГӀОН_ЦӀЕРА_БУХ_2":"basepagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee", +"ДИЙЦАРЕ_АГӀОН_ЦӀЕ":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","ДИЙЦАРЕ_АГӀОН_ЦӀЕ_2":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","АГӀОН_ЯЗЗАМАН_ЦӀЕ":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","АГӀОН_ЯЗЗАМАН_ЦӀЕ_2":"subjectpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ЦУЬНА_БАШХО":"revisionid","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕ_БАШХО":"revisionday","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕ_БАШХО_2":"revisionday2","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2": +"revisionday2","БЕТТА_БАШХО":"revisionmonth","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ШО_БАШХО":"revisionyear","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","КЪАСТАМ_ХЕНА_БАШХО":"revisiontimestamp","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ДЕКЪАШХОН_БАШХО":"revisionuser","ВЕРСИЯ_УЧАСНИКА":"revisionuser","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ЦӀЕРИЙН_АНА":"namespace","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ЦӀЕРИЙН_АНА_2":"namespacee","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER": +"namespacenumber","ДИЙЦАРИЙН_АНА":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ДИЙЦАРИЙН_АНА_2":"talkspacee","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ЯЗЗАМИЙН_АНА":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ЯЗЗАМИЙН_АНА_2":"subjectspacee","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ЯЗЗАМАШИ_ДУКХАЛЛА":"numberofarticles","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ФАЙЛИЙН_ДУКХАЛЛА":"numberoffiles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","ДЕКЪАШХОЙ_ДУКХАЛЛА":"numberofusers","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers", +"NUMBEROFUSERS":"numberofusers","ДУКХАЛЛА_ЖИГАРА_ДЕКЪАШХОЙ":"numberofactiveusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","АГlОНИЙ_ДУКХАЛЛА":"numberofpages","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КУЬГАЛХОЙ_ДУКХАЛЛА":"numberofadmins","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","НИСДАРИЙН_ДУКХАЛЛА":"numberofedits","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ГАЙТА_КОЬРТАМОГl":"displaytitle","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","КАРАРА_БУТТ":"currentmonth","КАРАРА_БУТТ_2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH": +"currentmonth","CURRENTMONTH2":"currentmonth","КАРАРА_БУТТ_1":"currentmonth1","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","КАРАРАЧУ_БЕТТА_ЦӀЕ":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","КАРАРАЧУ_БЕТТА_ЦӀЕ_МУХ":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","КАРАРАЧУ_БЕТТА_ЦӀЕ_АБР":"currentmonthabbrev","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","КАРАРА_ДЕ":"currentday","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","КАРАРА_ДЕ_2":"currentday2","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","КАРАРАЧУ_ДЕ_ЦӀЕ":"currentdayname","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ": +"currentdayname","CURRENTDAYNAME":"currentdayname","КАРАРА_ШО":"currentyear","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","КАРАРА_ХАН":"currenttime","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","КАРАРА_САХЬТ":"currenthour","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","МЕТТИГАН_БУТТ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","МЕСТНЫЙ_МЕСЯЦ":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕТТИГАН_БУТТ_1":"localmonth1","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","МЕТТИГАН_БЕТТА_ЦlЕ":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","МЕТТИГАН_БЕТТА_ЦlЕ_МУХ":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen", +"МЕТТИГАН_БЕТТА_ЦlЕ_АБР":"localmonthabbrev","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕТТИГАН_ДЕ":"localday","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕТТИГАН_ДЕ_2":"localday2","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","МЕТТИГАН_ДЕ_ЦӀЕ":"localdayname","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕТТИГАН_ШО":"localyear","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕТТИГАН_ХАН":"localtime","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕТТИГАН_САХЬТ":"localhour","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","МЕТТИГ_ЦlЕ":"sitename","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename","КАРАРА_КӀИРА":"currentweek","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK": +"currentweek","КАРАРА_КӀИРАН_ДЕ":"currentdow","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕТТИГЕРА_КӀИРА":"localweek","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕТТИГАН_КӀИРАН_ДЕ":"localdow","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ЙОЛШЙОЛУ_БАШХО":"currentversion","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","КЪАСТАМ_ЙОЛУЧУ_ХАННА":"currenttimestamp","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","КЪАСТАМ_МЕТТИГА_ХАННА":"localtimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","ХЬАЖОЧЕ_ХААМ":"directionmark","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK": +"directionmark","МОТТ_ЧУЛАЦАМ":"contentlanguage","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюяӀ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ceb.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ceb.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ceb.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ch.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ch.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ch.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cho.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cho.json new file mode 100644 index 0000000..195d9f8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cho.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry" +,"PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE" +:"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname", +"LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-chr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-chr.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-chr.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-chy.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-chy.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-chy.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ckb.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ckb.json new file mode 100644 index 0000000..89f1926 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ckb.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","pendingchangelevel":"pendingchangelevel","#target":"target","#babel": +"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#ئەگەر":"if","#if":"if","#ifeq":"ifeq","#گۆڕینەوە":"switch","#switch":"switch","#ئەگەر_ھەبوو":"ifexist","#ifexist":"ifexist","#ئەگەر_دەربڕین":"ifexpr","#ifexpr":"ifexpr","#ئەگەر_ھەڵە":"iferror","#iferror":"iferror","#کات":"time","#time":"time","#timel":"timel","#دەربڕین":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP": +"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp", +"REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR": +"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([ئابپتجچحخدرڕزژسشعغفڤقکگلڵمنوۆهھەیێ‌]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-co.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-co.json new file mode 100644 index 0000000..03b7a4a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-co.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag", +"#formatodata":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#alberocategorie":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath": +"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2", +"CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME" +:"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NOMESITO":"sitename","SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cr.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cr.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-crh.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-crh.json new file mode 100644 index 0000000..bf174d9 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-crh.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zâçğıñöşüа-яёʺʹ“»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cs.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cs.json new file mode 100644 index 0000000..4e8246d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cs.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__bezobsahu__":"notoc","__notoc__":"notoc","__bezgalerie__":"nogallery","__nogallery__":"nogallery","__bezgalérie__":"nogallery","__vždyobsah__":"forcetoc","__forcetoc__":"forcetoc","__vynútiťobsah__":"forcetoc","__obsah__":"toc","__toc__":"toc","__bezeditovatčást__":"noeditsection","__noeditsection__":"noeditsection", +"__neupravovaťsekcie__":"noeditsection","__bezkonverzenadpisu__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__bezkonverzeobsahu__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LINKPŘIDATKOMENTÁŘ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__BEZLINKUPŘIDATKOMENTÁŘ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__SKRÝTKAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__SKRYTÁKATEGÓRIA__":"hiddencat","__SKRYTÁKAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXOVAT__":"index","__INDEX__":"index","__NEINDEXOVAT__":"noindex","__NOINDEX__":"noindex","__STATICKÉPŘESMĚROVÁNÍ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"jmennýprostor":"ns","ns":"ns","mp":"ns","jmennýprostore":"nse","nse":"nse","enkódovaturl":"urlencode","urlencode": +"urlencode","prvnímalé":"lcfirst","lcfirst":"lcfirst","prvnívelké":"ucfirst","ucfirst":"ucfirst","malá":"lc","lc":"lc","velká":"uc","uc":"uc","místníurl":"localurl","localurl":"localurl","místníurle":"localurle","localurle":"localurle","plnéurl":"fullurl","fullurl":"fullurl","plnéurle":"fullurle","fullurle":"fullurle","kanonickéurl":"canonicalurl","canonicalurl":"canonicalurl","kanonickéurle":"canonicalurle","canonicalurle":"canonicalurle","formátujčíslo":"formatnum","formatnum":"formatnum","skloňuj":"grammar","grammar":"grammar","gramatika":"grammar","pohlaví":"gender","gender":"gender","plurál":"plural","plural":"plural","obasměry":"bidi","bidi":"bidi","#jazyk":"language","#language":"language","zarovnatvlevo":"padleft","padleft":"padleft","zarovnatvpravo":"padright","padright":"padright","enkódovatnadpis":"anchorencode","anchorencode":"anchorencode","cestaksouboru":"filepath","filepath":"filepath","cestaksúboru":"filepath","idstránky":"pageid","pageid": +"pageid","hlášení":"int","int":"int","#speciální":"special","#special":"special","#speciale":"speciale","#značka":"tag","#tag":"tag","#formátujdatum":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#cíl":"target","#target":"target","#babylon":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#když":"if","#if":"if","#ifeq":"ifeq","#switch":"switch","#kdyžexist":"ifexist","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#čas":"time","#time":"time","#timel":"timel","#výraz":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#vlastnost":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cestakčlánku":"articlepath","articlepath": +"articlepath","server":"server","názevserveru":"servername","servername":"servername","názovservera":"servername","cestakeskriptům":"scriptpath","scriptpath":"scriptpath","cestakuskriptu":"scriptpath","cestakestylům":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"POČETSTRAN":"numberofpages","NUMBEROFPAGES":"numberofpages","POČETSTRÁNOK":"numberofpages","POČETUŽIVATELŮ":"numberofusers","NUMBEROFUSERS":"numberofusers","POČETPOUŽÍVATEĽOV":"numberofusers","POČETAKTIVNÍCHUŽIVATELŮ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","POČETČLÁNKŮ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","POČETČLÁNKOV":"numberofarticles","POČETSOUBORŮ":"numberoffiles","NUMBEROFFILES":"numberoffiles","POČETSÚBOROV":"numberoffiles","POČETSPRÁVCŮ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","POČETSPRÁVCOV":"numberofadmins","POČETVESKUPINĚ":"numberingroup","NUMBERINGROUP":"numberingroup", +"NUMINGROUP":"numberingroup","POČETEDITACÍ":"numberofedits","NUMBEROFEDITS":"numberofedits","POČETÚPRAV":"numberofedits","KLÍČŘAZENÍ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","STRÁNEKVKATEGORII":"pagesincategory","STRÁNEKVKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","STRÁNOKVKATEGÓRII":"pagesincategory","STRÁNOKVKAT":"pagesincategory","VELIKOSTSTRÁNKY":"pagesize","PAGESIZE":"pagesize","VEĽKOSŤSTRÁNKY":"pagesize","ÚROVEŇZAMČENÍ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","VYPRŠENÍZAMČENÍ":"protectionexpiry","PROTECTIONEXPIRY":"protectionexpiry","JMENNÝPROSTORE":"namespacee","NAMESPACEE":"namespacee","MENNÝPRIESTORE":"namespacee","ČÍSLOJMENNÉHOPROSTORU":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSNÍPROSTOR":"talkspace","TALKSPACE":"talkspace","DISKUSNÝPRIESTOR":"talkspace","DISKUSNÍPROSTORE":"talkspacee","TALKSPACEE" +:"talkspacee","DISKUSNÝPRIESTORE":"talkspacee","ČLÁNEKPROSTOR":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","PRIESTORČLÁNKOV":"subjectspace","ČLÁNEKPROSTORE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PRIESTORČLÁNKOVE":"subjectspacee","NÁZEVSTRANY":"pagename","PAGENAME":"pagename","NÁZOVSTRÁNKY":"pagename","NÁZEVSTRANYE":"pagenamee","PAGENAMEE":"pagenamee","NÁZOVSTRÁNKYE":"pagenamee","PLNÝNÁZEVSTRANY":"fullpagename","FULLPAGENAME":"fullpagename","PLNÝNÁZOVSTRÁNKY":"fullpagename","PLNÝNÁZEVSTRANYE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","PLNÝNÁZOVSTRÁNKYE":"fullpagenamee","NÁZEVKOŘENOVÉSTRANY":"rootpagename","ROOTPAGENAME":"rootpagename","NÁZEVKOŘENOVÉSTRANYE":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NÁZEVNADSTRANY":"basepagename","BASEPAGENAME":"basepagename","NÁZOVZÁKLADNEJSTRÁNKY":"basepagename","NÁZEVNADSTRANYE":"basepagenamee","BASEPAGENAMEE":"basepagenamee", +"NÁZOVZÁKLADNEJSTRÁNKYE":"basepagenamee","NÁZEVPODSTRANY":"subpagename","SUBPAGENAME":"subpagename","NÁZOVPODSTRÁNKY":"subpagename","NÁZEVPODSTRANYE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NÁZOVPODSTRÁNKYE":"subpagenamee","NÁZEVDISKUSE":"talkpagename","TALKPAGENAME":"talkpagename","NÁZOVDISKUSNEJSTRÁNKY":"talkpagename","NÁZEVDISKUSEE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NÁZOVDISKUSNEJSTRÁNKYE":"talkpagenamee","NÁZEVČLÁNKU":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NÁZOVČLÁNKU":"subjectpagename","NÁZEVČLÁNKUE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","NÁZOVČLÁNKUE":"subjectpagenamee","IDREVIZE":"revisionid","REVISIONID":"revisionid","DENREVIZE":"revisionday","REVISIONDAY":"revisionday","DENREVIZE2":"revisionday2","REVISIONDAY2":"revisionday2","MĚSÍCREVIZE":"revisionmonth","REVISIONMONTH":"revisionmonth","MĚSÍCREVIZE1":"revisionmonth1", +"REVISIONMONTH1":"revisionmonth1","ROKREVIZE":"revisionyear","REVISIONYEAR":"revisionyear","KÓDČASUREVIZE":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","AUTORREVIZE":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","JMENNÝPROSTOR":"namespace","NAMESPACE":"namespace","MENNÝPRIESTOR":"namespace","ZOBRAZOVANÝNADPIS":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","AKTUÁLNÍMĚSÍC":"currentmonth","AKTUÁLNÍMĚSÍC2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","AKTUÁLNYMESIAC":"currentmonth","AKTUÁLNÍMĚSÍC1":"currentmonth1","CURRENTMONTH1":"currentmonth1","AKTUÁLNÍMĚSÍCJMÉNO":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NÁZOVAKTUÁLNEHOMESIACA":"currentmonthname","AKTUÁLNÍMĚSÍCGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NÁZOVAKTUÁLNEHOMESIACAGEN":"currentmonthnamegen","AKTUÁLNÍMĚSÍCZKR":"currentmonthabbrev","CURRENTMONTHABBREV": +"currentmonthabbrev","NÁZOVAKTUÁLNEHOMESIACASKRATKA":"currentmonthabbrev","AKTUÁLNÍDEN":"currentday","CURRENTDAY":"currentday","AKTUÁLNYDEŇ":"currentday","AKTUÁLNÍDEN2":"currentday2","CURRENTDAY2":"currentday2","AKTUÁLNYDEŇ2":"currentday2","AKTUÁLNÍDENJMÉNO":"currentdayname","CURRENTDAYNAME":"currentdayname","NÁZOVAKTUÁLNEHODŇA":"currentdayname","AKTUÁLNÍROK":"currentyear","CURRENTYEAR":"currentyear","AKTUÁLNYROK":"currentyear","AKTUÁLNÍČAS":"currenttime","CURRENTTIME":"currenttime","AKTUÁLNYČAS":"currenttime","AKTUÁLNÍHODINA":"currenthour","CURRENTHOUR":"currenthour","AKTUÁLNAHODINA":"currenthour","MÍSTNÍMĚSÍC":"localmonth","MÍSTNÍMĚSÍC2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MÍSTNÍMĚSÍC1":"localmonth1","LOCALMONTH1":"localmonth1","MÍSTNÍMĚSÍCJMÉNO":"localmonthname","LOCALMONTHNAME":"localmonthname","MÍSTNÍMĚSÍCGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MÍSTNÍMĚSÍCZKR": +"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","MÍSTNÍDEN":"localday","LOCALDAY":"localday","MÍSTNÍDEN2":"localday2","LOCALDAY2":"localday2","MÍSTNÍDENJMÉNO":"localdayname","LOCALDAYNAME":"localdayname","MÍSTNÍROK":"localyear","LOCALYEAR":"localyear","MÍSTNÍČAS":"localtime","LOCALTIME":"localtime","MÍSTNÍHODINA":"localhour","LOCALHOUR":"localhour","NÁZEVWEBU":"sitename","SITENAME":"sitename","NÁZOVLOKALITY":"sitename","AKTUÁLNÍTÝDEN":"currentweek","CURRENTWEEK":"currentweek","AKTUÁLNYTÝŽDEŇ":"currentweek","AKTUÁLNÍDENTÝDNE":"currentdow","CURRENTDOW":"currentdow","MÍSTNÍTÝDEN":"localweek","LOCALWEEK":"localweek","MÍSTNÍDENTÝDNE":"localdow","LOCALDOW":"localdow","VELIKOSTREVIZE":"revisionsize","REVISIONSIZE":"revisionsize","VERZESOFTWARE":"currentversion","CURRENTVERSION":"currentversion","AKTUÁLNAVERZIA":"currentversion","AKTUÁLNÍKÓDČASU":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MÍSTNÍKÓDČASU":"localtimestamp", +"LOCALTIMESTAMP":"localtimestamp","ZNAKSMĚRU":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","JAZYKOBSAHU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","JAZYKSTRÁNKY":"pagelanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záčďéěíňóřšťúůýž]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-csb.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-csb.json new file mode 100644 index 0000000..afcc8b0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-csb.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__bezspisu__":"notoc","__notoc__":"notoc","__bezgalerii__":"nogallery","__nogallery__":"nogallery","__zespisem__":"forcetoc","__wymuśspis__":"forcetoc","__forcetoc__":"forcetoc","__spis__":"toc","__toc__":"toc","__bezedycjisekcji__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert", +"__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LINKNOWEJSEKCJI__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORIAUKRYTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKSUJ__":"index","__INDEX__":"index","__NIEINDEKSUJ__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"pn":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","zmałej":"lcfirst","odmałej":"lcfirst","lcfirst":"lcfirst","zwielkiej":"ucfirst","zdużej":"ucfirst","odwielkiej":"ucfirst","oddużej":"ucfirst","ucfirst":"ucfirst","małe":"lc","lc":"lc","wielkie":"uc","duże":"uc","uc":"uc","localurl":"localurl","localurle":"localurle","pełnyurl":"fullurl","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle": +"canonicalurle","formatnum":"formatnum","odmiana":"grammar","grammar":"grammar","płeć":"gender","gender":"gender","mnoga":"plural","plural":"plural","bidi":"bidi","#język":"language","#language":"language","dolewej":"padleft","padleft":"padleft","doprawej":"padright","padright":"padright","anchorencode":"anchorencode","ścieżkapliku":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#specjalna":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#drzewokategorii":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h" +:"lsth","noexternallanglinks":"noexternallanglinks","#właściwość":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","ścieżkaartykułów":"articlepath","articlepath":"articlepath","serwer":"server","server":"server","nazwaserwera":"servername","servername":"servername","ścieżkaskryptu":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"STRON":"numberofpages","NUMBEROFPAGES":"numberofpages","UŻYTKOWNIKÓW":"numberofusers","NUMBEROFUSERS":"numberofusers","LICZBAAKTYWNYCHUŻYTKOWNIKÓW":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ARTYKUŁÓW":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","PLIKÓW":"numberoffiles","NUMBEROFFILES":"numberoffiles","ADMINISTRATORÓW":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","EDYCJI":"numberofedits","NUMBEROFEDITS": +"numberofedits","SORTUJ":"defaultsort","DOMYŚLNIESORTUJ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","STRONYWKATEGORII":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ROZMIARSTRONY":"pagesize","PAGESIZE":"pagesize","__POZIOMZABEZPIECZEŃ__":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","DYSKUSJA":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NAZWASTRONY":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","PELNANAZWASTRONY":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BAZOWANAZWASTRONY":"basepagename","BASEPAGENAME":"basepagename", +"BASEPAGENAMEE":"basepagenamee","NAZWAPODSTRONY":"subpagename","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","NAZWASTRONYDYSKUSJI":"talkpagename","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAZWAPRZESTRZENI":"namespace","NAMESPACE":"namespace","WYŚWIETLANYTYTUŁ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV": +"currentmonthabbrev","AKTUALNYDZIEŃ":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","NAZWADNIA":"currentdayname","CURRENTDAYNAME":"currentdayname","AKTUALNYROK":"currentyear","CURRENTYEAR":"currentyear","AKTUALNYCZAS":"currenttime","CURRENTTIME":"currenttime","AKTUALNAGODZINA":"currenthour","CURRENTHOUR":"currenthour","MIESIĄC":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","MIESIĄCNAZWA":"localmonthname","LOCALMONTHNAME":"localmonthname","MIESIĄCNAZWAD":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MIESIĄCNAZWASKR":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DZIEŃ":"localday","LOCALDAY":"localday","DZIEŃ2":"localday2","LOCALDAY2":"localday2","DZIEŃTYGODNIA":"localdayname","LOCALDAYNAME":"localdayname","ROK":"localyear","LOCALYEAR":"localyear","CZAS":"localtime","LOCALTIME":"localtime","GODZINA":"localhour","LOCALHOUR":"localhour","PROJEKT":"sitename","SITENAME":"sitename", +"AKTUALNYTYDZIEŃ":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","TYDZIEŃROKU":"localweek","LOCALWEEK":"localweek","DZIEŃTYGODNIANR":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","AKTUALNAWERSJA":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zęóąśłżźćńĘÓĄŚŁŻŹĆŃ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cu.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cu.json new file mode 100644 index 0000000..88c5800 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cu.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#ѩꙁꙑкъ":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel", +"#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize", +"PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev", +"LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters": +"/^([a-zабвгдеєжѕзїіıићклмнопсстѹфхѡѿцчшщъыьѣюѥѧѩѫѭѯѱѳѷѵґѓђёјйљњќуўџэ҄я“»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cv.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cv.json new file mode 100644 index 0000000..89dbc1d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cv.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender", +"множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#вавилон":"babel","#coordinates":"coordinates","#invoke":"invoke","#вызвать":"invoke","#related":"related","#if":"if","#если":"if","#ifeq":"ifeq","#switch":"switch","#переключатель":"switch","#ifexist": +"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#еслиошибка":"iferror","#time":"time","#время":"time","#timel":"timel","#мвремя":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ": +"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY": +"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour", +"МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename", +"ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zа-яĕçăӳ\"»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cy.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cy.json new file mode 100644 index 0000000..ca7b7d1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-cy.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__dimtaflencynnwys__":"notoc","__dimrhestrgynnwys__":"notoc","__dimrhg__":"notoc","__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__dimadrangolygu__":"noeditsection","__dimgolyguadran__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert", +"__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"_NEWSECTIONLINK_":"newsectionlink","_CYSWLLTADRANNEWYDD_":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","_HIDDENCAT_":"hiddencat","_CATCUDD_":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","urlllawn":"fullurl","fullurl":"fullurl","urlllawne":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","fformatiorhif":"formatnum","formatnum":"formatnum","grammar":"grammar","gramadeg":"grammar", +"gender":"gender","lluosog":"plural","plural":"plural","bidi":"bidi","#iaith":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#arbennig":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","gweinydd" +:"server","server":"server","enw'rgweinydd":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","TUDALENNAUYNYCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","MAINTTUD":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ENWTUDALEN":"pagename","PAGENAME":"pagename","ENWTUDALENE":"pagenamee","PAGENAMEE":"pagenamee","ENWLLAWNTUDALEN":"fullpagename","FULLPAGENAME":"fullpagename","ENWLLAWNTUDALENE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ENWISDUDALEN":"subpagename","SUBPAGENAME":"subpagename","ENWISDUDALENE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME": +"basepagename","BASEPAGENAMEE":"basepagenamee","ENWTUDALENSGWRS":"talkpagename","TALKPAGENAME":"talkpagename","ENWTUDALENSGWRSE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDYGOLYGIAD":"revisionid","REVISIONID":"revisionid","DIWRNODYGOLYGIAD":"revisionday","REVISIONDAY":"revisionday","DIWRNODYGOLYGIAD2":"revisionday2","REVISIONDAY2":"revisionday2","MISYGOLYGIAD":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","BLWYDDYNYGOLYGIAD":"revisionyear","REVISIONYEAR":"revisionyear","STAMPAMSERYGOLYGIAD":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","PARTH":"namespace","NAMESPACE":"namespacee","PARTHE":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NIFEROERTHYGLAU":"numberofarticles","NIFERYRERTHYGLAU":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NIFERYFFEILIAU":"numberoffiles","NUMBEROFFILES":"numberoffiles","NIFERYDEFNYDDWYR":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NIFERYGWEINYDDWYR":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NIFERYGOLYGIADAU":"numberofedits","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MISCYFOES":"currentmonth","MISCYFREDOL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","ENWMISCYFOES":"currentmonthname","ENWMISCYFREDOL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","GENENWMISCYFOES":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV": +"currentmonthabbrev","DYDDIADCYFOES":"currentday","DYDDCYFREDOL":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","ENWDYDDCYFOES":"currentdayname","ENWDYDDCYFREDOL":"currentdayname","CURRENTDAYNAME":"currentdayname","FLWYDDYNCYFOES":"currentyear","BLWYDDYNGYFREDOL":"currentyear","CURRENTYEAR":"currentyear","AMSERCYFOES":"currenttime","AMSERCYFREDOL":"currenttime","CURRENTTIME":"currenttime","AWRGYFREDOL":"currenthour","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","WYTHNOSGYFREDOL":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize", +"GOLYGIADCYFREDOL":"currentversion","CURRENTVERSION":"currentversion","STAMPAMSERCYFREDOL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STAMPAMSERLLEOL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","IAITHYCYNNWYS":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([àáâèéêìíîïòóôûŵŷa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-da.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-da.json new file mode 100644 index 0000000..a9bca0b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-da.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zæøå]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dag.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dag.json new file mode 100644 index 0000000..378cc57 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dag.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([ɛɣŋɔʒƐƔŊƆƷa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-de.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-de.json new file mode 100644 index 0000000..21c0a85 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-de.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"abschnitt":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__":"forcetoc","__inhaltsverzeichnis__":"toc","__toc__":"toc", +"__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__":"noindex","__NOINDEX__":"noindex","__PERMANENTE_WEITERLEITUNG__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","lokale_url":"localurl","localurl":"localurl","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zahlenformat":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#sprache":"language","#language":"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright": +"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorienbaum":"categorytree","#kategoriebaum":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx", +"#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE":"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SEITENNAME":"pagename","PAGENAME":"pagename","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSSIONSSEITE":"talkpagename","DISK":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","HAUPTSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR":"revisionyear","VERSIONSJAHR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp", +"REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","NAMENSRAUM":"namespace","NAMESPACE":"namespace","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DATEIANZAHL":"numberoffiles","NUMBEROFFILES":"numberoffiles","BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTIVE_BENUTZER": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JETZIGER_MONATSNAME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JETZIGER_KALENDERTAG":"currentday","JETZIGER_TAG":"currentday","CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2","JETZIGER_TAG_2": +"currentday2","CURRENTDAY2":"currentday2","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV":"localmonthnamegen","LOKALER_MONATSNAME_GEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2","LOKALER_TAG_2":"localday2","LOCALDAY2":"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME":"localdayname","LOKALES_JAHR" +:"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize","JETZIGE_VERSION":"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHALTSSPRACHE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE": +"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöüßa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-din.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-din.json new file mode 100644 index 0000000..d4b9394 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-din.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äëɛɛ̈éɣïŋöɔɔ̈óa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-diq.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-diq.json new file mode 100644 index 0000000..783f721 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-diq.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__estençino__":"notoc","__notoc__":"notoc","__galeri̇çino__":"nogallery","__nogallery__":"nogallery","__estenzaruret__":"forcetoc","__forcetoc__":"forcetoc","__esten__":"toc","__toc__":"toc","__ti̇markerdişçino__":"noeditsection","__noeditsection__":"noeditsection","__sernamevurnayişçino__":"notitleconvert", +"__svç__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__zerrevurnayişçino__":"nocontentconvert","__zvç__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__GREYÉSERNAMEDÉNEWİ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__GREYÉSERNAMEDÉNEWİÇINO__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORİYANIMITİ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__SERSIQ__":"index","__INDEX__":"index","__SERSIQÇINYO__":"noindex","__NOINDEX__":"noindex","__STATİKHETENAYIŞ__":"staticredirect","__STATICHETENAYIŞ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"cn":"ns","ns":"ns","cnv":"nse","nse":"nse","urlencode":"urlencode","khi̇lk":"lcfirst","lcfirst":"lcfirst", +"bhi̇lk":"ucfirst","ucfirst":"ucfirst","kh":"lc","lc":"lc","bh":"uc","uc":"uc","lokalgre":"localurl","localurl":"localurl","lokalgrev":"localurle","localurle":"localurle","greheme":"fullurl","fullurl":"fullurl","greyheme":"fullurle","fullurle":"fullurle","greyékanoni̇k":"canonicalurl","canonicalurl":"canonicalurl","greyokanoni̇k":"canonicalurle","canonicalurle":"canonicalurle","babetnayiş":"formatnum","formatnum":"formatnum","gramer":"grammar","grammar":"grammar","ci̇nsi̇yet":"gender","gender":"gender","zafen":"plural","plural":"plural","bidi":"bidi","#ziwan":"language","#language":"language","çepi̇pirk":"padleft","padleft":"padleft","raşti̇pirk":"padright","padright":"padright","anchorencode":"anchorencode","rayadosya":"filepath","filepath":"filepath","nimreyperer":"pageid","pageid":"pageid","i̇nt":"int","int":"int","#bağse":"special","#special":"special","#bağsiye":"speciale","#speciale":"speciale","#etiket":"tag","#tag":"tag","#deméformati":"formatdate","#formatdate": +"formatdate","#dateformat":"formatdate","#hedef":"target","#target":"target","#babil":"babel","#babel":"babel","#coordinates":"coordinates","#veynden":"invoke","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#sek":"ifeq","#ifeq":"ifeq","#rayek":"switch","#switch":"switch","#ifexist":"ifexist","#ifadeyose":"ifexpr","#ifexpr":"ifexpr","#çınyose":"iferror","#iferror":"iferror","#zeman":"time","#time":"time","#timel":"timel","#ifade":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#darakategori":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","tebergreyzuwaniçıniyo":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#nitelıg":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","sopaperan":"articlepath","articlepath":"articlepath","arden":"server","server":"server", +"nameyarden":"servername","servername":"servername","rayascripti̇":"scriptpath","scriptpath":"scriptpath","terzétewri̇":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"AMARİYAGRUBER":"numberingroup","AMARGRUB":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","RATNAYIŞOHESBNAYIŞ":"defaultsort","SIRMEYRATNAYIŞOHESBNAYIŞ":"defaultsort","KATEGORİYARATNAYIŞOHESBNAYIŞ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PERÉKKATEGORİDEYÉ":"pagesincategory","PERKATMİYAN":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","EBATAPERER":"pagesize","PAGESIZE":"pagesize","SEWİYEYASTARAN":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMEYPELA":"pagename","PAGENAME":"pagename","NAMEYPELAA":"pagenamee","PAGENAMEE":"pagenamee","NAMEYPERERPÉRO":"fullpagename", +"FULLPAGENAME":"fullpagename","NAMEYPERERPÉRON":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NAMEYBINPERER":"subpagename","SUBPAGENAME":"subpagename","NAMEYBINPERERAN":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NAMEYRÉÇERDAPERER":"rootpagename","ROOTPAGENAME":"rootpagename","NAMEYRÉÇERDAPERAN":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NAMEYESASPERER":"basepagename","BASEPAGENAME":"basepagename","NAMEYESASPERAN":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMEYPERAVATENAYIŞİ":"talkpagename","TALKPAGENAME":"talkpagename","NAMEYPERAVATENAYIŞAN":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMEYPERDAMESEL":"subjectpagename","NAMEYPERDAWESİQE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMEYPERDAMESELER":"subjectpagenamee","NAMEYPERDAWESİQER":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","NIMREYREVİZYONİ":"revisionid","REVISIONID":"revisionid","ROCAREVİZYONİ" +:"revisionday","REVISIONDAY":"revisionday","ROCAREVİZYON2":"revisionday2","REVISIONDAY2":"revisionday2","AŞMAREVİZYONİ":"revisionmonth","REVISIONMONTH":"revisionmonth","AŞMAREVİZYONİ1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","SERRAREVİZYONİ":"revisionyear","REVISIONYEAR":"revisionyear","MALUMATAREVİZYONDADEMİ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVİZYONAKARBERİ":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","CANAME":"namespace","NAMESPACE":"namespace","CANAMEE":"namespacee","NAMESPACEE":"namespacee","AMARİYACANAME":"namespacenumber","NAMESPACENUMBER":"namespacenumber","CAYÉVATENAYIŞİ":"talkspace","TALKSPACE":"talkspace","CAYÉVATENAYIŞAN":"talkspacee","TALKSPACEE":"talkspacee","CAYÉMESEL":"subjectspace","CAYÉWESİQE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","CAYÉMESELAN":"subjectspacee","CAYÉWESİQAN":"subjectspacee","SUBJECTSPACEE":"subjectspacee", +"ARTICLESPACEE":"subjectspacee","AMARİYAWESİQAN":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","AMARİYADOSYAYAN":"numberoffiles","NUMBEROFFILES":"numberoffiles","AMARİYAKARBERAN":"numberofusers","NUMBEROFUSERS":"numberofusers","AMARİYAAKTİVKARBERAN":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","AMARİYAPELAN":"numberofpages","NUMBEROFPAGES":"numberofpages","AMARİYAADMİNAN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","AMARİYAVURNAYIŞAN":"numberofedits","NUMBEROFEDITS":"numberofedits","SERNAMİBASNI":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","AŞMİYANEWKİ":"currentmonth","MEWCUDAŞMİ2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","AŞMİYANEWKİ1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMEYAŞMDANEWKİ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","AŞMACIYANEWKİ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","AŞMİYANEWKİKILMKERDIŞ": +"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ROCENEWKİ":"currentday","CURRENTDAY":"currentday","ROCENEWKİ2":"currentday2","CURRENTDAY2":"currentday2","NAMEYÉROCENEWKİ":"currentdayname","CURRENTDAYNAME":"currentdayname","SERRENEWKİ":"currentyear","CURRENTYEAR":"currentyear","DEMENEWKİ":"currenttime","CURRENTTIME":"currenttime","SEHATNEWKİ":"currenthour","CURRENTHOUR":"currenthour","WAREYAŞMİ":"localmonth","WAREYAŞMİ2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","WAREYAŞMİ1":"localmonth1","LOCALMONTH1":"localmonth1","NAMEYÉWAREYAŞMİ":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMEYWAREDÉAŞMİDACI":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","WAREYAŞMİKILMKERDIŞ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","WAREYROCE":"localday","LOCALDAY":"localday","WAREYROCE2":"localday2","LOCALDAY2":"localday2","NAMEYÉWAREYROCE":"localdayname","LOCALDAYNAME":"localdayname","WAREYSERRE":"localyear", +"LOCALYEAR":"localyear","WAREYDEME":"localtime","LOCALTIME":"localtime","WAREYSEHAT":"localhour","LOCALHOUR":"localhour","NAMEYSİTA":"sitename","SITENAME":"sitename","MEVCUDHEFTE":"currentweek","CURRENTWEEK":"currentweek","MEVCUDWAREYHEFTİ":"currentdow","CURRENTDOW":"currentdow","WAREYHEFTİ":"localweek","LOCALWEEK":"localweek","WAREYROCAHEFTİ":"localdow","LOCALDOW":"localdow","EBATAREVİZYONİ":"revisionsize","REVISIONSIZE":"revisionsize","VERSİYONVNEWKİ":"currentversion","CURRENTVERSION":"currentversion","WAREYSEHATÉNEWKİ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MALUMATÉWAREYSEHAT":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","HETANIŞANKERDIŞ":"directionmark","HETNIŞAN":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ZUWANÉKESTÉ":"contentlanguage","ZUWESTEN":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dsb.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dsb.json new file mode 100644 index 0000000..d777619 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dsb.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__":"forcetoc","__inhaltsverzeichnis__":"toc","__toc__":"toc", +"__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__":"noindex","__NOINDEX__":"noindex","__PERMANENTE_WEITERLEITUNG__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","lokale_url":"localurl","localurl":"localurl","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zahlenformat":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#sprache":"language","#language":"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright": +"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorienbaum":"categorytree","#kategoriebaum":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx", +"#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE":"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SEITENNAME":"pagename","PAGENAME":"pagename","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSSIONSSEITE":"talkpagename","DISK":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","HAUPTSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR":"revisionyear","VERSIONSJAHR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp", +"REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","NAMENSRAUM":"namespace","NAMESPACE":"namespace","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DATEIANZAHL":"numberoffiles","NUMBEROFFILES":"numberoffiles","BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTIVE_BENUTZER": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JETZIGER_MONATSNAME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JETZIGER_KALENDERTAG":"currentday","JETZIGER_TAG":"currentday","CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2","JETZIGER_TAG_2": +"currentday2","CURRENTDAY2":"currentday2","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV":"localmonthnamegen","LOKALER_MONATSNAME_GEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2","LOKALER_TAG_2":"localday2","LOCALDAY2":"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME":"localdayname","LOKALES_JAHR" +:"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize","JETZIGE_VERSION":"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHALTSSPRACHE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE": +"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöüßa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dty.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dty.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dty.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dv.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dv.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dv.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dz.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dz.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-dz.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ee.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ee.json new file mode 100644 index 0000000..04b8ca0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ee.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z̃ɖɛ́ƒɣŋɔ̄ʋ̀]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-el.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-el.json new file mode 100644 index 0000000..608229a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-el.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__χωρισππ__":"notoc","__χωρισπινακαπεριεχομενων__":"notoc","__notoc__":"notoc","__χωρισπινακοθηκη__":"nogallery","__nogallery__":"nogallery","__μεππ__":"forcetoc","__μεπινακαπεριεχομενων__":"forcetoc","__forcetoc__":"forcetoc","__ππ__":"toc", +"__πινακασπεριεχομενων__":"toc","__toc__":"toc","__χωρισεπεξενοτ__":"noeditsection","__χωρισεπεξεργασιαενοτητων__":"noeditsection","__noeditsection__":"noeditsection","__χωρισμετατροπητιτλου__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__χωρισμετατροπηπεριχομενου__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ΔΕΣΜΟΣΝΕΑΣΕΝΟΤΗΤΑΣ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__ΚΡΥΦΗΚΑΤΗΓΟΡΙΑ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ΕΥΡΕΤΗΡΙΟ__":"index","__INDEX__":"index","__ΧΩΡΙΣΕΥΡΕΤΗΡΙΟ__":"noindex","__NOINDEX__":"noindex","__ΣΤΑΤΙΚΗΑΝΑΚΑΤΕΥΘΥΝΣΗ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"χο":"ns","χωροσονοματων":"ns","οχ":"ns","ονοματοχωροσ":"ns","ns":"ns","nse":"nse","κωδικοποιησηurl":"urlencode","urlencode":"urlencode","πρωτοπεζο":"lcfirst","lcfirst":"lcfirst","πρωτοκεφαλαιο":"ucfirst","ucfirst":"ucfirst","πεζα":"lc","lc":"lc","κεφαλαια":"uc","uc":"uc","τοπικοurl":"localurl","localurl":"localurl","τοπικοurlκ":"localurle","localurle":"localurle","πληρεσurl":"fullurl","fullurl":"fullurl","πληρεσurlκ":"fullurle","fullurle":"fullurle","κανονικοurl":"canonicalurl","canonicalurl":"canonicalurl","κανονικοurlκ":"canonicalurle","canonicalurle":"canonicalurle","μορφοποιησηαριθμου":"formatnum","formatnum":"formatnum","γραμματικη":"grammar","grammar":"grammar","φυλο":"gender","gender":"gender", +"πληθυντικοσ":"plural","plural":"plural","bidi":"bidi","#γλωσσα":"language","#language":"language","αριστεροπαραγεμισμα":"padleft","padleft":"padleft","δεξιπαραγεμισμα":"padright","padright":"padright","κωδικοποιησηαγκυρασ":"anchorencode","anchorencode":"anchorencode","διαδρομηαρχειου":"filepath","filepath":"filepath","pageid":"pageid","εσωτ":"int","int":"int","#λειτουργία":"special","#special":"special","#speciale":"speciale","#ετικέτα":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx": +"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","εξυπηρετητησ":"server","server":"server","ονομαεξυπηρετητη":"servername","servername":"servername","διαδρομηπρογραμματοσ":"scriptpath","scriptpath":"scriptpath","διαδρομηστυλ":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ΟΜΑΔΑΑΡΙΘΜΗΣΗΣ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ΠΡΟΚΑΘΟΡΙΣΜΕΝΗΤΑΞΙΝΟΜΗΣΗ":"defaultsort","ΚΛΕΙΔΙΠΡΟΚΑΘΟΡΙΣΜΕΝΗΣΤΑΞΙΝΟΜΗΣΗΣ":"defaultsort","ΠΡΟΚΑΘΟΡΙΣΜΕΝΗΤΑΞΙΝΟΜΗΣΗΚΑΤΗΓΟΡΙΑΣ":"defaultsort","ΠΡΟΚΤΑΞ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY": +"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","ΣΕΛΙΔΕΣΣΤΗΝΚΑΤΗΓΟΡΙΑ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ΜΕΓΕΘΟΣΣΕΛΙΔΑΣ":"pagesize","PAGESIZE":"pagesize","ΕΠΙΠΕΔΟΠΡΟΣΤΑΣΙΑΣ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ΟΝΟΜΑΣΕΛΙΔΑΣ":"pagename","PAGENAME":"pagename","ΟΝΟΜΑΣΕΛΙΔΑΣΚ":"pagenamee","PAGENAMEE":"pagenamee","ΠΛΗΡΕΣΟΝΟΜΑΣΕΛΙΔΑΣ":"fullpagename","FULLPAGENAME":"fullpagename","ΠΛΗΡΕΣΟΝΟΜΑΣΕΛΙΔΑΣΚ":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ΟΝΟΜΑΥΠΟΣΕΛΙΔΑΣ":"subpagename","SUBPAGENAME":"subpagename","ΟΝΟΜΑΥΠΟΣΕΛΙΔΑΣΚ":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ΒΑΣΗΟΝΟΜΑΤΟΣΣΕΛΙΔΑΣ":"basepagename","BASEPAGENAME":"basepagename", +"ΒΑΣΗΟΝΟΜΑΤΟΣΣΕΛΙΔΑΣΚ":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ΟΝΟΜΑΣΕΛΙΔΑΣΣΥΖΗΤΗΣΕΩΝ":"talkpagename","TALKPAGENAME":"talkpagename","ΟΝΟΜΑΣΕΛΙΔΑΣΣΥΖΗΤΗΣΕΩΝΚ":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","ΟΝΟΜΑΣΕΛΙΔΑΣΘΕΜΑΤΟΣ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ΟΝΟΜΑΣΕΛΙΔΑΣΘΕΜΑΤΟΣΚ":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ΚΩΔΙΚΟΣΑΛΛΑΓΗΣ":"revisionid","REVISIONID":"revisionid","ΜΕΡΑΑΛΛΑΓΗΣ":"revisionday","REVISIONDAY":"revisionday","ΜΕΡΑΑΛΛΑΓΗΣ2":"revisionday2","REVISIONDAY2":"revisionday2","ΜΗΝΑΣΑΛΛΑΓΗΣ":"revisionmonth","REVISIONMONTH":"revisionmonth","ΜΗΝΑΣΑΝΑΘΕΩΡΗΣΗΣ1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ΕΤΟΣΑΛΛΑΓΗΣ":"revisionyear","REVISIONYEAR":"revisionyear", +"ΧΡΟΝΟΣΗΜΑΝΣΗΑΛΛΑΓΗΣ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ΧΡΗΣΤΗΣΑΝΑΘΕΩΡΗΣΗΣ":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ΠΕΡΙΟΧΗ":"namespace","NAMESPACE":"namespace","ΠΕΡΙΟΧΗΚ":"namespacee","NAMESPACEE":"namespacee","ΑΡΙΘΜΟΣΟΝΟΜΑΤΟΣΧΩΡΟΥ":"namespacenumber","ΑΡΙΘΜΟΣΟΝΟΜΑΤΟΧΩΡΟΥ":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ΠΕΡΙΟΧΗΣΥΖΗΤΗΣΕΩΝ":"talkspace","TALKSPACE":"talkspace","ΠΕΡΙΟΧΗΣΥΖΗΤΗΣΕΩΝΚ":"talkspacee","TALKSPACEE":"talkspacee","ΠΕΡΙΟΧΗΘΕΜΑΤΩΝ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ΠΕΡΙΟΧΗΘΕΜΑΤΩΝΚ":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ΑΡΙΘΜΟΣΑΡΘΡΩΝ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ΑΡΙΘΜΟΣΑΡΧΕΙΩΝ":"numberoffiles", +"NUMBEROFFILES":"numberoffiles","ΑΡΙΘΜΟΣΧΡΗΣΤΩΝ":"numberofusers","NUMBEROFUSERS":"numberofusers","ΕΝΕΡΓΟΙΧΡΗΣΤΕΣ":"numberofactiveusers","ΑΡΙΘΜΟΣΕΝΕΡΓΩΝΧΡΗΣΤΩΝ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ΑΡΙΘΜΟΣΣΕΛΙΔΩΝ":"numberofpages","NUMBEROFPAGES":"numberofpages","ΑΡΙΘΜΟΣΔΙΑΧΕΙΡΙΣΤΩΝ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ΑΡΙΘΜΟΣΕΠΕΞΕΡΓΑΣΙΩΝ":"numberofedits","NUMBEROFEDITS":"numberofedits","ΔΕΙΞΕΤΙΤΛΟ":"displaytitle","ΠΡΟΒΟΛΗΤΙΤΛΟΥ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ΤΡΕΧΩΝΜΗΝΑΣ":"currentmonth","ΤΡΕΧΩΝΜΗΝΑΣ2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ΤΡΕΧΩΝΜΗΝΑΣ1":"currentmonth1","CURRENTMONTH1":"currentmonth1","ΤΡΕΧΩΝΜΗΝΑΣΟΝΟΜΑ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname", +"ΤΡΕΧΩΝΜΗΝΑΣΓΕΝΙΚΗ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ΤΡΕΧΩΝΜΗΝΑΣΣΥΝΤ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ΤΡΕΧΟΥΣΑΜΕΡΑ":"currentday","CURRENTDAY":"currentday","ΤΡΕΧΟΥΣΑΜΕΡΑ2":"currentday2","CURRENTDAY2":"currentday2","ΤΡΕΧΟΥΣΑΜΕΡΑΟΝΟΜΑ":"currentdayname","CURRENTDAYNAME":"currentdayname","ΤΡΕΧΟΝΕΤΟΣ":"currentyear","CURRENTYEAR":"currentyear","ΤΡΕΧΩΝΧΡΟΝΟΣ":"currenttime","CURRENTTIME":"currenttime","ΤΡΕΧΟΥΣΑΩΡΑ":"currenthour","CURRENTHOUR":"currenthour","ΤΟΠΙΚΟΣΜΗΝΑΣ":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","ΤΟΠΙΚΟΣΜΗΝΑΣ1":"localmonth1","LOCALMONTH1":"localmonth1","ΤΟΠΙΚΟΣΜΗΝΑΣΟΝΟΜΑ":"localmonthname","LOCALMONTHNAME":"localmonthname","ΤΟΠΙΚΟΣΜΗΝΑΣΓΕΝΙΚΗ":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen", +"ΤΟΠΙΚΟΣΜΗΝΑΣΣΥΝΤ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ΤΟΠΙΚΗΜΕΡΑ":"localday","LOCALDAY":"localday","ΤΟΠΙΚΗΜΕΡΑ2":"localday2","LOCALDAY2":"localday2","ΤΟΠΙΚΗΜΕΡΑΟΝΟΜΑ":"localdayname","LOCALDAYNAME":"localdayname","ΤΟΠΙΚΟΕΤΟΣ":"localyear","LOCALYEAR":"localyear","ΤΟΠΙΚΟΣΧΡΟΝΟΣ":"localtime","LOCALTIME":"localtime","ΤΟΠΙΚΗΩΡΑ":"localhour","LOCALHOUR":"localhour","ΙΣΤΟΧΩΡΟΣ":"sitename","SITENAME":"sitename","ΤΡΕΧΟΥΣΑΕΒΔΟΜΑΔΑ":"currentweek","CURRENTWEEK":"currentweek","ΤΡΕΧΟΥΣΑΜΕΡΑΕΒΔΟΜΑΔΑΣ":"currentdow","CURRENTDOW":"currentdow","ΤΟΠΙΚΗΕΒΔΟΜΑΔΑ":"localweek","LOCALWEEK":"localweek","ΤΟΠΙΚΗΜΕΡΑΕΒΔΟΜΑΔΑΣ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ΤΡΕΧΟΥΣΑΕΚΔΟΣΗ":"currentversion","CURRENTVERSION":"currentversion","ΤΡΕΧΟΥΣΑΧΡΟΝΟΣΗΜΑΝΣΗ":"currenttimestamp", +"CURRENTTIMESTAMP":"currenttimestamp","ΤΟΠΙΚΗΧΡΟΝΟΣΗΜΑΝΣΗ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","ΚΩΔΙΚΟΣΦΟΡΑΣ":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ΓΛΩΣΣΑΠΕΡΙΕΧΟΜΕΝΟΥ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zαβγδεζηθικλμνξοπρστυφχψωςΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίόύώϊϋΐΰΆΈΉΊΌΎΏΪΫ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-eml.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-eml.json new file mode 100644 index 0000000..03b7a4a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-eml.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag", +"#formatodata":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#alberocategorie":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath": +"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2", +"CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME" +:"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NOMESITO":"sitename","SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-en.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-en.json new file mode 100644 index 0000000..521bba5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-en.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","pendingchangelevel":"pendingchangelevel","#target":"target","#babel": +"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#assessment":"assessment","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory", +"PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace", +"TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","SHORTDESC":"shortdesc","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN": +"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters": +"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-eo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-eo.json new file mode 100644 index 0000000..0172afc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-eo.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__ni__":"notoc","__neindekso__":"notoc","__nt__":"notoc","__notoc__":"notoc","__ng__":"nogallery","__senbildaro__":"nogallery","__sb__":"nogallery","__sg__":"nogallery","__sengalerio__":"nogallery","__nogallery__":"nogallery","__fi__":"forcetoc","__fortuindekson__":"forcetoc","__ft__":"forcetoc","__forcetoc__":"forcetoc", +"__i__":"toc","__t__":"toc","__indekso__":"toc","__toc__":"toc","__srs__":"noeditsection","__nes__":"noeditsection","__senredaktisekciojn__":"noeditsection","__senredaktisekcion__":"noeditsection","__noeditsection__":"noeditsection","__nekonvertutitolon__":"notitleconvert","__nkt__":"notitleconvert","__ntc__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nekonvertuenhavon__":"nocontentconvert","__nkh__":"nocontentconvert","__ncc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIGILOALNOVASEKCIO__":"newsectionlink","__NSL__":"newsectionlink","__LNS__":"newsectionlink","__LANS__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__SENLIGILOALNOVASEKCIO__":"nonewsectionlink","__NNSL__":"nonewsectionlink","__SLNS__":"nonewsectionlink","__SLANS__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KK__":"hiddencat","__KAŜITAKATEGORIO__":"hiddencat","__KASXITAKATEGORIO__": +"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKSU__":"index","__INDEKSI__":"index","__INDEX__":"index","__NEINDEKSU__":"noindex","__NIU__":"noindex","__NOINDEX__":"noindex","__STATIKAALIDIREKTO__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nss":"nse","nso":"nse","nse":"nse","urlencode":"urlencode","malmajuskleunua":"lcfirst","minuskleunua":"lcfirst","mmu":"lcfirst","lcfirst":"lcfirst","majuskleunua":"ucfirst","malminuskleunua":"ucfirst","mu":"ucfirst","ucfirst":"ucfirst","malmajuskle":"lc","minuskle":"lc","lc":"lc","majuskle":"uc","malminuskle":"uc","uc":"uc","lokattt":"localurl","localurl":"localurl","lokatttt":"localurle","localurle":"localurle","plenaligilo":"fullurl","plenlig":"fullurl","tutattt":"fullurl","fullurl":"fullurl","plenaligiloo":"fullurle","plenligg":"fullurle","tutatttt":"fullurle","fullurle":"fullurle", +"canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","gramatiko":"grammar","grammar":"grammar","sekso":"gender","gender":"gender","plurala":"plural","plural":"plural","bidi":"bidi","#lingvo":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","dosiero-voro":"filepath","dosiervojo":"filepath","filepath":"filepath","identigilodepaĝo":"pageid","paĝid":"pageid","pageid":"pageid","ene":"int","int":"int","#speciala":"special","#special":"special","#speciale":"speciale","#marko":"tag","#etikedo":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#kategoriarbo":"categorytree","#categorytree":"categorytree","#target":"target","#babelo":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seekv":"ifeq","#seekvacio":"ifeq","#seeglas":"ifeq","#laŭsamvalorade":"ifeq","#lauxsamvalorade":"ifeq","#ifeq":"ifeq","#ŝaltu": +"switch","#ŝalti":"switch","#sxaltu":"switch","#sxalti":"switch","#ŝalte":"switch","#sxalte":"switch","#switch":"switch","#seekzistas":"ifexist","#laŭasignade":"ifexist","#lauxasignade":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#seeksprimo":"ifexpr","#seesprimo":"ifexpr","#laŭevaluade":"ifexpr","#ifexpr":"ifexpr","#seeraras":"iferror","#laŭerarade":"iferror","#lauxerarade":"iferror","#iferror":"iferror","#tempo":"time","#datformu":"time","#time":"time","#tempoo":"timel","#lokaĵdatdonu":"timel","#lokajxdatdonu":"timel","#timel":"timel","#espr":"expr","#esprimo":"expr","#esprime":"expr","#expr":"expr","#absolutvojdonu":"rel2abs","#malrelativige":"rel2abs","#rel2abs":"rel2abs","#titolpartdonu":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","neniuligiloalalialingvo":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eco":"property","#property":"property", +"#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikolapado":"articlepath","artikolavojo":"articlepath","articlepath":"articlepath","servilo":"server","server":"server","nomodeservilo":"servername","servilanomo":"servername","servilonomo":"servername","servername":"servername","skripto-vojo":"scriptpath","skriptovojo":"scriptpath","skriptvojo":"scriptpath","scriptpath":"scriptpath","stilo-vojo":"stylepath","stilovojo":"stylepath","stilvojo":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBRODEPAĜOJ":"numberofpages","NOMBRODEPAGXOJ":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBRODEUZANTOJ":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBRODEAKTIVAJUZANTOJ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBRODEARTIKOLOJ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBRODEDOSIEROJ":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBRODEADMINOJ" +:"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NOMBRODEREDAKTOJ":"numberofedits","NUMBEROFEDITS":"numberofedits","DEFAŬLTORDIGO":"defaultsort","DEFAUXLTORDIGO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAĜOJENKATEGORIO":"pagesincategory","PAGXOJENKATEGORIO":"pagesincategory","PAĜOJENKAT":"pagesincategory","PAGXOJENKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAĜOPEZO":"pagesize","PAGXOPEZO":"pagesize","PEZODEPAĜO":"pagesize","PEZODEPAGXO":"pagesize","PAGESIZE":"pagesize","PROTEKTONIVELO":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMSPACOO":"namespacee","NAMESPACEE":"namespacee","NUMERODENOMSPACO":"namespacenumber","NOMSPACNUMERO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUTNOMSPACO":"talkspace","TALKSPACE":"talkspace","DISKUTNOMSPACOO": +"talkspacee","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAĜONOMO":"pagename","PAGXONOMO":"pagename","PAĜNOMO":"pagename","PAGXNOMO":"pagename","PAGENAME":"pagename","PAĜONOMOO":"pagenamee","PAGXONOMOO":"pagenamee","PAĜNOMOO":"pagenamee","PAGXNOMOO":"pagenamee","PAGENAMEE":"pagenamee","TUTAPAĜONOMO":"fullpagename","TUTAPAGXONOMO":"fullpagename","TUTAPAĜNOMO":"fullpagename","TUTAPAGXNOMO":"fullpagename","FULLPAGENAME":"fullpagename","TUTAPAĜONOMOO":"fullpagenamee","TUTAPAGXONOMOO":"fullpagenamee","TUTAPAĜNOMOO":"fullpagenamee","TUTAPAGXNOMOO":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","PATRAPAĜONOMO":"basepagename","PATRAPAGXONOMO":"basepagename","PATRAPAĜNOMO":"basepagename","PATRAPAGXNOMO":"basepagename","BASEPAGENAME":"basepagename","PATRAPAĜONOMOO":"basepagenamee","PATRAPAGXONOMOO":"basepagenamee", +"PATRAPAĜNOMOO":"basepagenamee","PATRAPAGXNOMOO":"basepagenamee","BASEPAGENAMEE":"basepagenamee","SUBPAĜONOMO":"subpagename","SUBPAGXONOMO":"subpagename","SUBPAĜNOMO":"subpagename","SUBPAGXNOMO":"subpagename","SUBPAGENAME":"subpagename","SUBPAĜONOMOO":"subpagenamee","SUBPAGXONOMOO":"subpagenamee","SUBPAĜNOMOO":"subpagenamee","SUBPAGXNOMOO":"subpagenamee","SUBPAGENAMEE":"subpagenamee","DISKUTPAĜONOMO":"talkpagename","DISKUTPAGXONOMO":"talkpagename","DISKUTPAĜNOMO":"talkpagename","DISKUTPAGXNOMO":"talkpagename","TALKPAGENAME":"talkpagename","DISKUTPAĜONOMOO":"talkpagenamee","DISKUTPAGXONOMOO":"talkpagenamee","DISKUTPAĜNOMOO":"talkpagenamee","DISKUTPAGXNOMOO":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth", +"REVISIONMONTH1":"revisionmonth1","JARODEREVIZIO":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NOMSPACO":"namespace","NAMESPACE":"namespace","MONTRUTITOLON":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","NUNAMONATO":"currentmonth","NUNAMONATO2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","NUNAMONATO1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NUNAMONATNOMO":"currentmonthname","NUNAMONATONOMO":"currentmonthname","NUNAMONATANOMO":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NUNAMONATNOMOGEN":"currentmonthnamegen","NUNAMONATONOMOGEN":"currentmonthnamegen","NUNAMONATANOMOGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NUNAMONATNOMOMAL":"currentmonthabbrev","NUNAMONATONOMOMAL":"currentmonthabbrev","NUNAMONATANOMOMAL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","NUNATAGO":"currentday" +,"CURRENTDAY":"currentday","NUNATAGO2":"currentday2","CURRENTDAY2":"currentday2","NUNATAGNOMO":"currentdayname","CURRENTDAYNAME":"currentdayname","NUNAJARO":"currentyear","CURRENTYEAR":"currentyear","NUNATEMPO":"currenttime","CURRENTTIME":"currenttime","NUNAHORO":"currenthour","CURRENTHOUR":"currenthour","LOKAMONATO":"localmonth","LOKAMONATO2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKAMONATO1":"localmonth1","LOCALMONTH1":"localmonth1","LOKAMONATNOMO":"localmonthname","LOKAMONATONOMO":"localmonthname","LOKAMONATANOMO":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKAMONATNOMOGEN":"localmonthnamegen","LOKAMONATONOMOGEN":"localmonthnamegen","LOKAMONATANOMOGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKAMONATNOMOMAL":"localmonthabbrev","LOKAMONATONOMOMAL":"localmonthabbrev","LOKAMONATANOMOMAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKATAGO":"localday","LOCALDAY":"localday","LOKATAGO2":"localday2","LOCALDAY2": +"localday2","LOKATAGNOMO":"localdayname","LOKATAGONOMO":"localdayname","LOKATAGANOMO":"localdayname","LOCALDAYNAME":"localdayname","LOKAJARO":"localyear","LOCALYEAR":"localyear","LOKATEMPO":"localtime","LOCALTIME":"localtime","LOKAHORO":"localhour","LOCALHOUR":"localhour","TTT-NOMO":"sitename","RETPAĜNOMO":"sitename","RETPAGXNOMO":"sitename","RETEJNOMO":"sitename","SITENAME":"sitename","NUNASEMAJNO":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOKASEMAJNO":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","NUNAVERSIO":"currentversion","CURRENTVERSION":"currentversion","NUNATEMPINDIKO":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKATEMPINDIKO":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ENHAVA-LINGVO":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-es.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-es.json new file mode 100644 index 0000000..968d03e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-es.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__sin_tdc__":"notoc","__notdc__":"notoc","__notoc__":"notoc","__sin_galería__":"nogallery","__nogalería__":"nogallery","__nogaleria__":"nogallery","__nogallery__":"nogallery","__forzar_tdc__":"forcetoc","__forzartdc__":"forcetoc","__forzartoc__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc","__toc__":"toc", +"__no_editar_sección__":"noeditsection","__noeditarsección__":"noeditsection","__noeditarseccion__":"noeditsection","__noeditsection__":"noeditsection","__noconvertirtitulo__":"notitleconvert","__noconvertirtítulo__":"notitleconvert","__noct___":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__noconvertircontenido__":"nocontentconvert","__nocc___":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__VINCULARANUEVASECCION__":"newsectionlink","__ENLACECREARSECCIÓN__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NOVINCULARANUEVASECCION__":"nonewsectionlink","__SINENLACECREARSECCIÓN__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATEGORÍAOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECCIÓNESTÁTICA__":"staticredirect", +"__REDIRECCIONESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIGUACION__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"en":"ns","ns":"ns","nse":"nse","codificarurl":"urlencode","urlencode":"urlencode","primerominus":"lcfirst","primerominús":"lcfirst","lcfirst":"lcfirst","primeromayus":"ucfirst","primeromayús":"ucfirst","ucfirst":"ucfirst","minus":"lc","minús":"lc","lc":"lc","mayus":"uc","mayús":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","urllocalc":"localurle","localurle":"localurle","urlcompleta":"fullurl","fullurl":"fullurl","urlcompletac":"fullurle","fullurle":"fullurle","urlcanonica":"canonicalurl","canonicalurl":"canonicalurl","urlcanonicac":"canonicalurle","canonicalurle":"canonicalurle","formatonúmero":"formatnum","formatonumero":"formatnum","formatnum":"formatnum","gramatica":"grammar","gramática":"grammar","grammar": +"grammar","género":"gender","genero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#idioma":"language","#language":"language","rellenarizquierda":"padleft","rellenarizq":"padleft","padleft":"padleft","rellenarderecha":"padright","rellenarder":"padright","padright":"padright","anchorencode":"anchorencode","rutaarchivo":"filepath","rutarchivo":"filepath","rutadearchivo":"filepath","filepath":"filepath","iddepágina":"pageid","idpágina":"pageid","iddepagina":"pageid","idpagina":"pageid","pageid":"pageid","int":"int","#especial":"special","#special":"special","#especialc":"speciale","#speciale":"speciale","#etiqueta":"tag","#tag":"tag","#formatodefecha":"formatdate","#formatearfecha":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#destino":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#invocar":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#siigual":"ifeq","#ifeq":"ifeq","#según":"switch","#switch" +:"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierror":"iferror","#iferror":"iferror","#tiempo":"time","#time":"time","#tiempol":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#árboldecategorías":"categorytree","#arboldecategorias":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","nointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propiedad":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","rutaartículo":"articlepath","rutaarticulo":"articlepath","articlepath":"articlepath","servidor":"server","server":"server","nombreservidor":"servername","servername":"servername","rutascript":"scriptpath","rutadescript":"scriptpath","scriptpath":"scriptpath","rutaestilo":"stylepath","rutadeestilo":"stylepath", +"stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NÚMEROENGRUPO":"numberingroup","NUMEROENGRUPO":"numberingroup","NUMENGRUPO":"numberingroup","NÚMENGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENAR":"defaultsort","CLAVEDEORDENPREDETERMINADO":"defaultsort","ORDENDECATEGORIAPREDETERMINADO":"defaultsort","ORDENDECATEGORÍAPREDETERMINADO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PÁGINASENCATEGORÍA":"pagesincategory","PÁGINASENCAT":"pagesincategory","PAGSENCAT":"pagesincategory","PAGINASENCATEGORIA":"pagesincategory","PAGINASENCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMAÑOPÁGINA":"pagesize","TAMAÑODEPÁGINA":"pagesize","TAMAÑOPAGINA":"pagesize","TAMAÑODEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECCIÓN":"protectionlevel","NIVELDEPROTECCION":"protectionlevel","PROTECTIONLEVEL" +:"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMBREDEPAGINA":"pagename","NOMBREDEPÁGINA":"pagename","PAGENAME":"pagename","NOMBREDEPAGINAC":"pagenamee","NOMBREDEPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMBRECOMPLETODEPÁGINA":"fullpagename","NOMBRECOMPLETODEPAGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMBRECOMPLETODEPAGINAC":"fullpagenamee","NOMBRECOMPLETODEPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMBREDESUBPAGINA":"subpagename","NOMBREDESUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMBREDESUBPAGINAC":"subpagenamee","NOMBREDESUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMBREDEPAGINARAIZ":"rootpagename","NOMBREDEPÁGINARAÍZ":"rootpagename","ROOTPAGENAME":"rootpagename","NOMBREDEPAGINARAIZC":"rootpagenamee","NOMBREDEPÁGINARAÍZC":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBREDEPAGINABASE":"basepagename","NOMBREDEPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMBREDEPAGINABASEC": +"basepagenamee","NOMBREDEPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMBREDEPÁGINADEDISCUSIÓN":"talkpagename","NOMBREDEPAGINADEDISCUSION":"talkpagename","NOMBREDEPAGINADISCUSION":"talkpagename","NOMBREDEPÁGINADISCUSIÓN":"talkpagename","TALKPAGENAME":"talkpagename","NOMBREDEPÁGINADEDISCUSIÓNC":"talkpagenamee","NOMBREDEPAGINADEDISCUSIONC":"talkpagenamee","NOMBREDEPAGINADISCUSIONC":"talkpagenamee","NOMBREDEPÁGINADISCUSIÓNC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMBREDEPAGINADETEMA":"subjectpagename","NOMBREDEPÁGINADETEMA":"subjectpagename","NOMBREDEPÁGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEARTICULO":"subjectpagename","NOMBREDEPÁGINADEARTÍCULO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMBREDEPAGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEASUNTOC": +"subjectpagenamee","NOMBREDEPAGINADEARTICULOC":"subjectpagenamee","NOMBREDEPÁGINADEARTÍCULOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDEREVISION":"revisionid","IDREVISION":"revisionid","IDDEREVISIÓN":"revisionid","IDREVISIÓN":"revisionid","REVISIONID":"revisionid","DIADEREVISION":"revisionday","DIAREVISION":"revisionday","DÍADEREVISIÓN":"revisionday","DÍAREVISIÓN":"revisionday","REVISIONDAY":"revisionday","DIADEREVISION2":"revisionday2","DIAREVISION2":"revisionday2","DÍADEREVISIÓN2":"revisionday2","DÍAREVISIÓN2":"revisionday2","REVISIONDAY2":"revisionday2","MESDEREVISION":"revisionmonth","MESDEREVISIÓN":"revisionmonth","MESREVISION":"revisionmonth","MESREVISIÓN":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDEREVISION1":"revisionmonth1","MESDEREVISIÓN1":"revisionmonth1","MESREVISION1":"revisionmonth1","MESREVISIÓN1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","AÑODEREVISION":"revisionyear", +"AÑODEREVISIÓN":"revisionyear","AÑOREVISION":"revisionyear","AÑOREVISIÓN":"revisionyear","REVISIONYEAR":"revisionyear","MARCADEHORADEREVISION":"revisiontimestamp","MARCADEHORADEREVISIÓN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODEREVISION":"revisionuser","USUARIODEREVISIÓN":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACIODENOMBRE":"namespace","NAMESPACE":"namespace","ESPACIODENOMBREC":"namespacee","NAMESPACEE":"namespacee","NÚMERODELESPACIO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACIODEDISCUSION":"talkspace","ESPACIODEDISCUSIÓN":"talkspace","TALKSPACE":"talkspace","ESPACIODEDISCUSIONC":"talkspacee","TALKSPACEE":"talkspacee","ESPACIODEASUNTO":"subjectspace","ESPACIODETEMA":"subjectspace","ESPACIODEARTÍCULO":"subjectspace","ESPACIODEARTICULO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACIODETEMAC":"subjectspacee","ESPACIODEASUNTOC":"subjectspacee", +"ESPACIODEARTICULOC":"subjectspacee","ESPACIODEARTÍCULOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NÚMERODEARTÍCULOS":"numberofarticles","NUMERODEARTICULOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NÚMERODEARCHIVOS":"numberoffiles","NUMERODEARCHIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NÚMERODEPÁGINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NÚMEROADMINIISTRADORES":"numberofadmins","NÚMEROADMINS":"numberofadmins","NUMEROADMINS":"numberofadmins","NUMEROADMINISTRADORES":"numberofadmins","NUMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINS":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINS": +"numberofadmins","NUMBEROFADMINS":"numberofadmins","NÚMERODEEDICIONES":"numberofedits","NUMERODEEDICIONES":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRARTÍTULO":"displaytitle","MOSTRARTITULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESACTUAL":"currentmonth","MESACTUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MESACTUALCOMPLETO":"currentmonthname","NOMBREMESACTUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MESACTUALGENITIVO":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESACTUALABREVIADO":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DÍAACTUAL":"currentday","DIAACTUAL":"currentday","DÍA_ACTUAL":"currentday","DIA_ACTUAL":"currentday","CURRENTDAY":"currentday","DÍAACTUAL2":"currentday2","DIAACTUAL2":"currentday2","DÍA_ACTUAL2":"currentday2","DIA_ACTUAL2":"currentday2","CURRENTDAY2":"currentday2", +"NOMBREDÍAACTUAL":"currentdayname","NOMBREDIAACTUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","AÑOACTUAL":"currentyear","AÑO_ACTUAL":"currentyear","CURRENTYEAR":"currentyear","HORA_MINUTOS_ACTUAL":"currenttime","HORAMINUTOSACTUAL":"currenttime","TIEMPOACTUAL":"currenttime","CURRENTTIME":"currenttime","HORAACTUAL":"currenthour","HORA_ACTUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","MESLOCALCOMPLETO":"localmonthname","NOMBREMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","MESLOCALGENITIVO":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREVIADO":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DÍALOCAL":"localday","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","DÍALOCAL2":"localday2","LOCALDAY2":"localday2","NOMBREDIALOCAL":"localdayname", +"NOMBREDÍALOCAL":"localdayname","LOCALDAYNAME":"localdayname","AÑOLOCAL":"localyear","LOCALYEAR":"localyear","HORAMINUTOSLOCAL":"localtime","TIEMPOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMBREDELSITIO":"sitename","SITENAME":"sitename","SEMANAACTUAL":"currentweek","CURRENTWEEK":"currentweek","DDSACTUAL":"currentdow","DIADESEMANAACTUAL":"currentdow","DÍADESEMANAACTUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DDSLOCAL":"localdow","DIADESEMANALOCAL":"localdow","DÍADESEMANALOCAL":"localdow","LOCALDOW":"localdow","TAMAÑODEREVISIÓN":"revisionsize","TAMAÑODEREVISION":"revisionsize","REVISIONSIZE":"revisionsize","VERSIONACTUAL":"currentversion","VERSIÓNACTUAL":"currentversion","CURRENTVERSION":"currentversion","MARCADEHORAACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MARCADEHORALOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark" +,"DIRMARK":"directionmark","IDIOMADELCONTENIDO":"contentlanguage","IDIOMADELCONT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záéíóúñ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-et.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-et.json new file mode 100644 index 0000000..c81da93 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-et.json @@ -0,0 +1,9 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__sisukorrata__":"notoc","__notoc__":"notoc","__galeriita__":"nogallery","__nogallery__":"nogallery","__sisukordees__":"forcetoc","__forcetoc__":"forcetoc","__sisukord__":"toc","__toc__":"toc","__alaosalingita__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__": +"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__UUEALAOSALINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__UUEALAOSALINGITA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__PEIDETUDKAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","INDEKSIGA":"index","__INDEX__":"index","INDEKSITA":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr1":"nse","nse":"nse","urlencode":"urlencode","esivt":"lcfirst","lcfirst":"lcfirst","esist":"ucfirst","ucfirst":"ucfirst","vt":"lc","lc":"lc","st":"uc","uc":"uc","kohalikurl":"localurl","localurl":"localurl","kohalikurl1":"localurle","localurle":"localurle","koguurl":"fullurl","fullurl":"fullurl","koguurl1":"fullurle","fullurle":"fullurle","canonicalurl": +"canonicalurl","canonicalurle":"canonicalurle","arvuvormindus":"formatnum","formatnum":"formatnum","grammar":"grammar","sugu":"gender","gender":"gender","plural":"plural","bidi":"bidi","#keel":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","failitee":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#eri":"special","#special":"special","#eri1":"speciale","#speciale":"speciale","#tag":"tag","#kuupäevavormindus":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#paabel":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategooriapuu":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x": +"lstx","#lsth":"lsth","#section-h":"lsth","välistekeelelinkideta":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#omadus":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","serverinimi":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"KASUTAJAIDRÜHMAS":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","JÄRJESTA":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","LEHEKÜLGIKATEGOORIAS":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","KAITSETASE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","LEHEKÜLJENIMI":"pagename","PAGENAME":"pagename","LEHEKÜLJENIMI1": +"pagenamee","PAGENAMEE":"pagenamee","KOGULEHEKÜLJENIMI":"fullpagename","FULLPAGENAME":"fullpagename","KOGULEHEKÜLJENIMI1":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ALAMLEHEKÜLJENIMI":"subpagename","SUBPAGENAME":"subpagename","ALAMLEHEKÜLJENIMI1":"subpagenamee","SUBPAGENAMEE":"subpagenamee","JUURLEHEKÜLJENIMI":"rootpagename","ROOTPAGENAME":"rootpagename","JUURLEHEKÜLJENIMI1":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NIMERUUMITANIMI":"basepagename","BASEPAGENAME":"basepagename","NIMERUUMITANIMI1":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ARUTELUNIMI":"talkpagename","TALKPAGENAME":"talkpagename","ARUTELUNIMI1":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1", +"REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NIMERUUM":"namespace","NAMESPACE":"namespace","NIMERUUM1":"namespacee","NAMESPACEE":"namespacee","NIMERUUMINUMBER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ARUTELUNIMERUUM":"talkspace","TALKSPACE":"talkspace","ARUTELUNIMERUUM1":"talkspacee","TALKSPACEE":"talkspacee","SISUNIMERUUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SISUNIMERUUM1":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKLIMÄÄR":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","FAILIMÄÄR":"numberoffiles","NUMBEROFFILES":"numberoffiles","KASUTAJAMÄÄR":"numberofusers","NUMBEROFUSERS":"numberofusers","TEGUSKASUTAJAMÄÄR":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","LEHEMÄÄR":"numberofpages","NUMBEROFPAGES":"numberofpages","ÜLEMAMÄÄR":"numberofadmins", +"NUMBEROFADMINS":"numberofadmins","REDIGEERIMISMÄÄR":"numberofedits","NUMBEROFEDITS":"numberofedits","PEALKIRI":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","HETKEKUU":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","HETKEKUU1":"currentmonth1","CURRENTMONTH1":"currentmonth1","HETKEKUUNIMETUS":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","HETKEKUUOM":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","HETKEKUULÜH":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HETKEKUUPÄEV":"currentday","CURRENTDAY":"currentday","HETKEKUUPÄEV2":"currentday2","CURRENTDAY2":"currentday2","HETKENÄDALAPÄEV":"currentdayname","CURRENTDAYNAME":"currentdayname","HETKEAASTA":"currentyear","CURRENTYEAR":"currentyear","HETKEAEG":"currenttime","CURRENTTIME":"currenttime","HETKETUND":"currenthour","CURRENTHOUR":"currenthour","KOHALIKKUU":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","KOHALIKKUU1": +"localmonth1","LOCALMONTH1":"localmonth1","KOHALIKKUUNIMETUS":"localmonthname","LOCALMONTHNAME":"localmonthname","KOHALIKKUUOM":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","KOHALIKKUULÜH":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","KOHALIKKUUPÄEV":"localday","LOCALDAY":"localday","KOHALIKKUUPÄEV2":"localday2","LOCALDAY2":"localday2","KOHALIKNÄDALAPÄEV":"localdayname","LOCALDAYNAME":"localdayname","KOHALIKAASTA":"localyear","LOCALYEAR":"localyear","KOHALIKAEG":"localtime","LOCALTIME":"localtime","KOHALIKTUND":"localhour","LOCALHOUR":"localhour","KOHANIMI":"sitename","SITENAME":"sitename","HETKENÄDAL":"currentweek","CURRENTWEEK":"currentweek","HETKENÄDALAPÄEV1":"currentdow","CURRENTDOW":"currentdow","KOHALIKNÄDAL":"localweek","LOCALWEEK":"localweek","KOHALIKNÄDALAPÄEV1":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","HETKEAJATEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp", +"KOHALIKAJATEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","VAIKEKEEL":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöõšüža-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-eu.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-eu.json new file mode 100644 index 0000000..aca2f63 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-eu.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","pagebanner":"PAGEBANNER","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ORRIALDEIZEN":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE": +"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKULUKOPURU":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","FITXATEGIKOPURU":"numberoffiles","NUMBEROFFILES":"numberoffiles","LANKIDEKOPURU":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ORRIALDEKOPURU":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","ALDAKETAKOPURU":"numberofedits","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ORAINGOHILABETE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","ORAINGOHILABETEIZEN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","ORAINGOEGUN":"currentday","CURRENTDAY":"currentday","ORAINGOEGUN2":"currentday2","CURRENTDAY2":"currentday2", +"ORAINGOEGUNIZEN":"currentdayname","CURRENTDAYNAME":"currentdayname","ORAINGOURTE":"currentyear","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ext.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ext.json new file mode 100644 index 0000000..968d03e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ext.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__sin_tdc__":"notoc","__notdc__":"notoc","__notoc__":"notoc","__sin_galería__":"nogallery","__nogalería__":"nogallery","__nogaleria__":"nogallery","__nogallery__":"nogallery","__forzar_tdc__":"forcetoc","__forzartdc__":"forcetoc","__forzartoc__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc","__toc__":"toc", +"__no_editar_sección__":"noeditsection","__noeditarsección__":"noeditsection","__noeditarseccion__":"noeditsection","__noeditsection__":"noeditsection","__noconvertirtitulo__":"notitleconvert","__noconvertirtítulo__":"notitleconvert","__noct___":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__noconvertircontenido__":"nocontentconvert","__nocc___":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__VINCULARANUEVASECCION__":"newsectionlink","__ENLACECREARSECCIÓN__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NOVINCULARANUEVASECCION__":"nonewsectionlink","__SINENLACECREARSECCIÓN__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATEGORÍAOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECCIÓNESTÁTICA__":"staticredirect", +"__REDIRECCIONESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIGUACION__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"en":"ns","ns":"ns","nse":"nse","codificarurl":"urlencode","urlencode":"urlencode","primerominus":"lcfirst","primerominús":"lcfirst","lcfirst":"lcfirst","primeromayus":"ucfirst","primeromayús":"ucfirst","ucfirst":"ucfirst","minus":"lc","minús":"lc","lc":"lc","mayus":"uc","mayús":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","urllocalc":"localurle","localurle":"localurle","urlcompleta":"fullurl","fullurl":"fullurl","urlcompletac":"fullurle","fullurle":"fullurle","urlcanonica":"canonicalurl","canonicalurl":"canonicalurl","urlcanonicac":"canonicalurle","canonicalurle":"canonicalurle","formatonúmero":"formatnum","formatonumero":"formatnum","formatnum":"formatnum","gramatica":"grammar","gramática":"grammar","grammar": +"grammar","género":"gender","genero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#idioma":"language","#language":"language","rellenarizquierda":"padleft","rellenarizq":"padleft","padleft":"padleft","rellenarderecha":"padright","rellenarder":"padright","padright":"padright","anchorencode":"anchorencode","rutaarchivo":"filepath","rutarchivo":"filepath","rutadearchivo":"filepath","filepath":"filepath","iddepágina":"pageid","idpágina":"pageid","iddepagina":"pageid","idpagina":"pageid","pageid":"pageid","int":"int","#especial":"special","#special":"special","#especialc":"speciale","#speciale":"speciale","#etiqueta":"tag","#tag":"tag","#formatodefecha":"formatdate","#formatearfecha":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#destino":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#invocar":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#siigual":"ifeq","#ifeq":"ifeq","#según":"switch","#switch" +:"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierror":"iferror","#iferror":"iferror","#tiempo":"time","#time":"time","#tiempol":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#árboldecategorías":"categorytree","#arboldecategorias":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","nointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propiedad":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","rutaartículo":"articlepath","rutaarticulo":"articlepath","articlepath":"articlepath","servidor":"server","server":"server","nombreservidor":"servername","servername":"servername","rutascript":"scriptpath","rutadescript":"scriptpath","scriptpath":"scriptpath","rutaestilo":"stylepath","rutadeestilo":"stylepath", +"stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NÚMEROENGRUPO":"numberingroup","NUMEROENGRUPO":"numberingroup","NUMENGRUPO":"numberingroup","NÚMENGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENAR":"defaultsort","CLAVEDEORDENPREDETERMINADO":"defaultsort","ORDENDECATEGORIAPREDETERMINADO":"defaultsort","ORDENDECATEGORÍAPREDETERMINADO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PÁGINASENCATEGORÍA":"pagesincategory","PÁGINASENCAT":"pagesincategory","PAGSENCAT":"pagesincategory","PAGINASENCATEGORIA":"pagesincategory","PAGINASENCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMAÑOPÁGINA":"pagesize","TAMAÑODEPÁGINA":"pagesize","TAMAÑOPAGINA":"pagesize","TAMAÑODEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECCIÓN":"protectionlevel","NIVELDEPROTECCION":"protectionlevel","PROTECTIONLEVEL" +:"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMBREDEPAGINA":"pagename","NOMBREDEPÁGINA":"pagename","PAGENAME":"pagename","NOMBREDEPAGINAC":"pagenamee","NOMBREDEPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMBRECOMPLETODEPÁGINA":"fullpagename","NOMBRECOMPLETODEPAGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMBRECOMPLETODEPAGINAC":"fullpagenamee","NOMBRECOMPLETODEPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMBREDESUBPAGINA":"subpagename","NOMBREDESUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMBREDESUBPAGINAC":"subpagenamee","NOMBREDESUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMBREDEPAGINARAIZ":"rootpagename","NOMBREDEPÁGINARAÍZ":"rootpagename","ROOTPAGENAME":"rootpagename","NOMBREDEPAGINARAIZC":"rootpagenamee","NOMBREDEPÁGINARAÍZC":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBREDEPAGINABASE":"basepagename","NOMBREDEPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMBREDEPAGINABASEC": +"basepagenamee","NOMBREDEPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMBREDEPÁGINADEDISCUSIÓN":"talkpagename","NOMBREDEPAGINADEDISCUSION":"talkpagename","NOMBREDEPAGINADISCUSION":"talkpagename","NOMBREDEPÁGINADISCUSIÓN":"talkpagename","TALKPAGENAME":"talkpagename","NOMBREDEPÁGINADEDISCUSIÓNC":"talkpagenamee","NOMBREDEPAGINADEDISCUSIONC":"talkpagenamee","NOMBREDEPAGINADISCUSIONC":"talkpagenamee","NOMBREDEPÁGINADISCUSIÓNC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMBREDEPAGINADETEMA":"subjectpagename","NOMBREDEPÁGINADETEMA":"subjectpagename","NOMBREDEPÁGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEARTICULO":"subjectpagename","NOMBREDEPÁGINADEARTÍCULO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMBREDEPAGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEASUNTOC": +"subjectpagenamee","NOMBREDEPAGINADEARTICULOC":"subjectpagenamee","NOMBREDEPÁGINADEARTÍCULOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDEREVISION":"revisionid","IDREVISION":"revisionid","IDDEREVISIÓN":"revisionid","IDREVISIÓN":"revisionid","REVISIONID":"revisionid","DIADEREVISION":"revisionday","DIAREVISION":"revisionday","DÍADEREVISIÓN":"revisionday","DÍAREVISIÓN":"revisionday","REVISIONDAY":"revisionday","DIADEREVISION2":"revisionday2","DIAREVISION2":"revisionday2","DÍADEREVISIÓN2":"revisionday2","DÍAREVISIÓN2":"revisionday2","REVISIONDAY2":"revisionday2","MESDEREVISION":"revisionmonth","MESDEREVISIÓN":"revisionmonth","MESREVISION":"revisionmonth","MESREVISIÓN":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDEREVISION1":"revisionmonth1","MESDEREVISIÓN1":"revisionmonth1","MESREVISION1":"revisionmonth1","MESREVISIÓN1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","AÑODEREVISION":"revisionyear", +"AÑODEREVISIÓN":"revisionyear","AÑOREVISION":"revisionyear","AÑOREVISIÓN":"revisionyear","REVISIONYEAR":"revisionyear","MARCADEHORADEREVISION":"revisiontimestamp","MARCADEHORADEREVISIÓN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODEREVISION":"revisionuser","USUARIODEREVISIÓN":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACIODENOMBRE":"namespace","NAMESPACE":"namespace","ESPACIODENOMBREC":"namespacee","NAMESPACEE":"namespacee","NÚMERODELESPACIO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACIODEDISCUSION":"talkspace","ESPACIODEDISCUSIÓN":"talkspace","TALKSPACE":"talkspace","ESPACIODEDISCUSIONC":"talkspacee","TALKSPACEE":"talkspacee","ESPACIODEASUNTO":"subjectspace","ESPACIODETEMA":"subjectspace","ESPACIODEARTÍCULO":"subjectspace","ESPACIODEARTICULO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACIODETEMAC":"subjectspacee","ESPACIODEASUNTOC":"subjectspacee", +"ESPACIODEARTICULOC":"subjectspacee","ESPACIODEARTÍCULOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NÚMERODEARTÍCULOS":"numberofarticles","NUMERODEARTICULOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NÚMERODEARCHIVOS":"numberoffiles","NUMERODEARCHIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NÚMERODEPÁGINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NÚMEROADMINIISTRADORES":"numberofadmins","NÚMEROADMINS":"numberofadmins","NUMEROADMINS":"numberofadmins","NUMEROADMINISTRADORES":"numberofadmins","NUMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINS":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINS": +"numberofadmins","NUMBEROFADMINS":"numberofadmins","NÚMERODEEDICIONES":"numberofedits","NUMERODEEDICIONES":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRARTÍTULO":"displaytitle","MOSTRARTITULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESACTUAL":"currentmonth","MESACTUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MESACTUALCOMPLETO":"currentmonthname","NOMBREMESACTUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MESACTUALGENITIVO":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESACTUALABREVIADO":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DÍAACTUAL":"currentday","DIAACTUAL":"currentday","DÍA_ACTUAL":"currentday","DIA_ACTUAL":"currentday","CURRENTDAY":"currentday","DÍAACTUAL2":"currentday2","DIAACTUAL2":"currentday2","DÍA_ACTUAL2":"currentday2","DIA_ACTUAL2":"currentday2","CURRENTDAY2":"currentday2", +"NOMBREDÍAACTUAL":"currentdayname","NOMBREDIAACTUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","AÑOACTUAL":"currentyear","AÑO_ACTUAL":"currentyear","CURRENTYEAR":"currentyear","HORA_MINUTOS_ACTUAL":"currenttime","HORAMINUTOSACTUAL":"currenttime","TIEMPOACTUAL":"currenttime","CURRENTTIME":"currenttime","HORAACTUAL":"currenthour","HORA_ACTUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","MESLOCALCOMPLETO":"localmonthname","NOMBREMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","MESLOCALGENITIVO":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREVIADO":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DÍALOCAL":"localday","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","DÍALOCAL2":"localday2","LOCALDAY2":"localday2","NOMBREDIALOCAL":"localdayname", +"NOMBREDÍALOCAL":"localdayname","LOCALDAYNAME":"localdayname","AÑOLOCAL":"localyear","LOCALYEAR":"localyear","HORAMINUTOSLOCAL":"localtime","TIEMPOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMBREDELSITIO":"sitename","SITENAME":"sitename","SEMANAACTUAL":"currentweek","CURRENTWEEK":"currentweek","DDSACTUAL":"currentdow","DIADESEMANAACTUAL":"currentdow","DÍADESEMANAACTUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DDSLOCAL":"localdow","DIADESEMANALOCAL":"localdow","DÍADESEMANALOCAL":"localdow","LOCALDOW":"localdow","TAMAÑODEREVISIÓN":"revisionsize","TAMAÑODEREVISION":"revisionsize","REVISIONSIZE":"revisionsize","VERSIONACTUAL":"currentversion","VERSIÓNACTUAL":"currentversion","CURRENTVERSION":"currentversion","MARCADEHORAACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MARCADEHORALOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark" +,"DIRMARK":"directionmark","IDIOMADELCONTENIDO":"contentlanguage","IDIOMADELCONT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záéíóúñ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fa.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fa.json new file mode 100644 index 0000000..72c1aaa --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fa.json @@ -0,0 +1,16 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__بی‌فهرست__":"notoc","__notoc__":"notoc","__بی‌نگارخانه__":"nogallery","__nogallery__":"nogallery","__بافهرست__":"forcetoc","__forcetoc__":"forcetoc","__فهرست__":"toc","__toc__":"toc","__بی‌بخش__":"noeditsection","__noeditsection__":"noeditsection", +"__عنوان‌تبدیل‌نشده__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__محتواتبدیل‌نشده__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__بخش‌جدید__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__بی‌پیوندبخش__":"nonewsectionlink","__بی‌پیوند‌بخش‌جدید__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__رده‌پنهان__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__نمایه__":"index","__INDEX__":"index","__بی‌نمایه__":"noindex","__NOINDEX__":"noindex","__تغییرمسیرثابت__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"فن":"ns","ns":"ns","فنک":"nse","nse":"nse","کدنشانی": +"urlencode","urlencode":"urlencode","ابتداکوچک":"lcfirst","ابتدا_کوچک":"lcfirst","lcfirst":"lcfirst","ابتدابزرگ":"ucfirst","ابتدا_بزرگ":"ucfirst","ucfirst":"ucfirst","ک":"lc","lc":"lc","ب":"uc","uc":"uc","نشانی":"localurl","localurl":"localurl","نشانی‌کد":"localurle","نشانی_کد":"localurle","localurle":"localurle","نشانی‌کامل":"fullurl","نشانی_کامل":"fullurl","fullurl":"fullurl","نشانی‌کامل‌کد":"fullurle","نشانی_کامل_کد":"fullurle","fullurle":"fullurle","نشانی_استاندارد":"canonicalurl","نشانی‌استاندارد":"canonicalurl","canonicalurl":"canonicalurl","نشانی_استاندارد_کد":"canonicalurle","نشانی‌استانداردکد":"canonicalurle","canonicalurle":"canonicalurle","آرایش‌عدد":"formatnum","آرایش_عدد":"formatnum","formatnum":"formatnum","دستورزبان":"grammar","دستور_زبان":"grammar","grammar":"grammar", +"جنسیت":"gender","جنس":"gender","gender":"gender","جمع":"plural","plural":"plural","bidi":"bidi","#زبان":"language","#language":"language","لبه‌چپ":"padleft","لبه_چپ":"padleft","padleft":"padleft","لبه‌راست":"padright","لبه_راست":"padright","padright":"padright","کدلنگر":"anchorencode","anchorencode":"anchorencode","مسیرپرونده":"filepath","مسیر_پرونده":"filepath","filepath":"filepath","شناسه_صفحه":"pageid","pageid":"pageid","ترجمه":"int","int":"int","#ویژه":"special","#special":"special","#ویژه_ای":"speciale","#speciale":"speciale","#برچسب":"tag","#tag":"tag","#آرایش‌تاریخ":"formatdate","#آرایش_تاریخ":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","pendingchangelevel":"pendingchangelevel","#هدف":"target","#target":"target","#بابل":"babel","#babel":"babel","#coordinates":"coordinates","#درخواست":"invoke","#invoke":"invoke","#related": +"related","#اگر":"if","#if":"if","#اگرمساوی":"ifeq","#ifeq":"ifeq","#گزینه":"switch","#switch":"switch","#اگرموجود":"ifexist","#ifexist":"ifexist","#اگرحساب":"ifexpr","#ifexpr":"ifexpr","#اگرخطا":"iferror","#iferror":"iferror","#زمان":"time","#time":"time","#زمان‌بلند":"timel","#timel":"timel","#حساب":"expr","#expr":"expr","#نسبی‌به‌مطلق":"rel2abs","#rel2abs":"rel2abs","#پاره‌عنوان":"titleparts","#titleparts":"titleparts","#درخت‌رده":"categorytree","#درخت_رده":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#ویژگی":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","مسیرمقاله":"articlepath","مسیر_مقاله":"articlepath","articlepath":"articlepath","سرور": +"server","کارساز":"server","server":"server","نام‌کارساز":"servername","نام_کارساز":"servername","نام‌سرور":"servername","نام_سرور":"servername","servername":"servername","مسیرسند":"scriptpath","مسیر_سند":"scriptpath","scriptpath":"scriptpath","مسیرسبک":"stylepath","مسیر_سبک":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"تعداددرگروه":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ترتیب":"defaultsort","ترتیب‌پیش‌فرض":"defaultsort","ترتیب_پیش_فرض":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","صفحه‌دررده":"pagesincategory","صفحه_در_رده":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","اندازه‌صفحه":"pagesize","اندازه_صفحه":"pagesize","PAGESIZE":"pagesize", +"سطح‌حفاطت":"protectionlevel","سطح_حفاظت":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","نام‌صفحه":"pagename","نام_صفحه":"pagename","PAGENAME":"pagename","نام‌صفحه‌کد":"pagenamee","نام_صفحه_کد":"pagenamee","PAGENAMEE":"pagenamee","نام‌کامل‌صفحه":"fullpagename","نام_کامل_صفحه":"fullpagename","FULLPAGENAME":"fullpagename","نام‌کامل‌صفحه‌کد":"fullpagenamee","نام_کامل_صفحه_کد":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","نام‌زیرصفحه":"subpagename","نام_زیرصفحه":"subpagename","SUBPAGENAME":"subpagename","نام‌زیرصفحه‌کد":"subpagenamee","نام_زیرصفحه_کد":"subpagenamee","SUBPAGENAMEE":"subpagenamee","نام_صفحه_ریشه":"rootpagename","ROOTPAGENAME":"rootpagename","نام_صفحه_ریشه_ای":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","نام‌صفحه‌مبنا": +"basepagename","نام_صفحه_مبنا":"basepagename","BASEPAGENAME":"basepagename","نام‌صفحه‌مبناکد":"basepagenamee","نام_صفحه_مبنا_کد":"basepagenamee","BASEPAGENAMEE":"basepagenamee","نام‌صفحه‌بحث":"talkpagename","نام_صفحه_بحث":"talkpagename","TALKPAGENAME":"talkpagename","نام‌صفحه‌بحث‌کد":"talkpagenamee","نام_صفحه_بحث_کد":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","نام‌صفحه‌موضوع":"subjectpagename","نام‌صفحه‌مقاله":"subjectpagename","نام_صفحه_موضوع":"subjectpagename","نام_صفحه_مقاله":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","نام‌صفحه‌موضوع‌کد":"subjectpagenamee","نام‌صفحه‌مقاله‌کد":"subjectpagenamee","نام_صفحه_موضوع_کد":"subjectpagenamee","نام_صفحه_مقاله_کد":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee", +"ARTICLEPAGENAMEE":"subjectpagenamee","نسخه":"revisionid","شماره‌نسخه":"revisionid","شماره_نسخه":"revisionid","REVISIONID":"revisionid","روزنسخه":"revisionday","روز_نسخه":"revisionday","REVISIONDAY":"revisionday","روزنسخه۲":"revisionday2","روز_نسخه۲":"revisionday2","روز_نسخه_۲":"revisionday2","REVISIONDAY2":"revisionday2","ماه‌نسخه":"revisionmonth","ماه_نسخه":"revisionmonth","REVISIONMONTH":"revisionmonth","ماه‌نسخه۱":"revisionmonth1","ماه_نسخه_۱":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","سال‌نسخه":"revisionyear","سال_نسخه":"revisionyear","REVISIONYEAR":"revisionyear","زمان‌یونیکسی‌نسخه":"revisiontimestamp","زمان‌نسخه":"revisiontimestamp","زمان_یونیکسی_نسخه":"revisiontimestamp","زمان_نسخه":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","کاربرنسخه":"revisionuser","کاربر_نسخه": +"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","فضای‌نام":"namespace","فضای_نام":"namespace","NAMESPACE":"namespace","فضای‌نام‌کد":"namespacee","فضای_نام_کد":"namespacee","NAMESPACEE":"namespacee","شماره_فضای_نام":"namespacenumber","شماره‌فضای‌نام":"namespacenumber","NAMESPACENUMBER":"namespacenumber","فضای‌بحث":"talkspace","فضای_بحث":"talkspace","TALKSPACE":"talkspace","فضای‌بحث‌کد":"talkspacee","فضای_بحث_کد":"talkspacee","TALKSPACEE":"talkspacee","فضای‌موضوع":"subjectspace","فضای‌مقاله":"subjectspace","فضای_موضوع":"subjectspace","فضای_مقاله":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","فضای‌موضوع‌کد":"subjectspacee","فضای‌مقاله‌کد":"subjectspacee","فضای_موضوع_کد":"subjectspacee","فضای_مقاله_کد":"subjectspacee","SUBJECTSPACEE" +:"subjectspacee","ARTICLESPACEE":"subjectspacee","تعدادمقاله‌ها":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","تعدادپرونده‌ها":"numberoffiles","NUMBEROFFILES":"numberoffiles","تعدادکاربران":"numberofusers","NUMBEROFUSERS":"numberofusers","کاربران‌فعال":"numberofactiveusers","کاربران_فعال":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","تعدادصفحه‌ها":"numberofpages","NUMBEROFPAGES":"numberofpages","تعدادمدیران":"numberofadmins","NUMBEROFADMINS":"numberofadmins","تعدادویرایش‌ها":"numberofedits","NUMBEROFEDITS":"numberofedits","عنوان‌ظاهری":"displaytitle","عنوان_ظاهری":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ماه":"currentmonth","ماه‌کنونی":"currentmonth","ماه_کنونی":"currentmonth","ماه‌کنونی۲":"currentmonth","ماه_کنونی۲":"currentmonth","CURRENTMONTH":"currentmonth", +"CURRENTMONTH2":"currentmonth","ماه۱":"currentmonth1","ماه‌کنونی۱":"currentmonth1","ماه_کنونی۱":"currentmonth1","CURRENTMONTH1":"currentmonth1","نام‌ماه":"currentmonthname","نام_ماه":"currentmonthname","نام‌ماه‌کنونی":"currentmonthname","نام_ماه_کنونی":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","نام‌ماه‌اضافه":"currentmonthnamegen","نام_ماه_اضافه":"currentmonthnamegen","نام‌ماه‌کنونی‌اضافه":"currentmonthnamegen","نام_ماه_کنونی_اضافه":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","مخفف‌نام‌ماه":"currentmonthabbrev","مخفف_نام_ماه":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","روز":"currentday","روزکنونی":"currentday","روز_کنونی":"currentday","CURRENTDAY":"currentday","روز۲":"currentday2","روز_۲":"currentday2","CURRENTDAY2":"currentday2","نام‌روز": +"currentdayname","نام_روز":"currentdayname","CURRENTDAYNAME":"currentdayname","سال":"currentyear","سال‌کنونی":"currentyear","سال_کنونی":"currentyear","CURRENTYEAR":"currentyear","زمان‌کنونی":"currenttime","زمان_کنونی":"currenttime","CURRENTTIME":"currenttime","ساعت":"currenthour","ساعت‌کنونی":"currenthour","ساعت_کنونی":"currenthour","CURRENTHOUR":"currenthour","ماه‌محلی":"localmonth","ماه_محلی":"localmonth","ماه‌محلی۲":"localmonth","ماه_محلی۲":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","ماه‌محلی۱":"localmonth1","ماه_محلی۱":"localmonth1","LOCALMONTH1":"localmonth1","نام‌ماه‌محلی":"localmonthname","نام_ماه_محلی":"localmonthname","LOCALMONTHNAME":"localmonthname","نام‌ماه‌محلی‌اضافه":"localmonthnamegen","نام_ماه_محلی_اضافه":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen", +"مخفف‌ماه‌محلی":"localmonthabbrev","مخفف_ماه_محلی":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","روزمحلی":"localday","روز_محلی":"localday","LOCALDAY":"localday","روزمحلی۲":"localday2","روز_محلی_۲":"localday2","LOCALDAY2":"localday2","نام‌روزمحلی":"localdayname","نام_روز_محلی":"localdayname","LOCALDAYNAME":"localdayname","سال‌محلی":"localyear","سال_محلی":"localyear","LOCALYEAR":"localyear","زمان‌محلی":"localtime","زمان_محلی":"localtime","LOCALTIME":"localtime","ساعت‌محلی":"localhour","ساعت_محلی":"localhour","LOCALHOUR":"localhour","نام‌وبگاه":"sitename","نام_وبگاه":"sitename","SITENAME":"sitename","هفته":"currentweek","CURRENTWEEK":"currentweek","روزهفته":"currentdow","روز_هفته":"currentdow","CURRENTDOW":"currentdow","هفته‌محلی":"localweek","هفته_محلی":"localweek","LOCALWEEK":"localweek", +"روزهفته‌محلی":"localdow","روز_هفته_محلی":"localdow","LOCALDOW":"localdow","اندازهٔ‌نسخه":"revisionsize","اندازهٔ_نسخه":"revisionsize","REVISIONSIZE":"revisionsize","نسخه‌کنونی":"currentversion","نسخه_کنونی":"currentversion","CURRENTVERSION":"currentversion","زمان‌یونیکسی":"currenttimestamp","زمان_یونیکسی":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","زمان‌یونیکسی‌محلی":"localtimestamp","زمان_یونیکسی_محلی":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","علامت‌جهت":"directionmark","علامت_جهت":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","زبان‌محتوا":"contentlanguage","زبان_محتوا":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیآأئؤة‌]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ff.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ff.json new file mode 100644 index 0000000..cd0b442 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ff.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙƁƊŊƝƳɓɗŋɲƴ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fi.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fi.json new file mode 100644 index 0000000..1e677cc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fi.json @@ -0,0 +1,9 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__eisisluett__":"notoc","__notoc__":"notoc","__nogallery__":"nogallery","__sisluettpakotus__":"forcetoc","__forcetoc__":"forcetoc","__sisällysluettelo__":"toc","__toc__":"toc","__eiosiomuokkausta__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert", +"__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__PIILOLUOKKA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__HAKUKONEKIELTO__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"na":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","paikallinenosoite":"localurl","localurl":"localurl","paikallinenosoitee":"localurle","localurle":"localurle","täysiosoite":"fullurl","fullurl":"fullurl","täysiosoitee":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","muotoileluku":"formatnum","formatnum":"formatnum","taivutus":"grammar","grammar":"grammar","sukupuoli": +"gender","gender":"gender","monikko":"plural","plural":"plural","bidi":"bidi","#kieli":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","tiedostopolku":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#ominaisuus":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor" +,"articlepath":"articlepath","palvelin":"server","server":"server","palvelinnimi":"servername","servername":"servername","skriptipolku":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","AAKKOSTUS":"defaultsort","OLETUSAAKKOSTUS":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SIVUKOKO":"pagesize","PAGESIZE":"pagesize","SUOJAUSTASO":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SIVUNIMI":"pagename","PAGENAME":"pagename","SIVUNIMIE":"pagenamee","PAGENAMEE":"pagenamee","KOKOSIVUNIMI":"fullpagename","FULLPAGENAME":"fullpagename","KOKOSIVUNIMIE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ALASIVUNIMI":"subpagename","SUBPAGENAME":"subpagename","ALASIVUNIMIE":"subpagenamee", +"SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","KANTASIVUNIMI":"basepagename","BASEPAGENAME":"basepagename","KANTASIVUNIMIE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","KESKUSTELUSIVUNIMI":"talkpagename","TALKPAGENAME":"talkpagename","KESKUSTELUSIVUNIMIE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","AIHESIVUNIMI":"subjectpagename","ARTIKKELISIVUNIMI":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","AIHESIVUNIMIE":"subjectpagenamee","ARTIKKELISIVUNIMIE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","VERSIOID":"revisionid","REVISIONID":"revisionid","VERSIOPÄIVÄ":"revisionday","REVISIONDAY":"revisionday","VERSIOPÄIVÄ2":"revisionday2","REVISIONDAY2":"revisionday2","VERSIOKUUKAUSI":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","VERSIOVUOSI":"revisionyear","REVISIONYEAR":"revisionyear","VERSIOAIKALEIMA": +"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NIMIAVARUUS":"namespace","NAMESPACE":"namespace","NIMIAVARUUSE":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","KESKUSTELUAVARUUS":"talkspace","TALKSPACE":"talkspace","KESKUSTELUAVARUUSE":"talkspacee","TALKSPACEE":"talkspacee","AIHEAVARUUS":"subjectspace","ARTIKKELIAVARUUS":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","AIHEAVARUUSE":"subjectspacee","ARTIKKELIAVARUUSE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKKELIMÄÄRÄ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","TIEDOSTOMÄÄRÄ":"numberoffiles","NUMBEROFFILES":"numberoffiles","KÄYTTÄJÄMÄÄRÄ":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SIVUMÄÄRÄ":"numberofpages","NUMBEROFPAGES":"numberofpages","YLLÄPITÄJÄMÄÄRÄ":"numberofadmins", +"NUMBEROFADMINS":"numberofadmins","MUOKKAUSMÄÄRÄ":"numberofedits","NUMBEROFEDITS":"numberofedits","NÄKYVÄOTSIKKO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","KULUVAKUU":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","KULUVAKUUNIMI":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","KULUVAKUUNIMIGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","KULUVAKUUNIMILYHYT":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","KULUVAPÄIVÄ":"currentday","CURRENTDAY":"currentday","KULUVAPÄIVÄ2":"currentday2","CURRENTDAY2":"currentday2","KULUVAPÄIVÄNIMI":"currentdayname","CURRENTDAYNAME":"currentdayname","KULUVAVUOSI":"currentyear","CURRENTYEAR":"currentyear","KULUVAAIKA":"currenttime","CURRENTTIME":"currenttime","KULUVATUNTI":"currenthour","CURRENTHOUR":"currenthour","PAIKALLINENKUU":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1": +"localmonth1","PAIKALLINENKUUNIMI":"localmonthname","LOCALMONTHNAME":"localmonthname","PAIKALLINENKUUNIMIGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","PAIKALLINENKUUNIMILYHYT":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","PAIKALLINENPÄIVÄ":"localday","LOCALDAY":"localday","PAIKALLINENPÄIVÄ2":"localday2","LOCALDAY2":"localday2","PAIKALLINENPÄIVÄNIMI":"localdayname","LOCALDAYNAME":"localdayname","PAIKALLINENVUOSI":"localyear","LOCALYEAR":"localyear","PAIKALLINENAIKA":"localtime","LOCALTIME":"localtime","PAIKALLINENTUNTI":"localhour","LOCALHOUR":"localhour","SIVUSTONIMI":"sitename","SITENAME":"sitename","KULUVAVIIKKO":"currentweek","CURRENTWEEK":"currentweek","KULUVAVIIKONPÄIVÄ":"currentdow","CURRENTDOW":"currentdow","PAIKALLINENVIIKKO":"localweek","LOCALWEEK":"localweek","PAIKALLINENVIIKONPÄIVÄ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","NYKYINENVERSIO":"currentversion","CURRENTVERSION":"currentversion","KULUVAAIKALEIMA": +"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","PAIKALLINENAIKALEIMA":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zäö]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fiu-vro.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fiu-vro.json new file mode 100644 index 0000000..9c42659 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fiu-vro.json @@ -0,0 +1,9 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__sisukorrata__":"notoc","__notoc__":"notoc","__galeriita__":"nogallery","__nogallery__":"nogallery","__sisukordees__":"forcetoc","__forcetoc__":"forcetoc","__sisukord__":"toc","__toc__":"toc","__alaosalingita__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__": +"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__UUEALAOSALINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__UUEALAOSALINGITA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__PEIDETUDKAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","INDEKSIGA":"index","__INDEX__":"index","INDEKSITA":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr1":"nse","nse":"nse","urlencode":"urlencode","esivt":"lcfirst","lcfirst":"lcfirst","esist":"ucfirst","ucfirst":"ucfirst","vt":"lc","lc":"lc","st":"uc","uc":"uc","kohalikurl":"localurl","localurl":"localurl","kohalikurl1":"localurle","localurle":"localurle","koguurl":"fullurl","fullurl":"fullurl","koguurl1":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle", +"arvuvormindus":"formatnum","formatnum":"formatnum","grammar":"grammar","sugu":"gender","gender":"gender","plural":"plural","bidi":"bidi","#keel":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","failitee":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#eri":"special","#special":"special","#eri1":"speciale","#speciale":"speciale","#tag":"tag","#kuupäevavormindus":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#kategooriapuu":"categorytree","#categorytree":"categorytree","#target":"target","#paabel":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth", +"välistekeelelinkideta":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#omadus":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","serverinimi":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"LEHEMÄÄR":"numberofpages","NUMBEROFPAGES":"numberofpages","KASUTAJAMÄÄR":"numberofusers","NUMBEROFUSERS":"numberofusers","TEGUSKASUTAJAMÄÄR":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ARTIKLIMÄÄR":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","FAILIMÄÄR":"numberoffiles","NUMBEROFFILES":"numberoffiles","ÜLEMAMÄÄR":"numberofadmins","NUMBEROFADMINS":"numberofadmins","KASUTAJAIDRÜHMAS":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","REDIGEERIMISMÄÄR":"numberofedits","NUMBEROFEDITS":"numberofedits", +"JÄRJESTA":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","LEHEKÜLGIKATEGOORIAS":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","KAITSETASE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NIMERUUM1":"namespacee","NAMESPACEE":"namespacee","NIMERUUMINUMBER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ARUTELUNIMERUUM":"talkspace","TALKSPACE":"talkspace","ARUTELUNIMERUUM1":"talkspacee","TALKSPACEE":"talkspacee","SISUNIMERUUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SISUNIMERUUM1":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","LEHEKÜLJENIMI":"pagename","PAGENAME":"pagename","LEHEKÜLJENIMI1":"pagenamee","PAGENAMEE":"pagenamee","KOGULEHEKÜLJENIMI":"fullpagename","FULLPAGENAME":"fullpagename","KOGULEHEKÜLJENIMI1":"fullpagenamee", +"FULLPAGENAMEE":"fullpagenamee","JUURLEHEKÜLJENIMI":"rootpagename","ROOTPAGENAME":"rootpagename","JUURLEHEKÜLJENIMI1":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NIMERUUMITANIMI":"basepagename","BASEPAGENAME":"basepagename","NIMERUUMITANIMI1":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ALAMLEHEKÜLJENIMI":"subpagename","SUBPAGENAME":"subpagename","ALAMLEHEKÜLJENIMI1":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ARUTELUNIMI":"talkpagename","TALKPAGENAME":"talkpagename","ARUTELUNIMI1":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NIMERUUM": +"namespace","NAMESPACE":"namespace","PEALKIRI":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","HETKEKUU":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","HETKEKUU1":"currentmonth1","CURRENTMONTH1":"currentmonth1","HETKEKUUNIMETUS":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","HETKEKUUOM":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","HETKEKUULÜH":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HETKEKUUPÄEV":"currentday","CURRENTDAY":"currentday","HETKEKUUPÄEV2":"currentday2","CURRENTDAY2":"currentday2","HETKENÄDALAPÄEV":"currentdayname","CURRENTDAYNAME":"currentdayname","HETKEAASTA":"currentyear","CURRENTYEAR":"currentyear","HETKEAEG":"currenttime","CURRENTTIME":"currenttime","HETKETUND":"currenthour","CURRENTHOUR":"currenthour","KOHALIKKUU":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","KOHALIKKUU1":"localmonth1","LOCALMONTH1":"localmonth1","KOHALIKKUUNIMETUS":"localmonthname", +"LOCALMONTHNAME":"localmonthname","KOHALIKKUUOM":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","KOHALIKKUULÜH":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","KOHALIKKUUPÄEV":"localday","LOCALDAY":"localday","KOHALIKKUUPÄEV2":"localday2","LOCALDAY2":"localday2","KOHALIKNÄDALAPÄEV":"localdayname","LOCALDAYNAME":"localdayname","KOHALIKAASTA":"localyear","LOCALYEAR":"localyear","KOHALIKAEG":"localtime","LOCALTIME":"localtime","KOHALIKTUND":"localhour","LOCALHOUR":"localhour","KOHANIMI":"sitename","SITENAME":"sitename","HETKENÄDAL":"currentweek","CURRENTWEEK":"currentweek","HETKENÄDALAPÄEV1":"currentdow","CURRENTDOW":"currentdow","KOHALIKNÄDAL":"localweek","LOCALWEEK":"localweek","KOHALIKNÄDALAPÄEV1":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","HETKEAJATEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","KOHALIKAJATEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK": +"directionmark","DIRMARK":"directionmark","VAIKEKEEL":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöõšüža-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fj.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fj.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fj.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fo.json new file mode 100644 index 0000000..bc57ede --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fo.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([áðíóúýæøa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fr.json new file mode 100644 index 0000000..65c713a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fr.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode", +"initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int", +"#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#assessment":"assessment","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks", +"#propriété":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGE": +"pagename","PAGENAME":"pagename","NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee", +"IDVERSION":"revisionid","REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX": +"subjectspacee","ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL": +"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-frp.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-frp.json new file mode 100644 index 0000000..5efce71 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-frp.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__nion_somèro__":"notoc","__niona_trâbla__":"notoc","__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__niona_galerie__":"nogallery","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forciér_lo_somèro__":"forcetoc","__forciér_la_trâbla__":"forcetoc","__forcersommaire__":"forcetoc", +"__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__somèro__":"toc","__trâbla__":"toc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sèccion_que_pôt_pas_étre_changiê__":"noeditsection","__sectionnoneditable__":"noeditsection","__noeditsection__":"noeditsection","__sen_convèrsion_de_titro__":"notitleconvert","__sencdt__":"notitleconvert","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sen_convèrsion_de_contegnu__":"nocontentconvert","__sencdc__":"nocontentconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIM_DE_NOVÈLA_SÈCCION__":"newsectionlink","__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NION_LIM_DE_NOVÈLA_SÈCCION__":"nonewsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__CATÈGORIE_CACHIÊ__":"hiddencat","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ENDÈXE__":"index","__INDEX__":"index","__NION_ENDÈXE__":"noindex","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRÈCCION_IMOBILA__":"staticredirect","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"èdn":"ns","espacen":"ns","ns":"ns","èdn_url":"nse","espacenx":"nse","nse":"nse","url_encodâ":"urlencode","encodeurl":"urlencode","urlencode":"urlencode","premiére_petiôta_lètra":"lcfirst","initminus":"lcfirst","lcfirst":"lcfirst","premiére_granta_lètra":"ucfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","petiôta_lètra":"lc","minus":"lc","lc":"lc","granta_lètra":"uc","majus":"uc","capit":"uc","uc":"uc","url_locala":"localurl","urllocale":"localurl","localurl":"localurl", +"url_locala_url":"localurle","urllocalex":"localurle","localurle":"localurle","url_complèta":"fullurl","urlcomplete":"fullurl","fullurl":"fullurl","url_complèta_url":"fullurle","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","format_nombro":"formatnum","formatnombre":"formatnum","formatnum":"formatnum","gramère":"grammar","grammaire":"grammar","grammar":"grammar","genro":"gender","genre":"gender","gender":"gender","plurâl":"plural","pluriel":"plural","plural":"plural","bidi":"bidi","#lengoua":"language","#langue":"language","#language":"language","borrâjo_a_gôche":"padleft","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","borrâjo_a_drêta":"padright","bourragedroite":"padright","bourredroite":"padright","padright":"padright","ancro_encodâ":"anchorencode","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin_d_accès": +"filepath","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","ent":"int","int":"int","#spèciâl":"special","#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balisa":"tag","#balise":"tag","#tag":"tag","#format_de_dâta":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#target":"target","#babél":"babel","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#lst":"lst", +"#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","chemin_de_l_articllo":"articlepath","cheminarticle":"articlepath","articlepath":"articlepath","sèrvor":"server","serveur":"server","server":"server","nom_du_sèrvor":"servername","nomserveur":"servername","servername":"servername","chemin_du_scripte":"scriptpath","cheminscript":"scriptpath","scriptpath":"scriptpath","chemin_du_stilo":"stylepath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBRO_DE_PÂGES":"numberofpages","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBRO_D_USANCIÉRS":"numberofusers","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBRO_D_USANCIÉRS_ACTIFS": +"numberofactiveusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBRO_D_ARTICLLOS":"numberofarticles","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBRO_DE_FICHIÉRS":"numberoffiles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBRO_D_ADMINS":"numberofadmins","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBRO_D_USANCIÉRS_DENS_LA_TROPA":"numberingroup","NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NOMBRO_DE_CHANGEMENTS":"numberofedits","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","CLLÂF_DE_TRI":"defaultsort","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PÂGES_DENS_LA_CATÈGORIE":"pagesincategory","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","TALYE_DE_LA_PÂGE":"pagesize","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVÉL_DE_PROTÈCCION":"protectionlevel","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ÈSPÂÇO_DE_NOMS_URL":"namespacee","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ÈSPÂÇO_DE_DISCUSSION":"talkspace","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ÈSPÂÇO_DE_DISCUSSION_URL":"talkspacee","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ÈSPÂÇO_DU_SUJÈT":"subjectspace","ÈSPÂÇO_DE_L_ARTICLLO":"subjectspace","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ÈSPÂÇO_DU_SUJÈT_URL":"subjectspacee","ÈSPÂÇO_DE_L_ARTICLLO_URL":"subjectspacee","ESPACESUJETX":"subjectspacee","ESPACEARTICLEX":"subjectspacee", +"SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOM_DE_LA_PÂGE":"pagename","NOMPAGE":"pagename","PAGENAME":"pagename","NOM_DE_LA_PÂGE_URL":"pagenamee","NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOM_COMPLÈT_DE_LA_PÂGE":"fullpagename","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOM_COMPLÈT_DE_LA_PÂGE_URL":"fullpagenamee","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOM_DE_LA_PÂGE_DE_BÂSA":"basepagename","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOM_DE_LA_PÂGE_DE_BÂSA_URL":"basepagenamee","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOM_DE_LA_SOT_PÂGE":"subpagename","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOM_DE_LA_SOT_PÂGE_URL":"subpagenamee","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOM_DE_LA_PÂGE_DE_DISCUSSION": +"talkpagename","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOM_DE_LA_PÂGE_DE_DISCUSSION_URL":"talkpagenamee","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOM_DE_LA_PÂGE_DU_SUJÈT":"subjectpagename","NOM_DE_LA_PÂGE_DE_L_ARTICLLO":"subjectpagename","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOM_DE_LA_PÂGE_DU_SUJÈT_URL":"subjectpagenamee","NOM_DE_LA_PÂGE_DE_L_ARTICLLO_URL":"subjectpagenamee","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","NUMERÔ_DE_LA_VÈRSION":"revisionid","IDVERSION":"revisionid","REVISIONID":"revisionid","JORN_DE_LA_VÈRSION":"revisionday","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JORN_DE_LA_VÈRSION_2":"revisionday2","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2", +"MÊS_DE_LA_VÈRSION":"revisionmonth","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MÊS_DE_LA_VÈRSION_1":"revisionmonth1","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","AN_DE_LA_VÈRSION":"revisionyear","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","DÂTA_ET_HORA_DE_LA_VÈRSION":"revisiontimestamp","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","USANCIÉR_DE_LA_VÈRSION":"revisionuser","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ÈSPÂÇO_DE_NOMS":"namespace","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","FÂRE_VÊRE_LO_TITRO":"displaytitle","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","MÊS_D_ORA":"currentmonth","MÊS_D_ORA_2":"currentmonth","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MÊS_D_ORA_1":"currentmonth1","MOIS1ACTUEL": +"currentmonth1","CURRENTMONTH1":"currentmonth1","NOM_DU_MÊS_D_ORA":"currentmonthname","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","GÈNITIF_DU_NOM_DU_MÊS_D_ORA":"currentmonthnamegen","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ABRÈV_DU_MÊS_D_ORA":"currentmonthabbrev","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JORN_D_ORA":"currentday","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JORN_D_ORA_2":"currentday2","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOM_DU_JORN_D_ORA":"currentdayname","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","AN_D_ORA":"currentyear","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORÈRO_D_ORA":"currenttime","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HORA_D_ORA":"currenthour","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MÊS_LOCAL":"localmonth", +"MÊS_LOCAL_2":"localmonth","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MÊS_LOCAL_1":"localmonth1","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOM_DU_MÊS_LOCAL":"localmonthname","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","GÈNITIF_DU_NOM_DU_MÊS_LOCAL":"localmonthnamegen","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABRÈV_DU_MÊS_LOCAL":"localmonthabbrev","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JORN_LOCAL":"localday","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JORN_LOCAL_2":"localday2","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOM_DU_JORN_LOCAL":"localdayname","NOMJOURLOCAL":"localdayname","LOCALDAYNAME":"localdayname","AN_LOCAL":"localyear","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORÈRO_LOCAL":"localtime","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","HORA_LOCALA": +"localhour","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOM_DU_SETO":"sitename","NOMSITE":"sitename","SITENAME":"sitename","SEMANA_D_ORA":"currentweek","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDS_D_ORA":"currentdow","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMANA_LOCALA":"localweek","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDS_LOCAL":"localdow","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VÈRSION_D_ORA":"currentversion","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","DÂTA_ET_HORA_D_ORA":"currenttimestamp","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","DÂTA_ET_HORA_LOCALA":"localtimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MÂRCA_DE_DIRÈCCION":"directionmark","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LENGOUA_DU_CONTEGNU":"contentlanguage", +"LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîœôû·’æäåāăëēïīòöōùü‘]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-frr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-frr.json new file mode 100644 index 0000000..85557c6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-frr.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"dynamicpagelist":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"pagelist":true,"pages":true,"pagequality":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__": +"forcetoc","__inhaltsverzeichnis__":"toc","__toc__":"toc","__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__":"noindex","__NOINDEX__":"noindex", +"__PERMANENTE_WEITERLEITUNG__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","lokale_url":"localurl","localurl":"localurl","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zahlenformat":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#sprache":"language","#language": +"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright":"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorienbaum":"categorytree","#kategoriebaum":"categorytree","#categorytree": +"categorytree","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx","#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE": +"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SEITENNAME":"pagename","PAGENAME":"pagename","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSSIONSSEITE":"talkpagename","DISK":"talkpagename","TALKPAGENAME":"talkpagename", +"DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","HAUPTSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR":"revisionyear","VERSIONSJAHR":"revisionyear","REVISIONYEAR": +"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","NAMENSRAUM":"namespace","NAMESPACE":"namespace","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DATEIANZAHL":"numberoffiles","NUMBEROFFILES":"numberoffiles", +"BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTIVE_BENUTZER":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JETZIGER_MONATSNAME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JETZIGER_KALENDERTAG":"currentday","JETZIGER_TAG":"currentday", +"CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2","JETZIGER_TAG_2":"currentday2","CURRENTDAY2":"currentday2","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV":"localmonthnamegen","LOKALER_MONATSNAME_GEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2","LOKALER_TAG_2":"localday2","LOCALDAY2": +"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME":"localdayname","LOKALES_JAHR":"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize","JETZIGE_VERSION":"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHALTSSPRACHE":"contentlanguage", +"CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zäöüßåāđē]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fur.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fur.json new file mode 100644 index 0000000..03b7a4a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fur.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag", +"#formatodata":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#alberocategorie":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath": +"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2", +"CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME" +:"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NOMESITO":"sitename","SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fy.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fy.json new file mode 100644 index 0000000..6880c38 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-fy.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#as":"if","#if":"if","#asgelyk":"ifeq","#ifeq":"ifeq","#skeakelje":"switch","#switch":"switch","#asbestiet":"ifexist","#ifexist":"ifexist","#asekspresje":"ifexpr","#ifexpr":"ifexpr","#asflater":"iferror","#iferror":"iferror","#tiid":"time","#time":"time","#tiidl":"timel","#timel":"timel","#ekspresje":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorybeam":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#eigenskip":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup", +"DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER": +"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour", +"LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàáèéìíòóùúâêîôûäëïöü]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ga.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ga.json new file mode 100644 index 0000000..d187a72 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ga.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__gancá__":"notoc","__notoc__":"notoc","__nogallery__":"nogallery","__cágachuair__":"forcetoc","__forcetoc__":"forcetoc","__cá__":"toc","__toc__":"toc","__ganmhírathrú__":"noeditsection","__noeditsection__":"noeditsection","__gantiontúnadteideal__":"notitleconvert","__gantt__":"notitleconvert","__notitleconvert__": +"notitleconvert","__notc__":"notitleconvert","__gantiontúnanábhair__":"nocontentconvert","__ganta__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"as":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","urláitiúil":"localurl","localurl":"localurl","urláitiúilb":"localurle","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","gramadach":"grammar","grammar":"grammar","gender":"gender","plural":"plural", +"bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","inmh":"int","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","freastalaí":"server","server":"server","ainmanfhreastalaí":"servername","servername": +"servername","scriptchosán":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","AINMANLGH":"pagename","PAGENAME":"pagename","AINMANLGHB":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE": +"subjectpagenamee","IDANLEASAITHE":"revisionid","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","AINMSPÁS":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","LÍONNANALT":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","LÍONNAGCOMHAD":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MÍLÁITHREACH": +"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","AINMNAMÍOSALÁITHREAÍ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","GINAINMNAMÍOSALÁITHREAÍ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","GIORRÚNAMÍOSALÁITHREAÍ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","LÁLÁITHREACH":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","AINMANLAELÁITHRIGH":"currentdayname","CURRENTDAYNAME":"currentdayname","BLIAINLÁITHREACH":"currentyear","CURRENTYEAR":"currentyear","AMLÁITHREACH":"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime", +"LOCALHOUR":"localhour","AINMANTSUÍMH":"sitename","SITENAME":"sitename","SEACHTAINLÁITHREACH":"currentweek","CURRENTWEEK":"currentweek","LÁLÁITHREACHNAS":"currentdow","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gag.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gag.json new file mode 100644 index 0000000..032f8d7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gag.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__içindekileryok__":"notoc","__notoc__":"notoc","__galeriyok__":"nogallery","__nogallery__":"nogallery","__içindekilerzorunlu__":"forcetoc","__forcetoc__":"forcetoc","__içindekiler__":"toc","__toc__":"toc","__değiştiryok__":"noeditsection","__düzenlemeyok__":"noeditsection","__noeditsection__":"noeditsection", +"__başlikdönüşümüyok__":"notitleconvert","__bdy__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__içerikdönüşümüyok__":"nocontentconvert","__idy__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__YENİBAŞLIKBAĞLANTISI__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__YENİBAŞLIKBAĞLANTISIYOK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__GİZLİKAT__":"hiddencat","__GİZLİKATEGORİ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__DİZİN__":"index","__ENDEKS__":"index","__INDEX__":"index","__DİZİNYOK__":"noindex","__ENDEKSYOK__":"noindex","__NOINDEX__":"noindex","__STATİKYÖNLENDİRME__":"staticredirect","__SABİTYÖNLENDİRME__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"aa":"ns","ab":"ns","ns":"ns","aau":"nse" +,"abu":"nse","nse":"nse","urlkodlama":"urlencode","urlencode":"urlencode","khilk":"lcfirst","lcfirst":"lcfirst","bhilk":"ucfirst","ucfirst":"ucfirst","kh":"lc","lc":"lc","bh":"uc","uc":"uc","yerelurl":"localurl","localurl":"localurl","yerelurlu":"localurle","localurle":"localurle","tamurl":"fullurl","fullurl":"fullurl","tamurlu":"fullurle","fullurle":"fullurle","kuralliurl":"canonicalurl","canonicalurl":"canonicalurl","kuralliurlu":"canonicalurle","canonicalurle":"canonicalurle","biçimnum":"formatnum","formatnum":"formatnum","dilbilgisi":"grammar","gramer":"grammar","grammar":"grammar","cinsiyet":"gender","gender":"gender","çoğul":"plural","plural":"plural","bidi":"bidi","#dil":"language","#lisan":"language","#language":"language","dolsol":"padleft","padleft":"padleft","dolsağ":"padright","padright":"padright","çengelkodlama":"anchorencode","anchorencode":"anchorencode","dosyayolu":"filepath","dosya_yolu":"filepath","filepath":"filepath","sayfano":"pageid","pageid":"pageid","int": +"int","#özel":"special","#special":"special","#özelu":"speciale","#speciale":"speciale","#etiket":"tag","#tag":"tag","#biçimtarih":"formatdate","#tarihbiçimi":"formatdate","#formattarihi":"formatdate","#tarihformatı":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#kategorihiyerarşisi":"categorytree","#kategoriağacı":"categorytree","#ulamhiyerarşisi":"categorytree","#ulamağacı":"categorytree","#categorytree":"categorytree","#target":"target","#babil":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#eğer":"if","#eger":"if","#if":"if","#ifeq":"ifeq","#değiştir":"switch","#degistir":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#işlem":"expr","#islem":"expr","#ifade":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth" +,"noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","maddeyolu":"articlepath","articlepath":"articlepath","sunucu":"server","server":"server","sunucuadi":"servername","servername":"servername","betikyolu":"scriptpath","scriptpath":"scriptpath","biçemyolu":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"SAYFASAYISI":"numberofpages","NUMBEROFPAGES":"numberofpages","KULLANICISAYISI":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTİFKULLANICISAYISI":"numberofactiveusers","ETKİNKULLANICISAYISI":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","MADDESAYISI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DOSYASAYISI":"numberoffiles","NUMBEROFFILES":"numberoffiles","HİZMETLİSAYISI":"numberofadmins","NUMBEROFADMINS":"numberofadmins","GRUPTAKİSAYI":"numberingroup","GRUBUNSAYISI":"numberingroup","NUMBERINGROUP":"numberingroup" +,"NUMINGROUP":"numberingroup","DEĞİŞİKLİKSAYISI":"numberofedits","NUMBEROFEDITS":"numberofedits","VARSAYILANSIRALA":"defaultsort","VARSAYILANSIRALAMAANAHTARI":"defaultsort","VARSAYILANKATEGORİSIRALA":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","KATEGORİDEKİSAYFALAR":"pagesincategory","KATTAKİSAYFALAR":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SAYFABOYUTU":"pagesize","PAGESIZE":"pagesize","KORUMASEVİYESİ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ADALANIU":"namespacee","İSİMALANIU":"namespacee","NAMESPACEE":"namespacee","ADALANINUMARASI":"namespacenumber","NAMESPACENUMBER":"namespacenumber","TARTIŞMAALANI":"talkspace","TARTIŞMABOŞLUĞU":"talkspace","TALKSPACE":"talkspace","TARTIŞMAALANIU":"talkspacee","TARTIŞMABOŞLUĞUU":"talkspacee","TALKSPACEE":"talkspacee","KONUALANI":"subjectspace","MADDEALANI": +"subjectspace","KONUBOŞLUĞU":"subjectspace","MADDEBOŞLUĞU":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","KONUALANIU":"subjectspacee","MADDEALANIU":"subjectspacee","KONUBOŞLUĞUU":"subjectspacee","MADDEBOŞLUĞUU":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","SAYFAADI":"pagename","PAGENAME":"pagename","SAYFAADIU":"pagenamee","PAGENAMEE":"pagenamee","TAMSAYFAADI":"fullpagename","FULLPAGENAME":"fullpagename","TAMSAYFAADIU":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ÜSTSAYFAADI":"basepagename","BASEPAGENAME":"basepagename","ÜSTSAYFAADIU":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ALTSAYFAADI":"subpagename","SUBPAGENAME":"subpagename","ALTSAYFAADIU":"subpagenamee","SUBPAGENAMEE":"subpagenamee","TARTIŞMASAYFASIADI":"talkpagename","TALKPAGENAME":"talkpagename","TARTIŞMASAYFASIADIU":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","KONUSAYFASIADI": +"subjectpagename","MADDESAYFASIADI":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","KONUSAYFASIADIU":"subjectpagenamee","MADDESAYFASIADIU":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","SÜRÜMNU":"revisionid","SÜRÜMNO":"revisionid","REVİZYONNU":"revisionid","REVİZYONNO":"revisionid","REVISIONID":"revisionid","SÜRÜMGÜNÜ":"revisionday","REVISIONDAY":"revisionday","SÜRÜMGÜNÜ2":"revisionday2","REVISIONDAY2":"revisionday2","SÜRÜMAYI":"revisionmonth","REVISIONMONTH":"revisionmonth","SÜRÜMAYI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","SÜRÜMYILI":"revisionyear","REVISIONYEAR":"revisionyear","SÜRÜMZAMANBİLGİSİ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","SÜRÜMKULLANICI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ADALANI":"namespace","İSİMALANI":"namespace","NAMESPACE":"namespace","BAŞLIKGÖSTER": +"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","MEVCUTAY":"currentmonth","MEVCUTAY2":"currentmonth","GÜNCELAY":"currentmonth","GÜNCELAY2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MEVCUTAY1":"currentmonth1","GÜNCELAY1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MEVCUTAYADI":"currentmonthname","GÜNCELAYADI":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MEVCUTAYADIİYELİK":"currentmonthnamegen","GÜNCELAYADIİYELİK":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MEVCUTAYKISALTMASI":"currentmonthabbrev","GÜNCELAYKISALTMASI":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","MEVCUTGÜN":"currentday","GÜNCELGÜN":"currentday","CURRENTDAY":"currentday","MEVCUTGÜN2":"currentday2","GÜNCELGÜN2":"currentday2","CURRENTDAY2":"currentday2","MEVCUTGÜNADI":"currentdayname","GÜNCELGÜNADI":"currentdayname","CURRENTDAYNAME":"currentdayname","MEVCUTYIL":"currentyear","GÜNCELYIL":"currentyear", +"CURRENTYEAR":"currentyear","MEVCUTZAMAN":"currenttime","GÜNCELZAMAN":"currenttime","CURRENTTIME":"currenttime","MEVCUTSAAT":"currenthour","GÜNCELSAAT":"currenthour","CURRENTHOUR":"currenthour","YERELAY":"localmonth","YERELAY2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","YERELAY1":"localmonth1","LOCALMONTH1":"localmonth1","YERELAYADI":"localmonthname","LOCALMONTHNAME":"localmonthname","YERELAYADIİYELİK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","YERELAYKISALTMASI":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","YERELGÜN":"localday","LOCALDAY":"localday","YERELGÜN2":"localday2","LOCALDAY2":"localday2","YERELGÜNADI":"localdayname","LOCALDAYNAME":"localdayname","YERELYIL":"localyear","LOCALYEAR":"localyear","YERELZAMAN":"localtime","LOCALTIME":"localtime","YERELSAAT":"localhour","LOCALHOUR":"localhour","SİTEADI":"sitename","SITENAME":"sitename","MEVCUTHAFTA":"currentweek","GÜNCELHAFTA":"currentweek","CURRENTWEEK":"currentweek", +"MEVCUTHAFTANINGÜNÜ":"currentdow","GÜNCELHAFTANINGÜNÜ":"currentdow","CURRENTDOW":"currentdow","YERELHAFTA":"localweek","LOCALWEEK":"localweek","YERELHAFTANINGÜNÜ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","MEVCUTSÜRÜM":"currentversion","GÜNCELSÜRÜM":"currentversion","CURRENTVERSION":"currentversion","MEVCUTZAMANBİLGİSİ":"currenttimestamp","GÜNCELZAMANBİLGİSİ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","YERELZAMANBİLGİSİ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","YÖNİŞARETİ:":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","İÇERİKDİLİ":"contentlanguage","İÇERİKLİSANI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zÇĞçğİıÖöŞşÜüÂâÎîÛû]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gan.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gan.json new file mode 100644 index 0000000..8f21bcd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gan.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__無目錄__":"notoc","__无目录__":"notoc","__notoc__":"notoc","__無圖庫__":"nogallery","__无图库__":"nogallery","__nogallery__":"nogallery","__強制目錄__":"forcetoc","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__目錄__":"toc","__目录__":"toc","__toc__":"toc","__無段落編輯__": +"noeditsection","__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__不轉換標題__":"notitleconvert","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換內容__":"nocontentconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__新段落链接__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__无新段落链接__":"nonewsectionlink","__隱藏分類__":"hiddencat","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__索引__":"index","__NOINDEX__":"noindex","__无索引__":"noindex","__靜態重新導向__":"staticredirect","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__消除歧义__": +"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"命名空間":"ns","名字空间":"ns","ns":"ns","名称空间":"ns","命名空間e":"nse","名字空间e":"nse","nse":"nse","名称空间e":"nse","urlencode":"urlencode","url编码":"urlencode","lcfirst":"lcfirst","小写首字":"lcfirst","ucfirst":"ucfirst","大写首字":"ucfirst","lc":"lc","小写":"lc","uc":"uc","大写":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","fullurl":"fullurl","完整url":"fullurl","fullurle":"fullurle","完整url等同":"fullurle","canonicalurl":"canonicalurl","规范url":"canonicalurl","canonicalurle":"canonicalurle","规范url等同":"canonicalurle","formatnum":"formatnum","格式化数字":"formatnum","grammar":"grammar","语法":"grammar","性別":"gender","性":"gender","性别":"gender","gender":"gender","plural":"plural","复数":"plural","bidi":"bidi","#語言": +"language","#语言":"language","#language":"language","padleft":"padleft","左填充":"padleft","padright":"padright","右填充":"padright","anchorencode":"anchorencode","锚编码":"anchorencode","filepath":"filepath","文件路径":"filepath","頁面id":"pageid","页面id":"pageid","pageid":"pageid","int":"int","界面":"int","#special":"special","#特殊":"special","#speciale":"speciale","#特殊等同":"speciale","#tag":"tag","#标记":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#格式化日期":"formatdate","#日期格式化":"formatdate","#目標":"target","#目标":"target","#target":"target","#巴別":"babel","#巴别":"babel","#babel":"babel","#coordinates":"coordinates","#調動":"invoke","#调用":"invoke","#invoke":"invoke","#related":"related","#若":"if","#非空式":"if","#如果":"if","#if":"if","#相同式":"ifeq","#匹配式":"ifeq","#若相等":"ifeq","#如果相等":"ifeq","#ifeq":"ifeq","#轉換":"switch","#多选式":"switch","#多条件式": +"switch","#双射式":"switch","#开关":"switch","#转换":"switch","#switch":"switch","#存在式":"ifexist","#若有":"ifexist","#如有":"ifexist","#ifexist":"ifexist","#若表達式":"ifexpr","#若表达式":"ifexpr","#ifexpr":"ifexpr","#如果錯誤":"iferror","#错误式":"iferror","#如果错误":"iferror","#iferror":"iferror","#時間":"time","#时间":"time","#time":"time","#時間l":"timel","#时间l":"timel","#timel":"timel","#表達式":"expr","#计算式":"expr","#表达式":"expr","#expr":"expr","#rel2abs":"rel2abs","#标题组成部分":"titleparts","#titleparts":"titleparts","#分類樹":"categorytree","#分类树":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","隱藏跨語言連結":"noexternallanglinks","无外部语言连接":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#屬性":"property","#属性":"property","#property":"property","#statements" +:"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","条目路径":"articlepath","伺服器":"server","服务器":"server","server":"server","伺服器名稱":"servername","服务器名":"servername","servername":"servername","scriptpath":"scriptpath","脚本路径":"scriptpath","stylepath":"stylepath","样式路径":"stylepath","numberofwikis":"numberofwikis","wb報表名稱":"wbreponame","wb报告名":"wbreponame","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","组中用户数":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","默认排序":"defaultsort","默认排序关键字":"defaultsort","默认分类排序":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","分类中页面数":"pagesincategory","PAGESIZE":"pagesize","页面大小":"pagesize","PROTECTIONLEVEL":"protectionlevel","保护级别": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","頁面名稱":"pagename","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","页面名等同":"pagenamee","页面名称等同":"pagenamee","FULLPAGENAME":"fullpagename","页面全称":"fullpagename","完整页面名称":"fullpagename","FULLPAGENAMEE":"fullpagenamee","完整页面名称等同":"fullpagenamee","SUBPAGENAME":"subpagename","子页面名称":"subpagename","SUBPAGENAMEE":"subpagenamee","子页面名称等同":"subpagenamee","根頁面名稱":"rootpagename","ROOTPAGENAME":"rootpagename","根页面名称":"rootpagename","根頁面名稱E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","根页面名称等同":"rootpagenamee","BASEPAGENAME":"basepagename","基础页面名称":"basepagename","BASEPAGENAMEE":"basepagenamee","基础页面名称等同":"basepagenamee","TALKPAGENAME":"talkpagename","讨论页面名称":"talkpagename","对话页面名称": +"talkpagename","TALKPAGENAMEE":"talkpagenamee","讨论页面名称等同":"talkpagenamee","对话页面名称等同":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主名字空间页面名称":"subjectpagename","条目页面名称":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","主名字空间页面名称等同":"subjectpagenamee","条目页面名称等同":"subjectpagenamee","REVISIONID":"revisionid","修订ID":"revisionid","REVISIONDAY":"revisionday","修订日":"revisionday","REVISIONDAY2":"revisionday2","修订日2":"revisionday2","REVISIONMONTH":"revisionmonth","修订月":"revisionmonth","REVISIONMONTH1":"revisionmonth1","修订月1":"revisionmonth1","REVISIONYEAR":"revisionyear","修订年":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","修订时间戳":"revisiontimestamp","修訂使用者":"revisionuser","REVISIONUSER":"revisionuser","修订用户":"revisionuser", +"CASCADINGSOURCES":"cascadingsources","级联来源":"cascadingsources","命名空間":"namespace","名字空间":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","名字空间等同":"namespacee","命名空間數":"namespacenumber","名字空间编号":"namespacenumber","NAMESPACENUMBER":"namespacenumber","對話空間":"talkspace","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","讨论空间等同":"talkspacee","讨论名字空间等同":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主名字空间":"subjectspace","条目名字空间":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","主名字空间等同":"subjectspacee","条目名字空间等同":"subjectspacee","文章數":"numberofarticles","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","檔案數":"numberoffiles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles", +"使用者人數量":"numberofusers","用户数":"numberofusers","NUMBEROFUSERS":"numberofusers","活躍使用者人數":"numberofactiveusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","頁面數":"numberofpages","页面数":"numberofpages","NUMBEROFPAGES":"numberofpages","管理員數":"numberofadmins","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","编辑数":"numberofedits","顯示標題":"displaytitle","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","本月1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","本月名":"currentmonthname","本月名称":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月名属格":"currentmonthnamegen","本月名称属格":"currentmonthnamegen","本月縮寫": +"currentmonthabbrev","本月简称":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","今天2":"currentday2","CURRENTDAYNAME":"currentdayname","星期":"currentdayname","今天名":"currentdayname","今天名称":"currentdayname","CURRENTYEAR":"currentyear","今年":"currentyear","目前時間":"currenttime","当前时间":"currenttime","此时":"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","当前小时":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","本地月":"localmonth","本地月2":"localmonth","LOCALMONTH1":"localmonth1","本地月1":"localmonth1","LOCALMONTHNAME":"localmonthname","本地月份名":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","本地月历":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","本地月缩写":"localmonthabbrev","LOCALDAY":"localday","本地日":"localday","LOCALDAY2":"localday2","本地日2": +"localday2","LOCALDAYNAME":"localdayname","本地日名":"localdayname","LOCALYEAR":"localyear","本地年":"localyear","LOCALTIME":"localtime","本地时间":"localtime","LOCALHOUR":"localhour","本地小时":"localhour","網站名稱":"sitename","站点名称":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","本周":"currentweek","CURRENTDOW":"currentdow","当前DOW":"currentdow","LOCALWEEK":"localweek","本地周":"localweek","LOCALDOW":"localdow","本地DOW":"localdow","REVISIONSIZE":"revisionsize","修订大小":"revisionsize","目前版本":"currentversion","当前版本":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","当前时间戳":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","本地时间戳":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","方向标记":"directionmark","內容語言":"contentlanguage","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^()(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gcr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gcr.json new file mode 100644 index 0000000..620e0f2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gcr.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode","initminus":"lcfirst","lcfirst":"lcfirst","initmajus": +"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int","#spécial":"special","#special":"special","#spéciale": +"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property","#property":"property","#statements":"statements", +"#commaseparatedlist":"commaSeparatedList","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","CLEFDETRI":"defaultsort", +"CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee","ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMPAGE":"pagename","PAGENAME":"pagename","NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename", +"FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid","REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday", +"REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev", +"JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL":"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL": +"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gd.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gd.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gd.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gl.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gl.json new file mode 100644 index 0000000..fe062f6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gl.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__seníndice__":"notoc","__semtdc__":"notoc","__semsumário__":"notoc","__notoc__":"notoc","__sengalería__":"nogallery","__semgaleria__":"nogallery","__nogallery__":"nogallery","__forzaroíndice__":"forcetoc","__forcartdc__":"forcetoc","__forcarsumario__":"forcetoc","__forçartdc__":"forcetoc","__forçarsumário__": +"forcetoc","__forcetoc__":"forcetoc","__índice__":"toc","__tdc__":"toc","__sumário__":"toc","__sumario__":"toc","__toc__":"toc","__secciónsnoneditables__":"noeditsection","__nãoeditarseção__":"noeditsection","__semeditarseção__":"noeditsection","__naoeditarsecao__":"noeditsection","__semeditarsecao__":"noeditsection","__noeditsection__":"noeditsection","__semconvertertitulo__":"notitleconvert","__semconvertertítulo__":"notitleconvert","__semct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__semconverterconteudo__":"nocontentconvert","__semconverterconteúdo__":"nocontentconvert","__semcc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIGAZÓNDANOVASECCIÓN__":"newsectionlink","__LINKDENOVASECAO__":"newsectionlink","__LINKDENOVASEÇÃO__":"newsectionlink","__LIGACAODENOVASECAO__":"newsectionlink","__LIGAÇÃODENOVASEÇÃO__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink", +"__SEMLINKDENOVASECAO__":"nonewsectionlink","__SEMLINKDENOVASEÇÃO__":"nonewsectionlink","__SEMLIGACAODENOVASECAO__":"nonewsectionlink","__SEMLIGAÇÃODENOVASEÇÃO__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATEGORÍAOCULTA__":"hiddencat","__CATEGORIAOCULTA__":"hiddencat","__CATOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NONINDEXAR__":"noindex","__NAOINDEXAR__":"noindex","__NÃOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECCIÓNESTÁTICA__":"staticredirect","__REDIRECCIONESTATICA__":"staticredirect","__REDIRECIONAMENTOESTATICO__":"staticredirect","__REDIRECIONAMENTOESTÁTICO__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__HOMONIMOS__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","codificaurl":"urlencode", +"urlencode":"urlencode","primeiraminúscula":"lcfirst","primeiraminuscula":"lcfirst","lcfirst":"lcfirst","primeiramaiúscula":"ucfirst","primeiramaiuscula":"ucfirst","ucfirst":"ucfirst","minúscula":"lc","minuscula":"lc","minusculas":"lc","minúsculas":"lc","lc":"lc","maiúscula":"uc","maiuscula":"uc","maiusculas":"uc","maiúsculas":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","localurle":"localurle","urlcompleto":"fullurl","fullurl":"fullurl","urlcompletoc":"fullurle","fullurle":"fullurle","urlcanónico":"canonicalurl","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","gramática":"grammar","grammar":"grammar","sexo":"gender","genero":"gender","gênero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#lingua":"language","#idioma":"language","#language":"language","padleft":"padleft","padright":"padright","codificaancora":"anchorencode","codificaâncora":"anchorencode","anchorencode":"anchorencode","caminhodoarquivo": +"filepath","filepath":"filepath","iddapáxina":"pageid","pageid":"pageid","int":"int","#especial":"special","#special":"special","#speciale":"speciale","#etiqueta":"tag","#tag":"tag","#formatodadata":"formatdate","#formateardata":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#chamar":"invoke","#invoke":"invoke","#related":"related","pagebanner":"PAGEBANNER","#se":"if","#if":"if","#seigual":"ifeq","#ifeq":"ifeq","#switch":"switch","#seexiste":"ifexist","#ifexist":"ifexist","#seexpr":"ifexpr","#ifexpr":"ifexpr","#seerro":"iferror","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#partesdotítulo":"titleparts","#partesdotitulo":"titleparts","#titleparts":"titleparts","#árboredecategorías":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#trecho":"lst","#lstx":"lstx","#section-x":"lstx","#trecho-x":"lstx","#lsth":"lsth","#section-h": +"lsth","noexternallanglinks":"noexternallanglinks","#propriedade":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","rutadoartigo":"articlepath","articlepath":"articlepath","servidor":"server","server":"server","nomedoservidor":"servername","servername":"servername","rutadaescritura":"scriptpath","caminhodoscript":"scriptpath","scriptpath":"scriptpath","rutadoestilo":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NÚMEROENGRUPO":"numberingroup","NUMEROENGRUPO":"numberingroup","NUMERONOGRUPO":"numberingroup","NÚMERONOGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENAR":"defaultsort","ORDENACAOPADRAO":"defaultsort","ORDENAÇÃOPADRÃO":"defaultsort","ORDEMPADRAO":"defaultsort","ORDEMPADRÃO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PÁXINASNACATEGORÍA": +"pagesincategory","PAXINASNACATEGORIA":"pagesincategory","PAGINASNACATEGORIA":"pagesincategory","PÁGINASNACATEGORIA":"pagesincategory","PAGINASNACAT":"pagesincategory","PÁGINASNACAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMAÑODAPÁXINA":"pagesize","TAMAÑODAPAXINA":"pagesize","TAMANHODAPAGINA":"pagesize","TAMANHODAPÁGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECCIÓN":"protectionlevel","NIVELDEPROTECCION":"protectionlevel","NIVELDEPROTECAO":"protectionlevel","NÍVELDEPROTEÇÃO":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMEDAPÁXINA":"pagename","NOMEDAPAGINA":"pagename","NOMEDAPÁGINA":"pagename","PAGENAME":"pagename","NOMEDAPAGINAC":"pagenamee","NOMEDAPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMECOMPLETODAPÁXINA":"fullpagename","NOMECOMPLETODAPAGINA":"fullpagename","NOMECOMPLETODAPÁGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMECOMPLETODAPAGINAC": +"fullpagenamee","NOMECOMPLETODAPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMEDASUBPÁXINA":"subpagename","NOMEDASUBPAGINA":"subpagename","NOMEDASUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMEDASUBPAGINAC":"subpagenamee","NOMEDASUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMEDAPÁXINARAÍZ":"rootpagename","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","NOMEDAPÁXINABASE":"basepagename","NOMEDAPAGINABASE":"basepagename","NOMEDAPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMEDAPAGINABASEC":"basepagenamee","NOMEDAPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMEDAPÁXINADECONVERSA":"talkpagename","NOMEDAPAGINADEDISCUSSAO":"talkpagename","NOMEDAPÁGINADEDISCUSSÃO":"talkpagename","TALKPAGENAME":"talkpagename","NOMEDAPAGINADEDISCUSSAOC":"talkpagenamee","NOMEDAPÁGINADEDISCUSSÃOC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMEDAPÁXINADECONTIDO":"subjectpagename","NOMEDAPAGINADECONTEUDO": +"subjectpagename","NOMEDAPÁGINADECONTEÚDO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMEDAPAGINADECONTEUDOC":"subjectpagenamee","NOMEDAPÁGINADECONTEÚDOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDAREVISIÓN":"revisionid","IDDAREVISAO":"revisionid","IDDAREVISÃO":"revisionid","REVISIONID":"revisionid","DÍADAREVISIÓN":"revisionday","DIADAREVISAO":"revisionday","DIADAREVISÃO":"revisionday","REVISIONDAY":"revisionday","DÍADAREVISIÓN2":"revisionday2","DIADAREVISAO2":"revisionday2","DIADAREVISÃO2":"revisionday2","REVISIONDAY2":"revisionday2","MESDAREVISIÓN":"revisionmonth","MESDAREVISAO":"revisionmonth","MÊSDAREVISÃO":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDAREVISIÓN1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANODAREVISIÓN":"revisionyear","ANODAREVISAO":"revisionyear","ANODAREVISÃO":"revisionyear","REVISIONYEAR":"revisionyear", +"DATAEHORADAREVISIÓN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODAREVISIÓN":"revisionuser","USUARIODAREVISAO":"revisionuser","USUÁRIODAREVISÃO":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPAZODENOMES":"namespace","DOMINIO":"namespace","DOMÍNIO":"namespace","ESPACONOMINAL":"namespace","ESPAÇONOMINAL":"namespace","NAMESPACE":"namespace","DOMINIOC":"namespacee","DOMÍNIOC":"namespacee","ESPACONOMINALC":"namespacee","ESPAÇONOMINALC":"namespacee","NAMESPACEE":"namespacee","NÚMERODOESPAZODENOMES":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPAZODECONVERSA":"talkspace","PAGINADEDISCUSSAO":"talkspace","PÁGINADEDISCUSSÃO":"talkspace","TALKSPACE":"talkspace","PAGINADEDISCUSSAOC":"talkspacee","PÁGINADEDISCUSSÃOC":"talkspacee","TALKSPACEE":"talkspacee","ESPAZODECONTIDO":"subjectspace","PAGINADECONTEUDO":"subjectspace","PAGINADECONTEÚDO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","PAGINADECONTEUDOC":"subjectspacee","PAGINADECONTEÚDOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NÚMERODEARTIGOS":"numberofarticles","NUMERODEARTIGOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NÚMERODEFICHEIROS":"numberoffiles","NUMERODEARQUIVOS":"numberoffiles","NÚMERODEARQUIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","NÚMERODEUSUÁRIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSATIVOS":"numberofactiveusers","NÚMERODEUSUÁRIOSATIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NÚMERODEPÁXINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","NÚMERODEPÁGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NÚMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINISTRADORES":"numberofadmins","NUMBEROFADMINS":"numberofadmins", +"NÚMERODEEDICIÓNS":"numberofedits","NUMERODEEDICOES":"numberofedits","NÚMERODEEDIÇÕES":"numberofedits","NUMBEROFEDITS":"numberofedits","AMOSAROTÍTULO":"displaytitle","MOSTRAROTÍTULO":"displaytitle","EXIBETITULO":"displaytitle","EXIBETÍTULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESACTUAL":"currentmonth","MESACTUAL2":"currentmonth","MESATUAL":"currentmonth","MESATUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL1":"currentmonth1","MESATUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMEDOMESACTUAL":"currentmonthname","NOMEDOMESATUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ABREVIATURADOMESACTUAL":"currentmonthabbrev","MESATUALABREV":"currentmonthabbrev","MESATUALABREVIADO":"currentmonthabbrev","ABREVIATURADOMESATUAL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DÍAACTUAL":"currentday","DIAATUAL":"currentday","CURRENTDAY": +"currentday","DÍAACTUAL2":"currentday2","DIAATUAL2":"currentday2","CURRENTDAY2":"currentday2","NOMEDODÍAACTUAL":"currentdayname","NOMEDODIAATUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANOACTUAL":"currentyear","ANOATUAL":"currentyear","CURRENTYEAR":"currentyear","DATAEHORAACTUAIS":"currenttime","HORARIOATUAL":"currenttime","CURRENTTIME":"currenttime","HORAACTUAL":"currenthour","HORAATUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEDOMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVIATURADOMESLOCAL":"localmonthabbrev","MESLOCALABREV":"localmonthabbrev","MESLOCALABREVIADO":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DÍALOCAL":"localday","DIALOCAL":"localday","LOCALDAY":"localday","DÍALOCAL2":"localday2","DIALOCAL2":"localday2","LOCALDAY2": +"localday2","NOMEDODÍALOCAL":"localdayname","NOMEDODIALOCAL":"localdayname","LOCALDAYNAME":"localdayname","ANOLOCAL":"localyear","LOCALYEAR":"localyear","DATAEHORALOCAIS":"localtime","HORARIOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMEDOSITIO":"sitename","NOMEDOSITE":"sitename","NOMEDOSÍTIO":"sitename","SITENAME":"sitename","SEMANAACTUAL":"currentweek","SEMANAATUAL":"currentweek","CURRENTWEEK":"currentweek","DIADASEMANAATUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DIADASEMANALOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIÓNACTUAL":"currentversion","REVISAOATUAL":"currentversion","REVISÃOATUAL":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LINGUADOCONTIDO":"contentlanguage","IDIOMADOCONTIDO":"contentlanguage", +"IDIOMADOCONTEUDO":"contentlanguage","IDIOMADOCONTEÚDO":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([áâãàéêẽçíòóôõq̃úüűũa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-glk.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-glk.json new file mode 100644 index 0000000..bc9cb45 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-glk.json @@ -0,0 +1,16 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__بی‌فهرست__":"notoc","__notoc__":"notoc","__بی‌نگارخانه__":"nogallery","__nogallery__":"nogallery","__بافهرست__":"forcetoc","__forcetoc__":"forcetoc","__فهرست__":"toc","__toc__":"toc","__بی‌بخش__":"noeditsection","__noeditsection__":"noeditsection", +"__عنوان‌تبدیل‌نشده__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__محتواتبدیل‌نشده__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__بخش‌جدید__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__بی‌پیوندبخش__":"nonewsectionlink","__بی‌پیوند‌بخش‌جدید__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__رده‌پنهان__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__نمایه__":"index","__INDEX__":"index","__بی‌نمایه__":"noindex","__NOINDEX__":"noindex","__تغییرمسیرثابت__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"فن":"ns","ns":"ns","فنک":"nse","nse":"nse","کدنشانی": +"urlencode","urlencode":"urlencode","ابتداکوچک":"lcfirst","ابتدا_کوچک":"lcfirst","lcfirst":"lcfirst","ابتدابزرگ":"ucfirst","ابتدا_بزرگ":"ucfirst","ucfirst":"ucfirst","ک":"lc","lc":"lc","ب":"uc","uc":"uc","نشانی":"localurl","localurl":"localurl","نشانی‌کد":"localurle","نشانی_کد":"localurle","localurle":"localurle","نشانی‌کامل":"fullurl","نشانی_کامل":"fullurl","fullurl":"fullurl","نشانی‌کامل‌کد":"fullurle","نشانی_کامل_کد":"fullurle","fullurle":"fullurle","نشانی_استاندارد":"canonicalurl","نشانی‌استاندارد":"canonicalurl","canonicalurl":"canonicalurl","نشانی_استاندارد_کد":"canonicalurle","نشانی‌استانداردکد":"canonicalurle","canonicalurle":"canonicalurle","آرایش‌عدد":"formatnum","آرایش_عدد":"formatnum","formatnum":"formatnum","دستورزبان":"grammar","دستور_زبان":"grammar","grammar":"grammar", +"جنسیت":"gender","جنس":"gender","gender":"gender","جمع":"plural","plural":"plural","bidi":"bidi","#زبان":"language","#language":"language","لبه‌چپ":"padleft","لبه_چپ":"padleft","padleft":"padleft","لبه‌راست":"padright","لبه_راست":"padright","padright":"padright","کدلنگر":"anchorencode","anchorencode":"anchorencode","مسیرپرونده":"filepath","مسیر_پرونده":"filepath","filepath":"filepath","شناسه_صفحه":"pageid","pageid":"pageid","ترجمه":"int","int":"int","#ویژه":"special","#special":"special","#ویژه_ای":"speciale","#speciale":"speciale","#برچسب":"tag","#tag":"tag","#آرایش‌تاریخ":"formatdate","#آرایش_تاریخ":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#هدف":"target","#target":"target","#بابل":"babel","#babel":"babel","#coordinates":"coordinates","#درخواست":"invoke","#invoke":"invoke","#related":"related","#اگر":"if","#if":"if", +"#اگرمساوی":"ifeq","#ifeq":"ifeq","#گزینه":"switch","#switch":"switch","#اگرموجود":"ifexist","#ifexist":"ifexist","#اگرحساب":"ifexpr","#ifexpr":"ifexpr","#اگرخطا":"iferror","#iferror":"iferror","#زمان":"time","#time":"time","#زمان‌بلند":"timel","#timel":"timel","#حساب":"expr","#expr":"expr","#نسبی‌به‌مطلق":"rel2abs","#rel2abs":"rel2abs","#پاره‌عنوان":"titleparts","#titleparts":"titleparts","#درخت‌رده":"categorytree","#درخت_رده":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#ویژگی":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","مسیرمقاله":"articlepath","مسیر_مقاله":"articlepath","articlepath":"articlepath","سرور":"server","کارساز":"server", +"server":"server","نام‌کارساز":"servername","نام_کارساز":"servername","نام‌سرور":"servername","نام_سرور":"servername","servername":"servername","مسیرسند":"scriptpath","مسیر_سند":"scriptpath","scriptpath":"scriptpath","مسیرسبک":"stylepath","مسیر_سبک":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"تعداددرگروه":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ترتیب":"defaultsort","ترتیب‌پیش‌فرض":"defaultsort","ترتیب_پیش_فرض":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","صفحه‌دررده":"pagesincategory","صفحه_در_رده":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","اندازه‌صفحه":"pagesize","اندازه_صفحه":"pagesize","PAGESIZE":"pagesize","سطح‌حفاطت": +"protectionlevel","سطح_حفاظت":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","نام‌صفحه":"pagename","نام_صفحه":"pagename","PAGENAME":"pagename","نام‌صفحه‌کد":"pagenamee","نام_صفحه_کد":"pagenamee","PAGENAMEE":"pagenamee","نام‌کامل‌صفحه":"fullpagename","نام_کامل_صفحه":"fullpagename","FULLPAGENAME":"fullpagename","نام‌کامل‌صفحه‌کد":"fullpagenamee","نام_کامل_صفحه_کد":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","نام‌زیرصفحه":"subpagename","نام_زیرصفحه":"subpagename","SUBPAGENAME":"subpagename","نام‌زیرصفحه‌کد":"subpagenamee","نام_زیرصفحه_کد":"subpagenamee","SUBPAGENAMEE":"subpagenamee","نام_صفحه_ریشه":"rootpagename","ROOTPAGENAME":"rootpagename","نام_صفحه_ریشه_ای":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","نام‌صفحه‌مبنا":"basepagename", +"نام_صفحه_مبنا":"basepagename","BASEPAGENAME":"basepagename","نام‌صفحه‌مبناکد":"basepagenamee","نام_صفحه_مبنا_کد":"basepagenamee","BASEPAGENAMEE":"basepagenamee","نام‌صفحه‌بحث":"talkpagename","نام_صفحه_بحث":"talkpagename","TALKPAGENAME":"talkpagename","نام‌صفحه‌بحث‌کد":"talkpagenamee","نام_صفحه_بحث_کد":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","نام‌صفحه‌موضوع":"subjectpagename","نام‌صفحه‌مقاله":"subjectpagename","نام_صفحه_موضوع":"subjectpagename","نام_صفحه_مقاله":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","نام‌صفحه‌موضوع‌کد":"subjectpagenamee","نام‌صفحه‌مقاله‌کد":"subjectpagenamee","نام_صفحه_موضوع_کد":"subjectpagenamee","نام_صفحه_مقاله_کد":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE": +"subjectpagenamee","نسخه":"revisionid","شماره‌نسخه":"revisionid","شماره_نسخه":"revisionid","REVISIONID":"revisionid","روزنسخه":"revisionday","روز_نسخه":"revisionday","REVISIONDAY":"revisionday","روزنسخه۲":"revisionday2","روز_نسخه۲":"revisionday2","روز_نسخه_۲":"revisionday2","REVISIONDAY2":"revisionday2","ماه‌نسخه":"revisionmonth","ماه_نسخه":"revisionmonth","REVISIONMONTH":"revisionmonth","ماه‌نسخه۱":"revisionmonth1","ماه_نسخه_۱":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","سال‌نسخه":"revisionyear","سال_نسخه":"revisionyear","REVISIONYEAR":"revisionyear","زمان‌یونیکسی‌نسخه":"revisiontimestamp","زمان‌نسخه":"revisiontimestamp","زمان_یونیکسی_نسخه":"revisiontimestamp","زمان_نسخه":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","کاربرنسخه":"revisionuser","کاربر_نسخه":"revisionuser", +"REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","فضای‌نام":"namespace","فضای_نام":"namespace","NAMESPACE":"namespace","فضای‌نام‌کد":"namespacee","فضای_نام_کد":"namespacee","NAMESPACEE":"namespacee","شماره_فضای_نام":"namespacenumber","شماره‌فضای‌نام":"namespacenumber","NAMESPACENUMBER":"namespacenumber","فضای‌بحث":"talkspace","فضای_بحث":"talkspace","TALKSPACE":"talkspace","فضای‌بحث‌کد":"talkspacee","فضای_بحث_کد":"talkspacee","TALKSPACEE":"talkspacee","فضای‌موضوع":"subjectspace","فضای‌مقاله":"subjectspace","فضای_موضوع":"subjectspace","فضای_مقاله":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","فضای‌موضوع‌کد":"subjectspacee","فضای‌مقاله‌کد":"subjectspacee","فضای_موضوع_کد":"subjectspacee","فضای_مقاله_کد":"subjectspacee","SUBJECTSPACEE": +"subjectspacee","ARTICLESPACEE":"subjectspacee","تعدادمقاله‌ها":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","تعدادپرونده‌ها":"numberoffiles","NUMBEROFFILES":"numberoffiles","تعدادکاربران":"numberofusers","NUMBEROFUSERS":"numberofusers","کاربران‌فعال":"numberofactiveusers","کاربران_فعال":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","تعدادصفحه‌ها":"numberofpages","NUMBEROFPAGES":"numberofpages","تعدادمدیران":"numberofadmins","NUMBEROFADMINS":"numberofadmins","تعدادویرایش‌ها":"numberofedits","NUMBEROFEDITS":"numberofedits","عنوان‌ظاهری":"displaytitle","عنوان_ظاهری":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ماه":"currentmonth","ماه‌کنونی":"currentmonth","ماه_کنونی":"currentmonth","ماه‌کنونی۲":"currentmonth","ماه_کنونی۲":"currentmonth","CURRENTMONTH":"currentmonth", +"CURRENTMONTH2":"currentmonth","ماه۱":"currentmonth1","ماه‌کنونی۱":"currentmonth1","ماه_کنونی۱":"currentmonth1","CURRENTMONTH1":"currentmonth1","نام‌ماه":"currentmonthname","نام_ماه":"currentmonthname","نام‌ماه‌کنونی":"currentmonthname","نام_ماه_کنونی":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","نام‌ماه‌اضافه":"currentmonthnamegen","نام_ماه_اضافه":"currentmonthnamegen","نام‌ماه‌کنونی‌اضافه":"currentmonthnamegen","نام_ماه_کنونی_اضافه":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","مخفف‌نام‌ماه":"currentmonthabbrev","مخفف_نام_ماه":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","روز":"currentday","روزکنونی":"currentday","روز_کنونی":"currentday","CURRENTDAY":"currentday","روز۲":"currentday2","روز_۲":"currentday2","CURRENTDAY2":"currentday2","نام‌روز": +"currentdayname","نام_روز":"currentdayname","CURRENTDAYNAME":"currentdayname","سال":"currentyear","سال‌کنونی":"currentyear","سال_کنونی":"currentyear","CURRENTYEAR":"currentyear","زمان‌کنونی":"currenttime","زمان_کنونی":"currenttime","CURRENTTIME":"currenttime","ساعت":"currenthour","ساعت‌کنونی":"currenthour","ساعت_کنونی":"currenthour","CURRENTHOUR":"currenthour","ماه‌محلی":"localmonth","ماه_محلی":"localmonth","ماه‌محلی۲":"localmonth","ماه_محلی۲":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","ماه‌محلی۱":"localmonth1","ماه_محلی۱":"localmonth1","LOCALMONTH1":"localmonth1","نام‌ماه‌محلی":"localmonthname","نام_ماه_محلی":"localmonthname","LOCALMONTHNAME":"localmonthname","نام‌ماه‌محلی‌اضافه":"localmonthnamegen","نام_ماه_محلی_اضافه":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen", +"مخفف‌ماه‌محلی":"localmonthabbrev","مخفف_ماه_محلی":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","روزمحلی":"localday","روز_محلی":"localday","LOCALDAY":"localday","روزمحلی۲":"localday2","روز_محلی_۲":"localday2","LOCALDAY2":"localday2","نام‌روزمحلی":"localdayname","نام_روز_محلی":"localdayname","LOCALDAYNAME":"localdayname","سال‌محلی":"localyear","سال_محلی":"localyear","LOCALYEAR":"localyear","زمان‌محلی":"localtime","زمان_محلی":"localtime","LOCALTIME":"localtime","ساعت‌محلی":"localhour","ساعت_محلی":"localhour","LOCALHOUR":"localhour","نام‌وبگاه":"sitename","نام_وبگاه":"sitename","SITENAME":"sitename","هفته":"currentweek","CURRENTWEEK":"currentweek","روزهفته":"currentdow","روز_هفته":"currentdow","CURRENTDOW":"currentdow","هفته‌محلی":"localweek","هفته_محلی":"localweek","LOCALWEEK":"localweek", +"روزهفته‌محلی":"localdow","روز_هفته_محلی":"localdow","LOCALDOW":"localdow","اندازهٔ‌نسخه":"revisionsize","اندازهٔ_نسخه":"revisionsize","REVISIONSIZE":"revisionsize","نسخه‌کنونی":"currentversion","نسخه_کنونی":"currentversion","CURRENTVERSION":"currentversion","زمان‌یونیکسی":"currenttimestamp","زمان_یونیکسی":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","زمان‌یونیکسی‌محلی":"localtimestamp","زمان_یونیکسی_محلی":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","علامت‌جهت":"directionmark","علامت_جهت":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","زبان‌محتوا":"contentlanguage","زبان_محتوا":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیآأئؤة‌]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gn.json new file mode 100644 index 0000000..968d03e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gn.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__sin_tdc__":"notoc","__notdc__":"notoc","__notoc__":"notoc","__sin_galería__":"nogallery","__nogalería__":"nogallery","__nogaleria__":"nogallery","__nogallery__":"nogallery","__forzar_tdc__":"forcetoc","__forzartdc__":"forcetoc","__forzartoc__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc","__toc__":"toc", +"__no_editar_sección__":"noeditsection","__noeditarsección__":"noeditsection","__noeditarseccion__":"noeditsection","__noeditsection__":"noeditsection","__noconvertirtitulo__":"notitleconvert","__noconvertirtítulo__":"notitleconvert","__noct___":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__noconvertircontenido__":"nocontentconvert","__nocc___":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__VINCULARANUEVASECCION__":"newsectionlink","__ENLACECREARSECCIÓN__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NOVINCULARANUEVASECCION__":"nonewsectionlink","__SINENLACECREARSECCIÓN__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATEGORÍAOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECCIÓNESTÁTICA__":"staticredirect", +"__REDIRECCIONESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIGUACION__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"en":"ns","ns":"ns","nse":"nse","codificarurl":"urlencode","urlencode":"urlencode","primerominus":"lcfirst","primerominús":"lcfirst","lcfirst":"lcfirst","primeromayus":"ucfirst","primeromayús":"ucfirst","ucfirst":"ucfirst","minus":"lc","minús":"lc","lc":"lc","mayus":"uc","mayús":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","urllocalc":"localurle","localurle":"localurle","urlcompleta":"fullurl","fullurl":"fullurl","urlcompletac":"fullurle","fullurle":"fullurle","urlcanonica":"canonicalurl","canonicalurl":"canonicalurl","urlcanonicac":"canonicalurle","canonicalurle":"canonicalurle","formatonúmero":"formatnum","formatonumero":"formatnum","formatnum":"formatnum","gramatica":"grammar","gramática":"grammar","grammar": +"grammar","género":"gender","genero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#idioma":"language","#language":"language","rellenarizquierda":"padleft","rellenarizq":"padleft","padleft":"padleft","rellenarderecha":"padright","rellenarder":"padright","padright":"padright","anchorencode":"anchorencode","rutaarchivo":"filepath","rutarchivo":"filepath","rutadearchivo":"filepath","filepath":"filepath","iddepágina":"pageid","idpágina":"pageid","iddepagina":"pageid","idpagina":"pageid","pageid":"pageid","int":"int","#especial":"special","#special":"special","#especialc":"speciale","#speciale":"speciale","#etiqueta":"tag","#tag":"tag","#formatodefecha":"formatdate","#formatearfecha":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#destino":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#invocar":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#siigual":"ifeq","#ifeq":"ifeq","#según":"switch","#switch" +:"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierror":"iferror","#iferror":"iferror","#tiempo":"time","#time":"time","#tiempol":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#árboldecategorías":"categorytree","#arboldecategorias":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","nointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propiedad":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","rutaartículo":"articlepath","rutaarticulo":"articlepath","articlepath":"articlepath","servidor":"server","server":"server","nombreservidor":"servername","servername":"servername","rutascript":"scriptpath","rutadescript":"scriptpath","scriptpath":"scriptpath","rutaestilo":"stylepath","rutadeestilo":"stylepath", +"stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NÚMEROENGRUPO":"numberingroup","NUMEROENGRUPO":"numberingroup","NUMENGRUPO":"numberingroup","NÚMENGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENAR":"defaultsort","CLAVEDEORDENPREDETERMINADO":"defaultsort","ORDENDECATEGORIAPREDETERMINADO":"defaultsort","ORDENDECATEGORÍAPREDETERMINADO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PÁGINASENCATEGORÍA":"pagesincategory","PÁGINASENCAT":"pagesincategory","PAGSENCAT":"pagesincategory","PAGINASENCATEGORIA":"pagesincategory","PAGINASENCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMAÑOPÁGINA":"pagesize","TAMAÑODEPÁGINA":"pagesize","TAMAÑOPAGINA":"pagesize","TAMAÑODEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECCIÓN":"protectionlevel","NIVELDEPROTECCION":"protectionlevel","PROTECTIONLEVEL" +:"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMBREDEPAGINA":"pagename","NOMBREDEPÁGINA":"pagename","PAGENAME":"pagename","NOMBREDEPAGINAC":"pagenamee","NOMBREDEPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMBRECOMPLETODEPÁGINA":"fullpagename","NOMBRECOMPLETODEPAGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMBRECOMPLETODEPAGINAC":"fullpagenamee","NOMBRECOMPLETODEPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMBREDESUBPAGINA":"subpagename","NOMBREDESUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMBREDESUBPAGINAC":"subpagenamee","NOMBREDESUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMBREDEPAGINARAIZ":"rootpagename","NOMBREDEPÁGINARAÍZ":"rootpagename","ROOTPAGENAME":"rootpagename","NOMBREDEPAGINARAIZC":"rootpagenamee","NOMBREDEPÁGINARAÍZC":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBREDEPAGINABASE":"basepagename","NOMBREDEPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMBREDEPAGINABASEC": +"basepagenamee","NOMBREDEPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMBREDEPÁGINADEDISCUSIÓN":"talkpagename","NOMBREDEPAGINADEDISCUSION":"talkpagename","NOMBREDEPAGINADISCUSION":"talkpagename","NOMBREDEPÁGINADISCUSIÓN":"talkpagename","TALKPAGENAME":"talkpagename","NOMBREDEPÁGINADEDISCUSIÓNC":"talkpagenamee","NOMBREDEPAGINADEDISCUSIONC":"talkpagenamee","NOMBREDEPAGINADISCUSIONC":"talkpagenamee","NOMBREDEPÁGINADISCUSIÓNC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMBREDEPAGINADETEMA":"subjectpagename","NOMBREDEPÁGINADETEMA":"subjectpagename","NOMBREDEPÁGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEARTICULO":"subjectpagename","NOMBREDEPÁGINADEARTÍCULO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMBREDEPAGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEASUNTOC": +"subjectpagenamee","NOMBREDEPAGINADEARTICULOC":"subjectpagenamee","NOMBREDEPÁGINADEARTÍCULOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDEREVISION":"revisionid","IDREVISION":"revisionid","IDDEREVISIÓN":"revisionid","IDREVISIÓN":"revisionid","REVISIONID":"revisionid","DIADEREVISION":"revisionday","DIAREVISION":"revisionday","DÍADEREVISIÓN":"revisionday","DÍAREVISIÓN":"revisionday","REVISIONDAY":"revisionday","DIADEREVISION2":"revisionday2","DIAREVISION2":"revisionday2","DÍADEREVISIÓN2":"revisionday2","DÍAREVISIÓN2":"revisionday2","REVISIONDAY2":"revisionday2","MESDEREVISION":"revisionmonth","MESDEREVISIÓN":"revisionmonth","MESREVISION":"revisionmonth","MESREVISIÓN":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDEREVISION1":"revisionmonth1","MESDEREVISIÓN1":"revisionmonth1","MESREVISION1":"revisionmonth1","MESREVISIÓN1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","AÑODEREVISION":"revisionyear", +"AÑODEREVISIÓN":"revisionyear","AÑOREVISION":"revisionyear","AÑOREVISIÓN":"revisionyear","REVISIONYEAR":"revisionyear","MARCADEHORADEREVISION":"revisiontimestamp","MARCADEHORADEREVISIÓN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODEREVISION":"revisionuser","USUARIODEREVISIÓN":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACIODENOMBRE":"namespace","NAMESPACE":"namespace","ESPACIODENOMBREC":"namespacee","NAMESPACEE":"namespacee","NÚMERODELESPACIO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACIODEDISCUSION":"talkspace","ESPACIODEDISCUSIÓN":"talkspace","TALKSPACE":"talkspace","ESPACIODEDISCUSIONC":"talkspacee","TALKSPACEE":"talkspacee","ESPACIODEASUNTO":"subjectspace","ESPACIODETEMA":"subjectspace","ESPACIODEARTÍCULO":"subjectspace","ESPACIODEARTICULO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACIODETEMAC":"subjectspacee","ESPACIODEASUNTOC":"subjectspacee", +"ESPACIODEARTICULOC":"subjectspacee","ESPACIODEARTÍCULOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NÚMERODEARTÍCULOS":"numberofarticles","NUMERODEARTICULOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NÚMERODEARCHIVOS":"numberoffiles","NUMERODEARCHIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NÚMERODEPÁGINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NÚMEROADMINIISTRADORES":"numberofadmins","NÚMEROADMINS":"numberofadmins","NUMEROADMINS":"numberofadmins","NUMEROADMINISTRADORES":"numberofadmins","NUMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINS":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINS": +"numberofadmins","NUMBEROFADMINS":"numberofadmins","NÚMERODEEDICIONES":"numberofedits","NUMERODEEDICIONES":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRARTÍTULO":"displaytitle","MOSTRARTITULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESACTUAL":"currentmonth","MESACTUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MESACTUALCOMPLETO":"currentmonthname","NOMBREMESACTUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MESACTUALGENITIVO":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESACTUALABREVIADO":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DÍAACTUAL":"currentday","DIAACTUAL":"currentday","DÍA_ACTUAL":"currentday","DIA_ACTUAL":"currentday","CURRENTDAY":"currentday","DÍAACTUAL2":"currentday2","DIAACTUAL2":"currentday2","DÍA_ACTUAL2":"currentday2","DIA_ACTUAL2":"currentday2","CURRENTDAY2":"currentday2", +"NOMBREDÍAACTUAL":"currentdayname","NOMBREDIAACTUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","AÑOACTUAL":"currentyear","AÑO_ACTUAL":"currentyear","CURRENTYEAR":"currentyear","HORA_MINUTOS_ACTUAL":"currenttime","HORAMINUTOSACTUAL":"currenttime","TIEMPOACTUAL":"currenttime","CURRENTTIME":"currenttime","HORAACTUAL":"currenthour","HORA_ACTUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","MESLOCALCOMPLETO":"localmonthname","NOMBREMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","MESLOCALGENITIVO":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREVIADO":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DÍALOCAL":"localday","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","DÍALOCAL2":"localday2","LOCALDAY2":"localday2","NOMBREDIALOCAL":"localdayname", +"NOMBREDÍALOCAL":"localdayname","LOCALDAYNAME":"localdayname","AÑOLOCAL":"localyear","LOCALYEAR":"localyear","HORAMINUTOSLOCAL":"localtime","TIEMPOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMBREDELSITIO":"sitename","SITENAME":"sitename","SEMANAACTUAL":"currentweek","CURRENTWEEK":"currentweek","DDSACTUAL":"currentdow","DIADESEMANAACTUAL":"currentdow","DÍADESEMANAACTUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DDSLOCAL":"localdow","DIADESEMANALOCAL":"localdow","DÍADESEMANALOCAL":"localdow","LOCALDOW":"localdow","TAMAÑODEREVISIÓN":"revisionsize","TAMAÑODEREVISION":"revisionsize","REVISIONSIZE":"revisionsize","VERSIONACTUAL":"currentversion","VERSIÓNACTUAL":"currentversion","CURRENTVERSION":"currentversion","MARCADEHORAACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MARCADEHORALOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark" +,"DIRMARK":"directionmark","IDIOMADELCONTENIDO":"contentlanguage","IDIOMADELCONT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záéíóúñ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gom.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gom.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gom.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gor.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gor.json new file mode 100644 index 0000000..04dbf3d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gor.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa": +"language","#bhs":"language","#language":"language","isikiri":"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1":"timel","#timel":"timel","#hitung":"expr", +"#expr":"expr","#rel2abs":"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","HALAMANDIKATEGORI":"pagesincategory","HALDIKAT": +"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee", +"NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE":"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid","HARIREVISI":"revisionday","HAREV":"revisionday" +,"REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace","RUANGNAMAE":"namespacee","RUNAME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace","RUTAMA":"subjectspace","RUTIKEL":"subjectspace", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","JUMLAHARTIKEL":"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits","NUMBEROFEDITS":"numberofedits","JUDULTAMPILAN":"displaytitle" +,"JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME":"currentdayname","TAHUNKINI":"currentyear","TAKIN":"currentyear", +"CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN":"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname","LOCALDAYNAME":"localdayname","TAHUNLOKAL":"localyear","TALOK":"localyear", +"LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK":"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","BAHASAISI":"contentlanguage", +"BHSISI":"contentlanguage","BASI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-got.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-got.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-got.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gu.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gu.json new file mode 100644 index 0000000..93190b2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gu.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([\\x{0A80}-\\x{0AFF}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gv.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gv.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-gv.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ha.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ha.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ha.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hak.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hak.json new file mode 100644 index 0000000..8f21bcd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hak.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__無目錄__":"notoc","__无目录__":"notoc","__notoc__":"notoc","__無圖庫__":"nogallery","__无图库__":"nogallery","__nogallery__":"nogallery","__強制目錄__":"forcetoc","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__目錄__":"toc","__目录__":"toc","__toc__":"toc","__無段落編輯__": +"noeditsection","__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__不轉換標題__":"notitleconvert","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換內容__":"nocontentconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__新段落链接__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__无新段落链接__":"nonewsectionlink","__隱藏分類__":"hiddencat","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__索引__":"index","__NOINDEX__":"noindex","__无索引__":"noindex","__靜態重新導向__":"staticredirect","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__消除歧义__": +"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"命名空間":"ns","名字空间":"ns","ns":"ns","名称空间":"ns","命名空間e":"nse","名字空间e":"nse","nse":"nse","名称空间e":"nse","urlencode":"urlencode","url编码":"urlencode","lcfirst":"lcfirst","小写首字":"lcfirst","ucfirst":"ucfirst","大写首字":"ucfirst","lc":"lc","小写":"lc","uc":"uc","大写":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","fullurl":"fullurl","完整url":"fullurl","fullurle":"fullurle","完整url等同":"fullurle","canonicalurl":"canonicalurl","规范url":"canonicalurl","canonicalurle":"canonicalurle","规范url等同":"canonicalurle","formatnum":"formatnum","格式化数字":"formatnum","grammar":"grammar","语法":"grammar","性別":"gender","性":"gender","性别":"gender","gender":"gender","plural":"plural","复数":"plural","bidi":"bidi","#語言": +"language","#语言":"language","#language":"language","padleft":"padleft","左填充":"padleft","padright":"padright","右填充":"padright","anchorencode":"anchorencode","锚编码":"anchorencode","filepath":"filepath","文件路径":"filepath","頁面id":"pageid","页面id":"pageid","pageid":"pageid","int":"int","界面":"int","#special":"special","#特殊":"special","#speciale":"speciale","#特殊等同":"speciale","#tag":"tag","#标记":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#格式化日期":"formatdate","#日期格式化":"formatdate","#目標":"target","#目标":"target","#target":"target","#巴別":"babel","#巴别":"babel","#babel":"babel","#coordinates":"coordinates","#調動":"invoke","#调用":"invoke","#invoke":"invoke","#related":"related","#若":"if","#非空式":"if","#如果":"if","#if":"if","#相同式":"ifeq","#匹配式":"ifeq","#若相等":"ifeq","#如果相等":"ifeq","#ifeq":"ifeq","#轉換":"switch","#多选式":"switch","#多条件式": +"switch","#双射式":"switch","#开关":"switch","#转换":"switch","#switch":"switch","#存在式":"ifexist","#若有":"ifexist","#如有":"ifexist","#ifexist":"ifexist","#若表達式":"ifexpr","#若表达式":"ifexpr","#ifexpr":"ifexpr","#如果錯誤":"iferror","#错误式":"iferror","#如果错误":"iferror","#iferror":"iferror","#時間":"time","#时间":"time","#time":"time","#時間l":"timel","#时间l":"timel","#timel":"timel","#表達式":"expr","#计算式":"expr","#表达式":"expr","#expr":"expr","#rel2abs":"rel2abs","#标题组成部分":"titleparts","#titleparts":"titleparts","#分類樹":"categorytree","#分类树":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","隱藏跨語言連結":"noexternallanglinks","无外部语言连接":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#屬性":"property","#属性":"property","#property":"property","#statements" +:"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","条目路径":"articlepath","伺服器":"server","服务器":"server","server":"server","伺服器名稱":"servername","服务器名":"servername","servername":"servername","scriptpath":"scriptpath","脚本路径":"scriptpath","stylepath":"stylepath","样式路径":"stylepath","numberofwikis":"numberofwikis","wb報表名稱":"wbreponame","wb报告名":"wbreponame","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","组中用户数":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","默认排序":"defaultsort","默认排序关键字":"defaultsort","默认分类排序":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","分类中页面数":"pagesincategory","PAGESIZE":"pagesize","页面大小":"pagesize","PROTECTIONLEVEL":"protectionlevel","保护级别": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","頁面名稱":"pagename","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","页面名等同":"pagenamee","页面名称等同":"pagenamee","FULLPAGENAME":"fullpagename","页面全称":"fullpagename","完整页面名称":"fullpagename","FULLPAGENAMEE":"fullpagenamee","完整页面名称等同":"fullpagenamee","SUBPAGENAME":"subpagename","子页面名称":"subpagename","SUBPAGENAMEE":"subpagenamee","子页面名称等同":"subpagenamee","根頁面名稱":"rootpagename","ROOTPAGENAME":"rootpagename","根页面名称":"rootpagename","根頁面名稱E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","根页面名称等同":"rootpagenamee","BASEPAGENAME":"basepagename","基础页面名称":"basepagename","BASEPAGENAMEE":"basepagenamee","基础页面名称等同":"basepagenamee","TALKPAGENAME":"talkpagename","讨论页面名称":"talkpagename","对话页面名称": +"talkpagename","TALKPAGENAMEE":"talkpagenamee","讨论页面名称等同":"talkpagenamee","对话页面名称等同":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主名字空间页面名称":"subjectpagename","条目页面名称":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","主名字空间页面名称等同":"subjectpagenamee","条目页面名称等同":"subjectpagenamee","REVISIONID":"revisionid","修订ID":"revisionid","REVISIONDAY":"revisionday","修订日":"revisionday","REVISIONDAY2":"revisionday2","修订日2":"revisionday2","REVISIONMONTH":"revisionmonth","修订月":"revisionmonth","REVISIONMONTH1":"revisionmonth1","修订月1":"revisionmonth1","REVISIONYEAR":"revisionyear","修订年":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","修订时间戳":"revisiontimestamp","修訂使用者":"revisionuser","REVISIONUSER":"revisionuser","修订用户":"revisionuser", +"CASCADINGSOURCES":"cascadingsources","级联来源":"cascadingsources","命名空間":"namespace","名字空间":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","名字空间等同":"namespacee","命名空間數":"namespacenumber","名字空间编号":"namespacenumber","NAMESPACENUMBER":"namespacenumber","對話空間":"talkspace","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","讨论空间等同":"talkspacee","讨论名字空间等同":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主名字空间":"subjectspace","条目名字空间":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","主名字空间等同":"subjectspacee","条目名字空间等同":"subjectspacee","文章數":"numberofarticles","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","檔案數":"numberoffiles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles", +"使用者人數量":"numberofusers","用户数":"numberofusers","NUMBEROFUSERS":"numberofusers","活躍使用者人數":"numberofactiveusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","頁面數":"numberofpages","页面数":"numberofpages","NUMBEROFPAGES":"numberofpages","管理員數":"numberofadmins","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","编辑数":"numberofedits","顯示標題":"displaytitle","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","本月1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","本月名":"currentmonthname","本月名称":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月名属格":"currentmonthnamegen","本月名称属格":"currentmonthnamegen","本月縮寫": +"currentmonthabbrev","本月简称":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","今天2":"currentday2","CURRENTDAYNAME":"currentdayname","星期":"currentdayname","今天名":"currentdayname","今天名称":"currentdayname","CURRENTYEAR":"currentyear","今年":"currentyear","目前時間":"currenttime","当前时间":"currenttime","此时":"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","当前小时":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","本地月":"localmonth","本地月2":"localmonth","LOCALMONTH1":"localmonth1","本地月1":"localmonth1","LOCALMONTHNAME":"localmonthname","本地月份名":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","本地月历":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","本地月缩写":"localmonthabbrev","LOCALDAY":"localday","本地日":"localday","LOCALDAY2":"localday2","本地日2": +"localday2","LOCALDAYNAME":"localdayname","本地日名":"localdayname","LOCALYEAR":"localyear","本地年":"localyear","LOCALTIME":"localtime","本地时间":"localtime","LOCALHOUR":"localhour","本地小时":"localhour","網站名稱":"sitename","站点名称":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","本周":"currentweek","CURRENTDOW":"currentdow","当前DOW":"currentdow","LOCALWEEK":"localweek","本地周":"localweek","LOCALDOW":"localdow","本地DOW":"localdow","REVISIONSIZE":"revisionsize","修订大小":"revisionsize","目前版本":"currentversion","当前版本":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","当前时间戳":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","本地时间戳":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","方向标记":"directionmark","內容語言":"contentlanguage","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^()(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-haw.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-haw.json new file mode 100644 index 0000000..ef8d714 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-haw.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","pilinaʻōlelo":"grammar","pilinaōlelo":"grammar","pilinaolelo":"grammar","grammar":"grammar","keka":"gender","gender":"gender","plural":"plural","bidi":"bidi","#ʻōlelo":"language","#ōlelo":"language","#olelo":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","alawaihona":"filepath","filepath":"filepath","idʻaoʻao":"pageid","idaoao":"pageid","pageid":"pageid","int":"int","#kūikawā":"special", +"#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babela":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","kikowaenapūnaewele":"server","kikowaenapunaewele":"server","server":"server","inoakikowaenapūnaewele":"servername","inoakikowaenapunaewele":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis", +"wbreponame":"wbreponame"},{"HELUʻAOʻAO":"numberofpages","HELUAOAO":"numberofpages","NUMBEROFPAGES":"numberofpages","HELUMEAHOʻOHANA":"numberofusers","HELUMEAHOOHANA":"numberofusers","NUMBEROFUSERS":"numberofusers","HELUMEAHOʻOHANANEI":"numberofactiveusers","HELUMEAHOOHANANEI":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","HELUMEA":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","HELUWAIHONA":"numberoffiles","NUMBEROFFILES":"numberoffiles","HELUKAHU":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","HELULOLI":"numberofedits","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","HELULEWAINOA":"namespacenumber","NAMESPACENUMBER": +"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","INOAʻAOʻAO":"pagename","INOAAOAO":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser", +"CASCADINGSOURCES":"cascadingsources","LEWAINOA":"namespace","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","KĒIAMAHINA":"currentmonth","KEIAMAHINA":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","KĒIAINOAMAHINA":"currentmonthname","KEIAINOAMAHINA":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","KĒIALĀ":"currentday","KEIALA":"currentday","CURRENTDAY":"currentday","KĒIALĀ2":"currentday2","KEIALA2":"currentday2","CURRENTDAY2":"currentday2","KĒIAINOALĀ":"currentdayname","KEIAINOALA":"currentdayname","CURRENTDAYNAME":"currentdayname","KĒIAMAKAHIKI":"currentyear","KEIAMAKAHIKI":"currentyear","CURRENTYEAR":"currentyear","KĒIAMANAWA":"currenttime","KEIAMANAWA":"currenttime","CURRENTTIME":"currenttime","KĒIAHOLA":"currenthour","KEIAHOLA":"currenthour","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth", +"LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","INOAKAHUA":"sitename","SITENAME":"sitename","KĒIAPULE":"currentweek","KEIAPULE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-he.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-he.json new file mode 100644 index 0000000..3be2273 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-he.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"קטע":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__ללא_תוכן_עניינים__":"notoc","__ללא_תוכן__":"notoc","__notoc__":"notoc","__ללא_גלריה__":"nogallery","__nogallery__":"nogallery","__חייב_תוכן_עניינים__":"forcetoc","__חייב_תוכן__":"forcetoc","__forcetoc__":"forcetoc","__תוכן_עניינים__":"toc", +"__תוכן__":"toc","__toc__":"toc","__ללא_עריכה__":"noeditsection","__noeditsection__":"noeditsection","__ללא_המרת_כותרת__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__ללא_המרת_תוכן__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__יצירת_הערה__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__ללא_יצירת_הערה__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__קטגוריה_מוסתרת__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__לחיפוש__":"index","__INDEX__":"index","__לא_לחיפוש__":"noindex","__NOINDEX__":"noindex","__הפניה_קבועה__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__פירושונים__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}], +"functionSynonyms":[{"מרחב שם":"ns","ns":"ns","מרחב שם מקודד":"nse","nse":"nse","נתיב מקודד":"urlencode","urlencode":"urlencode","אות ראשונה קטנה":"lcfirst","lcfirst":"lcfirst","אות ראשונה גדולה":"ucfirst","ucfirst":"ucfirst","אותיות קטנות":"lc","lc":"lc","אותיות גדולות":"uc","uc":"uc","כתובת יחסית":"localurl","localurl":"localurl","כתובת יחסית מקודד":"localurle","localurle":"localurle","כתובת מלאה":"fullurl","fullurl":"fullurl","כתובת מלאה מקודד":"fullurle","fullurle":"fullurle","כתובת קנונית":"canonicalurl","canonicalurl":"canonicalurl","כתובת קנונית מקודד":"canonicalurle","canonicalurle":"canonicalurle","עיצוב מספר":"formatnum","formatnum":"formatnum","דקדוק":"grammar","grammar":"grammar","מגדר":"gender","gender":"gender","רבים":"plural","plural":"plural","bidi":"bidi","#שפה":"language","#language":"language", +"ריפוד משמאל":"padleft","padleft":"padleft","ריפוד מימין":"padright","padright":"padright","עוגן מקודד":"anchorencode","anchorencode":"anchorencode","נתיב לקובץ":"filepath","filepath":"filepath","pageid":"pageid","הודעה":"int","int":"int","#מיוחד":"special","#special":"special","#speciale":"speciale","#תג":"tag","#תגית":"tag","#tag":"tag","#עיצוב תאריך":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#בבל":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#תנאי":"if","#if":"if","#שווה":"ifeq","#ifeq":"ifeq","#בחר":"switch","#switch":"switch","#קיים":"ifexist","#ifexist":"ifexist","#חשב תנאי":"ifexpr","#ifexpr":"ifexpr","#תנאי שגיאה":"iferror","#iferror":"iferror","#זמן":"time","#time":"time","#זמןמ":"timel","#timel":"timel","#חשב":"expr","#expr":"expr","#יחסי למוחלט":"rel2abs","#rel2abs" +:"rel2abs","#חלק בכותרת":"titleparts","#titleparts":"titleparts","#עץ_קטגוריה":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#קטע":"lst","#lstx":"lstx","#section-x":"lstx","#בלי קטע":"lstx","#lsth":"lsth","#section-h":"lsth","ללא_קישורי_שפה":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#מאפיין":"property","#property":"property","#קביעות":"statements","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","נתיב הדפים":"articlepath","articlepath":"articlepath","כתובת השרת":"server","שרת":"server","server":"server","שם השרת":"servername","servername":"servername","נתיב הקבצים":"scriptpath","scriptpath":"scriptpath","נתיב הסגנון":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","שם_המאגר":"wbreponame","wbreponame":"wbreponame"},{"מספר בקבוצה":"numberingroup", +"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","מיון רגיל":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","דפים בקטגוריה":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","גודל דף":"pagesize","PAGESIZE":"pagesize","רמת הגנה":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","שם הדף":"pagename","PAGENAME":"pagename","שם הדף מקודד":"pagenamee","PAGENAMEE":"pagenamee","שם הדף המלא":"fullpagename","FULLPAGENAME":"fullpagename","שם הדף המלא מקודד":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","שם דף המשנה":"subpagename","SUBPAGENAME":"subpagename","שם דף המשנה מקודד":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","שם דף הבסיס":"basepagename","BASEPAGENAME":"basepagename", +"שם דף הבסיס מקודד":"basepagenamee","BASEPAGENAMEE":"basepagenamee","שם דף השיחה":"talkpagename","TALKPAGENAME":"talkpagename","שם דף השיחה מקודד":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","שם דף הנושא":"subjectpagename","שם הערך":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","שם דף הנושא מקודד":"subjectpagenamee","שם הערך מקודד":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","מזהה גרסה":"revisionid","REVISIONID":"revisionid","יום גרסה":"revisionday","REVISIONDAY":"revisionday","יום גרסה 2":"revisionday2","REVISIONDAY2":"revisionday2","חודש גרסה":"revisionmonth","REVISIONMONTH":"revisionmonth","חודש גרסה 1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","שנת גרסה":"revisionyear","REVISIONYEAR":"revisionyear","זמן גרסה":"revisiontimestamp","REVISIONTIMESTAMP": +"revisiontimestamp","כותב גרסה":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","מרחב השם":"namespace","NAMESPACE":"namespace","מרחב השם מקודד":"namespacee","NAMESPACEE":"namespacee","מספר_מרחב_השם":"namespacenumber","NAMESPACENUMBER":"namespacenumber","מרחב השיחה":"talkspace","TALKSPACE":"talkspace","מרחב השיחה מקודד":"talkspacee","TALKSPACEE":"talkspacee","מרחב הנושא":"subjectspace","מרחב הערכים":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","מרחב הנושא מקודד":"subjectspacee","מרחב הערכים מקודד":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","מספר ערכים":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","מספר קבצים":"numberoffiles","NUMBEROFFILES":"numberoffiles","מספר משתמשים":"numberofusers","NUMBEROFUSERS":"numberofusers", +"מספר משתמשים פעילים":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","מספר דפים כולל":"numberofpages","מספר דפים":"numberofpages","NUMBEROFPAGES":"numberofpages","מספר מפעילים":"numberofadmins","NUMBEROFADMINS":"numberofadmins","מספר עריכות":"numberofedits","NUMBEROFEDITS":"numberofedits","כותרת תצוגה":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","חודש נוכחי":"currentmonth","חודש נוכחי 2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","חודש נוכחי 1":"currentmonth1","CURRENTMONTH1":"currentmonth1","שם חודש נוכחי":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","שם חודש נוכחי קניין":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","קיצור חודש נוכחי":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","יום נוכחי":"currentday","CURRENTDAY": +"currentday","יום נוכחי 2":"currentday2","CURRENTDAY2":"currentday2","שם יום נוכחי":"currentdayname","CURRENTDAYNAME":"currentdayname","שנה נוכחית":"currentyear","CURRENTYEAR":"currentyear","שעה נוכחית":"currenttime","CURRENTTIME":"currenttime","שעות נוכחיות":"currenthour","CURRENTHOUR":"currenthour","חודש מקומי":"localmonth","חודש מקומי 2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","חודש מקומי 1":"localmonth1","LOCALMONTH1":"localmonth1","שם חודש מקומי":"localmonthname","LOCALMONTHNAME":"localmonthname","שם חודש מקומי קניין":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","קיצור חודש מקומי":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","יום מקומי":"localday","LOCALDAY":"localday","יום מקומי 2":"localday2","LOCALDAY2":"localday2","שם יום מקומי":"localdayname","LOCALDAYNAME":"localdayname", +"שנה מקומית":"localyear","LOCALYEAR":"localyear","שעה מקומית":"localtime","LOCALTIME":"localtime","שעות מקומיות":"localhour","LOCALHOUR":"localhour","שם האתר":"sitename","SITENAME":"sitename","שבוע נוכחי":"currentweek","CURRENTWEEK":"currentweek","מספר יום נוכחי":"currentdow","CURRENTDOW":"currentdow","שבוע מקומי":"localweek","LOCALWEEK":"localweek","מספר יום מקומי":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","גרסה נוכחית":"currentversion","CURRENTVERSION":"currentversion","זמן נוכחי":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","זמן מקומי":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","סימן כיווניות":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","שפת תוכן":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zא-ת]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hi.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hi.json new file mode 100644 index 0000000..7f8de78 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hi.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"quiz":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__बिना_अनुक्रम__":"notoc","__विषय_सूची_हीन__":"notoc","__notoc__":"notoc","__गैलरी_नहीं__":"nogallery","__nogallery__":"nogallery","__अनुक्रम_दिखाएँ__":"forcetoc","__विषय_सूची_दिखाएँ__":"forcetoc", +"__विषय_सूची_दिखायें__":"forcetoc","__forcetoc__":"forcetoc","__अनुक्रम__":"toc","__विषय_सूची__":"toc","__toc__":"toc","__अनुभाग_सम्पादन_नहीं__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__विषय_जोड़ें_कड़ी__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__विषय_जोड़े_कड़ी_रहित__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__छुपी_श्रेणी__":"hiddencat","__छिपी_श्रेणी__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__सूचीबद्ध__":"index","__INDEX__":"index","__असूचीबद्ध__":"noindex","__NOINDEX__":"noindex", +"__स्थिर_पुनर्प्रेषण__":"staticredirect","__स्थिर_अनुप्रेषण__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"नामस्थान":"ns","ns":"ns","नामस्थान_कोड":"nse","nse":"nse","यू_आर_एल_कोड":"urlencode","urlencode":"urlencode","छोटे_अक्षर_से_शुरू":"lcfirst","lcfirst":"lcfirst","बड़े_अक्षर_से_शुरू":"ucfirst","ucfirst":"ucfirst","छोटे_अक्षर":"lc","lc":"lc","बड़े_अक्षर":"uc","uc":"uc","स्थानीय_यू_आर_एल":"localurl","localurl":"localurl","स्थानीय_यू_आर_एल_कोड":"localurle","localurle":"localurle","पूर्ण_यू_आर_एल":"fullurl","fullurl":"fullurl", +"पूर्ण_यू_आर_एल_कोड":"fullurle","fullurle":"fullurle","मानक_यू_आर_एल":"canonicalurl","canonicalurl":"canonicalurl","मानक_यू_आर_एल_कोड":"canonicalurle","canonicalurle":"canonicalurle","संख्या_रूप":"formatnum","formatnum":"formatnum","व्याकरण":"grammar","grammar":"grammar","लिंग":"gender","gender":"gender","वचन":"plural","plural":"plural","bidi":"bidi","#भाषा":"language","#language":"language","बाएँ_जोड़ें":"padleft","बायें_जोड़ें":"padleft","padleft":"padleft","दाएँ_जोड़ें":"padright","दायें_जोड़ें":"padright","padright":"padright","ऐंकर_कोड":"anchorencode","anchorencode":"anchorencode","फ़ाइल_पथ":"filepath","filepath":"filepath","पृष्ठ_आइ_डी":"pageid","pageid":"pageid","विश्व":"int","int":"int","#विशेष":"special" +,"#special":"special","#विशेष_कोड":"speciale","#speciale":"speciale","#टैग":"tag","#tag":"tag","#तिथि_रूप":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","pendingchangelevel":"pendingchangelevel","#लक्ष्य":"target","#target":"target","#बेबल":"babel","#babel":"babel","#coordinates":"coordinates","#बुलाएँ":"invoke","#बुलायें":"invoke","#invoke":"invoke","#related":"related","#यदि":"if","#if":"if","#यदिसम":"ifeq","#यदि_समान":"ifeq","#यदि_बराबर":"ifeq","#ifeq":"ifeq","#बदलें":"switch","#switch":"switch","#यदि_मौजूद":"ifexist","#ifexist":"ifexist","#यदि_सूत्र":"ifexpr","#ifexpr":"ifexpr","#यदि_त्रुटि":"iferror","#iferror":"iferror","#समय":"time","#time":"time","#समय_स्थानीय":"timel","#timel":"timel","#सूत्र":"expr","#expr":"expr", +"#सम्बन्धित_से_पूर्ण":"rel2abs","#संबंधित_से_पूर्ण":"rel2abs","#rel2abs":"rel2abs","#शीर्षक_भाग":"titleparts","#titleparts":"titleparts","#श्रेणी_वृक्ष":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","कोई_अंतरविकी_कड़ियाँ_नहीं":"noexternallanglinks","कोई_अंतरविकि_कड़ियाँ_नहीं":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#गुण":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","लेख_पथ":"articlepath","articlepath":"articlepath","सर्वर":"server","server":"server","सर्वर_नाम":"servername","servername":"servername","स्क्रिप्ट_पथ":"scriptpath", +"scriptpath":"scriptpath","स्टाइल_पथ":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"समूह_संख्या":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","मूल_सॉर्ट":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","श्रेणी_में_पृष्ठ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","पृष्ठ_आकार":"pagesize","PAGESIZE":"pagesize","सुरक्षा_स्तर":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","पृष्ठ_नाम":"pagename","PAGENAME":"pagename","पृष्ठ_नाम_कोड":"pagenamee","PAGENAMEE":"pagenamee","पूर्ण_पृष्ठ_नाम":"fullpagename","FULLPAGENAME":"fullpagename","पूर्ण_पृष्ठ_नाम_कोड" +:"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","उपपृष्ठ_नाम":"subpagename","SUBPAGENAME":"subpagename","उपपृष्ठ_नाम_कोड":"subpagenamee","SUBPAGENAMEE":"subpagenamee","मूल_पृष्ठ_नाम":"rootpagename","ROOTPAGENAME":"rootpagename","मूल_पृष्ठ_नाम_कोड":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","तल_पृष्ठ_नाम":"basepagename","BASEPAGENAME":"basepagename","तल_पृष्ठ_नाम_कोड":"basepagenamee","BASEPAGENAMEE":"basepagenamee","चर्चा_पृष्ठ_नाम":"talkpagename","TALKPAGENAME":"talkpagename","चर्चा_पृष्ठ_नाम_कोड":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","सामग्री_पृष्ठ_नाम":"subjectpagename","लेख_पृष्ठ_नाम":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename", +"सामग्री_पृष्ठ_नाम_कोड":"subjectpagenamee","लेख_पृष्ठ_नाम_कोड":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","अवतरण_संख्या":"revisionid","REVISIONID":"revisionid","अवतरण_दिन":"revisionday","REVISIONDAY":"revisionday","अवतरण_दिन2":"revisionday2","अवतरण_दिन२":"revisionday2","REVISIONDAY2":"revisionday2","अवतरण_माह":"revisionmonth","REVISIONMONTH":"revisionmonth","अवतरण_माह1":"revisionmonth1","अवतरण_माह१":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","अवतरण_वर्ष":"revisionyear","REVISIONYEAR":"revisionyear","अवतरण_समय":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","अवतरण_सदस्य":"revisionuser","REVISIONUSER":"revisionuser","सीढ़ी_सुरक्षा_स्रोत": +"cascadingsources","CASCADINGSOURCES":"cascadingsources","नामस्थान":"namespace","NAMESPACE":"namespace","नामस्थान_कोड":"namespacee","NAMESPACEE":"namespacee","नामस्थान_संख्या":"namespacenumber","NAMESPACENUMBER":"namespacenumber","चर्चा_स्थान":"talkspace","TALKSPACE":"talkspace","चर्चा_स्थान_कोड":"talkspacee","TALKSPACEE":"talkspacee","सामग्री_स्थान":"subjectspace","लेख_स्थान":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","सामग्री_स्थान_कोड":"subjectspacee","लेख_स्थान_कोड":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","लेख_संख्या":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","फ़ाइल_संख्या":"numberoffiles","फाइल_संख्या":"numberoffiles","NUMBEROFFILES": +"numberoffiles","सदस्य_संख्या":"numberofusers","NUMBEROFUSERS":"numberofusers","सक्रिय_सदस्य_संख्या":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","पृष्ठ_संख्या":"numberofpages","NUMBEROFPAGES":"numberofpages","प्रबन्धक_संख्या":"numberofadmins","प्रबंधक_संख्या":"numberofadmins","NUMBEROFADMINS":"numberofadmins","सम्पादन_संख्या":"numberofedits","NUMBEROFEDITS":"numberofedits","दृश्य_शीर्षक":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","वर्तमान_माह":"currentmonth","वर्तमान_माह2":"currentmonth","वर्तमान_माह२":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","वर्तमान_माह1":"currentmonth1","वर्तमान_माह१":"currentmonth1","CURRENTMONTH1": +"currentmonth1","वर्तमान_माह_नाम":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","वर्तमान_माह_सम्बन्ध":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","वर्तमान_माह_संक्षेप":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","वर्तमान_दिन":"currentday","CURRENTDAY":"currentday","वर्तमान_दिन2":"currentday2","वर्तमान_दिन२":"currentday2","CURRENTDAY2":"currentday2","वर्तमान_दिन_नाम":"currentdayname","CURRENTDAYNAME":"currentdayname","वर्तमान_वर्ष":"currentyear","CURRENTYEAR":"currentyear","वर्तमान_समय":"currenttime","CURRENTTIME":"currenttime","वर्तमान_घंटा":"currenthour","CURRENTHOUR":"currenthour","स्थानीय_माह":"localmonth","स्थानीय_माह2":"localmonth", +"स्थानीय_माह२":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","स्थानीय_माह1":"localmonth1","स्थानीय_माह१":"localmonth1","LOCALMONTH1":"localmonth1","स्थानीय_माह_नाम":"localmonthname","LOCALMONTHNAME":"localmonthname","स्थानीय_माह_सम्बन्ध":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","स्थानीय_माह_संक्षेप":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","स्थानीय_दिन":"localday","LOCALDAY":"localday","स्थानीय_दिन2":"localday2","स्थानीय_दिन२":"localday2","LOCALDAY2":"localday2","स्थानीय_दिन_नाम":"localdayname","LOCALDAYNAME":"localdayname","स्थानीय_वर्ष":"localyear","LOCALYEAR":"localyear","स्थानीय_समय":"localtime","LOCALTIME":"localtime", +"स्थानीय_घंटा":"localhour","LOCALHOUR":"localhour","साइट_नाम":"sitename","SITENAME":"sitename","वर्तमान_सप्ताह":"currentweek","CURRENTWEEK":"currentweek","वर्तमान_सप्ताह_का_दिन":"currentdow","CURRENTDOW":"currentdow","स्थानीय_सप्ताह":"localweek","LOCALWEEK":"localweek","स्थानीय_सप्ताह_का_दिन":"localdow","LOCALDOW":"localdow","अवतरण_आकार":"revisionsize","REVISIONSIZE":"revisionsize","वर्तमान_अवतरण":"currentversion","CURRENTVERSION":"currentversion","वर्तमान_समय_मुहर":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","स्थानीय_समय_मुहर":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","दिशा_चिन्ह":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","सामग्री_भाषा": +"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z\\x{0900}-\\x{0963}\\x{0966}-\\x{A8E0}-\\x{A8FF}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hif.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hif.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hif.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ho.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ho.json new file mode 100644 index 0000000..a906994 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ho.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"charinsert":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hr.json new file mode 100644 index 0000000..6a77f32 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hr.json @@ -0,0 +1,10 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__bezsadržaja__":"notoc","__notoc__":"notoc","__bezgalerije__":"nogallery","__nogallery__":"nogallery","__uključisadržaj__":"forcetoc","__forcetoc__":"forcetoc","__sadržaj__":"toc","__toc__":"toc","__bezuređivanjaodlomaka__":"noeditsection","__noeditsection__":"noeditsection","__bezpretvaranjanaslova__":"notitleconvert" +,"__bpn__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__bezpretvaranjasadržaja__":"nocontentconvert","__bps__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NOVIODLOMAKPOVEZNICA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__SKRIVENAKAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__KAZALO__":"index","__INDEX__":"index","__BEZKAZALA__":"noindex","__NOINDEX__":"noindex","__NEPOMIČNOPREUSMJERAVANJE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"imp":"ns","ns":"ns","nse":"nse","urlkodiranje":"urlencode","urlencode":"urlencode","msprvo":"lcfirst","lcfirst":"lcfirst","vsprvo":"ucfirst","ucfirst":"ucfirst","ms":"lc","lc":"lc","vs":"uc","uc":"uc", +"mjesniurl":"localurl","localurl":"localurl","mjesniurle":"localurle","localurle":"localurle","puniurl":"fullurl","fullurl":"fullurl","puniurle":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","oblikbroja":"formatnum","formatnum":"formatnum","gramatika":"grammar","grammar":"grammar","gender":"gender","množina":"plural","plural":"plural","bidi":"bidi","#jezik":"language","#language":"language","postavalijevo":"padleft","padleft":"padleft","postavadesno":"padright","padright":"padright","sidrokodiranje":"anchorencode","anchorencode":"anchorencode","putanjadatoteke":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#posebno":"special","#special":"special","#speciale":"speciale","#oznaka":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist", +"#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","imeservera":"servername","servername":"servername","putanjaskripte":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","GLAVNIRASPORED":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","STRANICEPOKATEGORIJI":"pagesincategory","STRANICEUKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory", +"VELIČINASTRANICE":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","IMESTRANICE":"pagename","PAGENAME":"pagename","IMESTRANICEE":"pagenamee","PAGENAMEE":"pagenamee","PUNOIMESTRANICE":"fullpagename","FULLPAGENAME":"fullpagename","PUNOIMESTRANICEE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","IMEPODSTRANICE":"subpagename","SUBPAGENAME":"subpagename","IMEPODSTRANICEE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","IMEOSNOVNESTRANICE":"basepagename","BASEPAGENAME":"basepagename","IMEOSNOVNESTRANICEE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","IMERAZGOVORASTRANICE":"talkpagename","TALKPAGENAME":"talkpagename","IMERAZGOVORASTRANICEE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","IMEGLAVNESTRANICE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","IMEGLAVNESTRANICEE":"subjectpagenamee","SUBJECTPAGENAMEE": +"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDIZMJENE":"revisionid","REVISIONID":"revisionid","DANIZMJENE":"revisionday","REVISIONDAY":"revisionday","DANIZMJENE2":"revisionday2","REVISIONDAY2":"revisionday2","MJESECIZMJENE":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","GODINAIZMJENE":"revisionyear","REVISIONYEAR":"revisionyear","VREMENSKAOZNAKAIZMJENE":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","IMENSKIPROSTOR":"namespace","NAMESPACE":"namespace","IMENSKIPROSTORE":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RAZGOVOR":"talkspace","TALKSPACE":"talkspace","RAZGOVORE":"talkspacee","TALKSPACEE":"talkspacee","PROSTORSTRANICE":"subjectspace","IMPSTRANICE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","PROSTORSTRANICEE":"subjectspacee","IMPSTRANICEE":"subjectspacee","SUBJECTSPACEE":"subjectspacee", +"ARTICLESPACEE":"subjectspacee","BROJČLANAKA":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","BROJDATOTEKA":"numberoffiles","NUMBEROFFILES":"numberoffiles","BROJSURADNIKA":"numberofusers","NUMBEROFUSERS":"numberofusers","BROJAKTIVNIHSURADNIKA":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","BROJSTRANICA":"numberofpages","NUMBEROFPAGES":"numberofpages","BROJADMINA":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BROJUREĐIVANJA":"numberofedits","NUMBEROFEDITS":"numberofedits","POKAŽINASLOV":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","TRENUTAČNIMJESEC":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","TRENUTAČNIMJESEC1":"currentmonth1","CURRENTMONTH1":"currentmonth1","TRENUTAČNIMJESECIME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","TRENUTAČNIMJESECIMEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","TRENUTAČNIMJESECKRAT":"currentmonthabbrev","CURRENTMONTHABBREV": +"currentmonthabbrev","TRENUTAČNIDAN":"currentday","CURRENTDAY":"currentday","TRENUTAČNIDAN2":"currentday2","CURRENTDAY2":"currentday2","TRENUTAČNIDANIME":"currentdayname","CURRENTDAYNAME":"currentdayname","TRENUTAČNAGODINA":"currentyear","CURRENTYEAR":"currentyear","TRENUTAČNOVRIJEME":"currenttime","CURRENTTIME":"currenttime","TRENUTAČNISAT":"currenthour","CURRENTHOUR":"currenthour","MJESNIMJESEC":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MJESNIMJESEC1":"localmonth1","LOCALMONTH1":"localmonth1","MJESNIMJESECIME":"localmonthname","LOCALMONTHNAME":"localmonthname","MJESNIMJESECIMEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MJESNIMJESECKRAT":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","MJESNIDAN":"localday","LOCALDAY":"localday","MJESNIDAN2":"localday2","LOCALDAY2":"localday2","MJESNIDANIME":"localdayname","LOCALDAYNAME":"localdayname","MJESNAGODINA":"localyear","LOCALYEAR":"localyear","MJESNOVRIJEME":"localtime", +"LOCALTIME":"localtime","MJESNISAT":"localhour","LOCALHOUR":"localhour","IMEPROJEKTA":"sitename","SITENAME":"sitename","TRENUTAČNITJEDAN":"currentweek","CURRENTWEEK":"currentweek","TRENUTAČNIDANTJEDNA":"currentdow","CURRENTDOW":"currentdow","MJESNITJEDAN":"localweek","LOCALWEEK":"localweek","MJESNIDANTJEDNA":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","TRENUTAČNAIZMJENA":"currentversion","CURRENTVERSION":"currentversion","TRENUTAČNAOZNAKAVREMENA":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MJESNAOZNAKAVREMENA":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","JEZIKPROJEKTA":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([čšžćđßa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hsb.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hsb.json new file mode 100644 index 0000000..d777619 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hsb.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__":"forcetoc","__inhaltsverzeichnis__":"toc","__toc__":"toc", +"__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__":"noindex","__NOINDEX__":"noindex","__PERMANENTE_WEITERLEITUNG__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","lokale_url":"localurl","localurl":"localurl","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zahlenformat":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#sprache":"language","#language":"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright": +"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorienbaum":"categorytree","#kategoriebaum":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx", +"#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE":"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SEITENNAME":"pagename","PAGENAME":"pagename","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSSIONSSEITE":"talkpagename","DISK":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","HAUPTSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR":"revisionyear","VERSIONSJAHR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp", +"REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","NAMENSRAUM":"namespace","NAMESPACE":"namespace","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DATEIANZAHL":"numberoffiles","NUMBEROFFILES":"numberoffiles","BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTIVE_BENUTZER": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JETZIGER_MONATSNAME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JETZIGER_KALENDERTAG":"currentday","JETZIGER_TAG":"currentday","CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2","JETZIGER_TAG_2": +"currentday2","CURRENTDAY2":"currentday2","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV":"localmonthnamegen","LOKALER_MONATSNAME_GEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2","LOKALER_TAG_2":"localday2","LOCALDAY2":"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME":"localdayname","LOKALES_JAHR" +:"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize","JETZIGE_VERSION":"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHALTSSPRACHE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE": +"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöüßa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ht.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ht.json new file mode 100644 index 0000000..c9ca907 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ht.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode", +"initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int", +"#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property", +"#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGE":"pagename","PAGENAME":"pagename", +"NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid", +"REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee", +"ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL": +"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàèòÀÈÒ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hu.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hu.json new file mode 100644 index 0000000..270e0a5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hu.json @@ -0,0 +1,10 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"talkpage":true,"thread":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__nincstartalomjegyzék__":"notoc","__nincstj__":"notoc","__notoc__":"notoc","__nincsgaléria__":"nogallery","__nogallery__":"nogallery","__legyentartalomjegyzék__":"forcetoc","__legyentj__":"forcetoc","__forcetoc__":"forcetoc","__tartalomjegyzék__":"toc","__tj__":"toc","__toc__":"toc", +"__nincsszerkesztés__":"noeditsection","__nincsszerk__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ÚJSZAKASZHIV__":"newsectionlink","__ÚJSZAKASZLINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NINCSÚJSZAKASZHIV__":"nonewsectionlink","__NINCSÚJSZAKASZLINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__REJTETTKAT__":"hiddencat","__REJTETTKATEGÓRIA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NINCSINDEX__":"noindex","__NOINDEX__":"noindex","__ÁLLANDÓÁTIRÁNYÍTÁS__":"staticredirect","__STATIKUSÁTIRÁNYÍTÁS__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"névtér":"ns","ns": +"ns","nse":"nse","urlkódolva":"urlencode","urlencode":"urlencode","kiskezdő":"lcfirst","kiskezdőbetű":"lcfirst","lcfirst":"lcfirst","nagykezdő":"ucfirst","nagykezdőbetű":"ucfirst","ucfirst":"ucfirst","kisbetű":"lc","kisbetűk":"lc","kb":"lc","kisbetűs":"lc","lc":"lc","nagybetű":"uc","nagybetűk":"uc","nb":"uc","nagybetűs":"uc","uc":"uc","helyiurl":"localurl","localurl":"localurl","helyiurle":"localurle","localurle":"localurle","teljesurl":"fullurl","fullurl":"fullurl","teljesurle":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formázottszám":"formatnum","számformázás":"formatnum","számform":"formatnum","formatnum":"formatnum","nyelvtan":"grammar","grammar":"grammar","gender":"gender","többesszám":"plural","plural":"plural","bidi":"bidi","#nyelv":"language","#language":"language","padleft":"padleft","padright":"padright","horgonykódolva":"anchorencode","anchorencode":"anchorencode","elérésiút":"filepath","filepath": +"filepath","pageid":"pageid","int":"int","#speciális":"special","#special":"special","#speciale":"speciale","#tag":"tag","#dátumformázás":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#useliquidthreads":"useliquidthreads","#lqtpagelimit":"lqtpagelimit","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#assessment":"assessment","#ha":"if","#if":"if","#haegyenlő":"ifeq","#ifeq":"ifeq","#switch":"switch","#halétezik":"ifexist","#ifexist":"ifexist","#hakif":"ifexpr","#ifexpr":"ifexpr","#hahibás":"iferror","#iferror":"iferror","#idő":"time","#time":"time","#timel":"timel","#kif":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist": +"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","szerver":"server","kiszolgáló":"server","server":"server","szerverneve":"servername","kiszolgálóneve":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"CSOPORTTAGOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","RENDEZÉS":"defaultsort","KULCS":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","LAPOKAKATEGÓRIÁBAN":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","LAPMÉRET":"pagesize","PAGESIZE":"pagesize","VÉDELMISZINT":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","OLDALNEVE":"pagename","PAGENAME":"pagename","OLDALNEVEE":"pagenamee","PAGENAMEE":"pagenamee","LAPTELJESNEVE":"fullpagename","FULLPAGENAME":"fullpagename","LAPTELJESNEVEE": +"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ALLAPNEVE":"subpagename","SUBPAGENAME":"subpagename","ALLAPNEVEE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ALAPLAPNEVE":"basepagename","BASEPAGENAME":"basepagename","ALAPLAPNEVEE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","VITALAPNEVE":"talkpagename","TALKPAGENAME":"talkpagename","VITALAPNEVEE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SZÓCIKKNEVE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SZÓCIKKNEVEE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","VÁLTOZATAZON":"revisionid","VÁLTOZATAZONOSÍTÓ":"revisionid","REVISIONID":"revisionid","VÁLTOZATNAPJA":"revisionday","REVISIONDAY":"revisionday","VÁLTOZATNAPJA2":"revisionday2","REVISIONDAY2":"revisionday2","VÁLTOZATHÓNAPJA":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1", +"VÁLTOZATÉVE":"revisionyear","REVISIONYEAR":"revisionyear","VÁLTOZATIDŐBÉLYEG":"revisiontimestamp","VÁLTOZATIDEJE":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","VÁLTOZATSZERKESZTŐJE":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NÉVTERE":"namespace","NAMESPACE":"namespace","NÉVTEREE":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","VITATERE":"talkspace","TALKSPACE":"talkspace","VITATEREE":"talkspacee","TALKSPACEE":"talkspacee","SZÓCIKKNÉVTERE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SZÓCIKKNÉVTEREE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","SZÓCIKKEKSZÁMA":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","FÁJLOKSZÁMA":"numberoffiles","KÉPEKSZÁMA":"numberoffiles","NUMBEROFFILES":"numberoffiles","SZERKESZTŐKSZÁMA":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTÍVSZERKESZTŐKSZÁMA": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","OLDALAKSZÁMA":"numberofpages","LAPOKSZÁMA":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINOKSZÁMA":"numberofadmins","NUMBEROFADMINS":"numberofadmins","SZERKESZTÉSEKSZÁMA":"numberofedits","NUMBEROFEDITS":"numberofedits","MEGJELENÍTENDŐCÍM":"displaytitle","CÍM":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","HÓNAP":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","HÓNAP1":"currentmonth1","CURRENTMONTH1":"currentmonth1","HÓNAPNEVE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","HÓNAPRÖVID":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","MAINAP":"currentday","CURRENTDAY":"currentday","MAINAP2":"currentday2","CURRENTDAY2":"currentday2","MAINAPNEVE":"currentdayname","CURRENTDAYNAME":"currentdayname","ÉV":"currentyear","CURRENTYEAR":"currentyear","IDŐ":"currenttime","CURRENTTIME":"currenttime", +"ÓRA":"currenthour","CURRENTHOUR":"currenthour","HELYIHÓNAP":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","HELYIHÓNAP1":"localmonth1","LOCALMONTH1":"localmonth1","HELYIHÓNAPNÉV":"localmonthname","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","HELYIHÓNAPRÖVIDÍTÉS":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HELYINAP":"localday","LOCALDAY":"localday","HELYINAP2":"localday2","LOCALDAY2":"localday2","HELYINAPNEVE":"localdayname","LOCALDAYNAME":"localdayname","HELYIÉV":"localyear","LOCALYEAR":"localyear","HELYIIDŐ":"localtime","LOCALTIME":"localtime","HELYIÓRA":"localhour","LOCALHOUR":"localhour","WIKINEVE":"sitename","SITENAME":"sitename","HÉT":"currentweek","CURRENTWEEK":"currentweek","HÉTNAPJA":"currentdow","CURRENTDOW":"currentdow","HELYIHÉT":"localweek","LOCALWEEK":"localweek","HELYIHÉTNAPJA":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","JELENLEGIVÁLTOZAT":"currentversion","CURRENTVERSION": +"currentversion","IDŐBÉLYEG":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","HELYIIDŐBÉLYEG":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","IRÁNYJELZŐ":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","TARTALOMNYELVE":"contentlanguage","TARTNYELVE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záéíóúöüőűÁÉÍÓÚÖÜŐŰ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hy.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hy.json new file mode 100644 index 0000000..f4e45c5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hy.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__առանց_բով__":"notoc","__notoc__":"notoc","__առանց_սրահի__":"nogallery","__nogallery__":"nogallery","__ստիպել_բով__":"forcetoc","__forcetoc__":"forcetoc","__բով__":"toc","__toc__":"toc","__առանց_բաժնի_խմբագրման__":"noeditsection","__noeditsection__":"noeditsection", +"__առանց_վերնագրի_փոփոխման__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__առանց_պարունակության_փոփոխման__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ՀՂՈՒՄ_ՆՈՐ_ԲԱԺՆԻ_ՎՐԱ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ատ՝":"ns","ns":"ns","nse":"nse","մշակված_հասցե՛":"urlencode","urlencode":"urlencode","փոքրատառ_սկզբնատառ՝":"lcfirst","lcfirst":"lcfirst","մեծատառ_սկզբնատառ՝":"ucfirst","ucfirst":"ucfirst","փոքրատառ՝":"lc","lc": +"lc","մեծատառ՝":"uc","uc":"uc","տեղական_հասցեն՝":"localurl","localurl":"localurl","տեղական_հասցեն_2՝":"localurle","localurle":"localurle","լրիվ_հասցեն՝":"fullurl","fullurl":"fullurl","լրիվ_հասցեն_2՝":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","ձեվել_թիվը":"formatnum","formatnum":"formatnum","հոլով՛":"grammar","grammar":"grammar","gender":"gender","հոգնակի՝":"plural","plural":"plural","bidi":"bidi","#լեզու՝":"language","#language":"language","լրացնել_ձախից":"padleft","padleft":"padleft","լրացնել_աջից":"padright","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","ներք՝":"int","int":"int","#սպասարկող":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates": +"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","սերվերը":"server","server":"server","սերվերի_անունը":"servername","servername":"servername","սքրիպտի_ճանապարհը":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ԼՌՈՒԹՅԱՄԲ_ԴԱՍԱՎՈՐՈՒՄ՝":"defaultsort","DEFAULTSORT":"defaultsort", +"DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ԷՋԻ_ԱՆՈՒՆԸ":"pagename","PAGENAME":"pagename","ԷՋԻ_ԱՆՈՒՆԸ_2":"pagenamee","PAGENAMEE":"pagenamee","ARTICLESPACE":"subjectspace","ԷՋԻ_ԼՐԻՎ_ԱՆՎԱՆՈՒՄԸ":"fullpagename","FULLPAGENAME":"fullpagename","ԷՋԻ_ԼՐԻՎ_ԱՆՎԱՆՈՒՄԸ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ԵՆԹԱԷՋԻ_ԱՆՎԱՆՈՒՄԸ":"subpagename","SUBPAGENAME":"subpagename","ԵՆԹԱԷՋԻ_ԱՆՎԱՆՈՒՄԸ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ՀԻՄՆԱԿԱՆ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ":"basepagename","BASEPAGENAME":"basepagename","ՀԻՄՆԱԿԱՆ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ՔՆՆԱՐԿՄԱՆ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ": +"talkpagename","TALKPAGENAME":"talkpagename","ՔՆՆԱՐԿՄԱՆ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","ՀՈԴՎԱԾԻ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ՀՈԴՎԱԾԻ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ՏԱՐԲԵՐԱԿԻ_ՀԱՄԱՐԸ":"revisionid","REVISIONID":"revisionid","ՏԱՐԲԵՐԱԿԻ_ՕՐԸ":"revisionday","REVISIONDAY":"revisionday","ՏԱՐԲԵՐԱԿԻ_ՕՐԸ_2":"revisionday2","REVISIONDAY2":"revisionday2","ՏԱՐԲԵՐԱԿԻ_ԱՄԻՍԸ":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ՏԱՐԲԵՐԱԿԻ_ՏԱՐԻՆ":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ԱՆՎԱՆԱՏԱՐԱԾՔ":"namespace","NAMESPACE":"namespace", +"ԱՆՎԱՆԱՏԱՐԱԾՔ_2":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","ՔՆՆԱՐԿՄԱՆ_ՏԱՐԱԾՔԸ":"talkspace","TALKSPACE":"talkspace","ՔՆՆԱՐԿՄԱՆ_ՏԱՐԱԾՔԸ_2":"talkspacee","TALKSPACEE":"talkspacee","ՀՈԴՎԱԾՆԵՐԻ_ՏԱՐԱԾՔԸ":"subjectspace","SUBJECTSPACE":"subjectspace","ՀՈԴՎԱԾՆԵՐԻ_ՏԱՐԱԾՔԸ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ՀՈԴՎԱԾՆԵՐԻ_ՔԱՆԱԿԸ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ՖԱՅԼԵՐԻ_ՔԱՆԱԿԸ":"numberoffiles","NUMBEROFFILES":"numberoffiles","ՄԱՍՆԱԿԻՑՆԵՐԻ_ՔԱՆԱԿԸ":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ԷՋԵՐԻ_ՔԱՆԱԿԸ":"numberofpages","NUMBEROFPAGES":"numberofpages","ԱԴՄԻՆՆԵՐԻ_ՔԱՆԱԿԸ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","ՑՈՒՅՑ_ՏԱԼ_ՎԵՐՆԱԳԻՐԸ": +"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ԸՆԹԱՑԻՔ_ԱՄԻՍԸ":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","ԸՆԹԱՑԻՔ_ԱՄՍՎԱ_ԱՆՈՒՆԸ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","ԸՆԹԱՑԻՔ_ԱՄՍՎԱ_ԱՆՈՒՆԸ_ՍԵՌ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ԸՆԹԱՑԻՔ_ԱՄՍՎԱ_ԱՆՎԱՆ_ՀԱՊԱՎՈՒՄԸ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ԸՆԹԱՑԻՔ_ՕՐԸ":"currentday","CURRENTDAY":"currentday","ԸՆԹԱՑԻՔ_ՕՐԸ_2":"currentday2","CURRENTDAY2":"currentday2","ԸՆԹԱՑԻՔ_ՕՐՎԱ_ԱՆՈՒՆԸ":"currentdayname","CURRENTDAYNAME":"currentdayname","ԸՆԹԱՑԻՔ_ՏԱՐԻՆ":"currentyear","CURRENTYEAR":"currentyear","ԸՆԹԱՑԻՔ_ԺԱՄԱՆԱԿԸ":"currenttime","CURRENTTIME":"currenttime","ԸՆԹԱՑԻՔ_ԺԱՄԸ":"currenthour","CURRENTHOUR":"currenthour","ՏԵՂԱԿԱՆ_ԱՄԻՍԸ": +"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","ՏԵՂԱԿԱՆ_ԱՄՍՎԱ_ԱՆՈՒՆԸ":"localmonthname","LOCALMONTHNAME":"localmonthname","ՏԵՂԱԿԱՆ_ԱՄՍՎԱ_ԱՆՈՒՆԸ_ՍԵՌ":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ՏԵՂԱԿԱՆ_ԱՄՍՎԱ_ԱՆՎԱՆ_ՀԱՊԱՎՈՒՄԸ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ՏԵՂԱԿԱՆ_ՕՐԸ":"localday","LOCALDAY":"localday","ՏԵՂԱԿԱՆ_ՕՐԸ_2":"localday2","LOCALDAY2":"localday2","ՏԵՂԱԿԱՆ_ՕՐՎԱ_ԱՆՈՒՆԸ":"localdayname","LOCALDAYNAME":"localdayname","ՏԵՂԱԿԱՆ_ՏԱՐԻՆ":"localyear","LOCALYEAR":"localyear","ՏԵՂԱԿԱՆ_ԺԱՄԱՆԱԿԸ":"localtime","LOCALTIME":"localtime","ՏԵՂԱԿԱՆ_ԺԱՄԸ":"localhour","LOCALHOUR":"localhour","ԿԱՅՔԻ_ԱՆՈՒՆԸ":"sitename","SITENAME":"sitename","ԸՆԹԱՑԻՔ_ՇԱԲԱԹԸ":"currentweek","CURRENTWEEK":"currentweek","ԸՆԹԱՑԻՔ_ՇԱԲԱԹՎԱ_ՕՐԸ":"currentdow", +"CURRENTDOW":"currentdow","ՏԵՂԱԿԱՆ_ՇԱԲԱԹՎԸ":"localweek","LOCALWEEK":"localweek","ՏԵՂԱԿԱՆ_ՇԱԲԱԹՎԱ_ՕՐԸ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ԸՆԹԱՑԻՔ_ՏԱՐԲԵՐԱԿԸ":"currentversion","CURRENTVERSION":"currentversion","ԸՆԹԱՑԻՔ_ԺԱՄԱՆԱԿԻ_ԴՐՈՇՄ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ՏԵՂԱԿԱՆ_ԺԱՄԱՆԱԿԻ_ԴՐՈՇՄ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","ՆԱՄԱԿԻ_ՈՒՂՂՈՒԹՅՈՒՆԸ":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ՊԱՐՈՒՆԱԿՈՒԹՅԱՆ_ԼԵԶՈՒՆ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև«»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hyw.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hyw.json new file mode 100644 index 0000000..3789acd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hyw.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__առանց_բով__":"notoc","__notoc__":"notoc","__առանց_սրահի__":"nogallery","__nogallery__":"nogallery","__ստիպել_բով__":"forcetoc","__forcetoc__":"forcetoc","__բով__":"toc","__toc__":"toc","__առանց_բաժնի_խմբագրման__":"noeditsection","__noeditsection__":"noeditsection", +"__առանց_վերնագրի_փոփոխման__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__առանց_պարունակության_փոփոխման__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ՀՂՈՒՄ_ՆՈՐ_ԲԱԺՆԻ_ՎՐԱ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ատ՝":"ns","ns":"ns","nse":"nse","մշակված_հասցե՛":"urlencode","urlencode":"urlencode","փոքրատառ_սկզբնատառ՝":"lcfirst","lcfirst":"lcfirst","մեծատառ_սկզբնատառ՝":"ucfirst","ucfirst":"ucfirst","փոքրատառ՝":"lc","lc":"lc","մեծատառ՝":"uc","uc":"uc", +"տեղական_հասցեն՝":"localurl","localurl":"localurl","տեղական_հասցեն_2՝":"localurle","localurle":"localurle","լրիվ_հասցեն՝":"fullurl","fullurl":"fullurl","լրիվ_հասցեն_2՝":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","ձեվել_թիվը":"formatnum","formatnum":"formatnum","հոլով՛":"grammar","grammar":"grammar","gender":"gender","հոգնակի՝":"plural","plural":"plural","bidi":"bidi","#լեզու՝":"language","#language":"language","լրացնել_ձախից":"padleft","padleft":"padleft","լրացնել_աջից":"padright","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","ներք՝":"int","int":"int","#սպասարկող":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates": +"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","սերվերը":"server","server":"server","սերվերի_անունը":"servername","servername":"servername","սքրիպտի_ճանապարհը":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ԷՋԵՐԻ_ՔԱՆԱԿԸ":"numberofpages","NUMBEROFPAGES":"numberofpages","ՄԱՍՆԱԿԻՑՆԵՐԻ_ՔԱՆԱԿԸ":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers", +"ՀՈԴՎԱԾՆԵՐԻ_ՔԱՆԱԿԸ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ՖԱՅԼԵՐԻ_ՔԱՆԱԿԸ":"numberoffiles","NUMBEROFFILES":"numberoffiles","ԱԴՄԻՆՆԵՐԻ_ՔԱՆԱԿԸ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","ԼՌՈՒԹՅԱՄԲ_ԴԱՍԱՎՈՐՈՒՄ՝":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ԱՆՎԱՆԱՏԱՐԱԾՔ_2":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","ՔՆՆԱՐԿՄԱՆ_ՏԱՐԱԾՔԸ":"talkspace","TALKSPACE":"talkspace","ՔՆՆԱՐԿՄԱՆ_ՏԱՐԱԾՔԸ_2":"talkspacee","TALKSPACEE":"talkspacee","ՀՈԴՎԱԾՆԵՐԻ_ՏԱՐԱԾՔԸ":"subjectspace","SUBJECTSPACE":"subjectspace" +,"ARTICLESPACE":"subjectspace","ՀՈԴՎԱԾՆԵՐԻ_ՏԱՐԱԾՔԸ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ԷՋԻ_ԱՆՈՒՆԸ":"pagename","PAGENAME":"pagename","ԷՋԻ_ԱՆՈՒՆԸ_2":"pagenamee","PAGENAMEE":"pagenamee","ԷՋԻ_ԼՐԻՎ_ԱՆՎԱՆՈՒՄԸ":"fullpagename","FULLPAGENAME":"fullpagename","ԷՋԻ_ԼՐԻՎ_ԱՆՎԱՆՈՒՄԸ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ՀԻՄՆԱԿԱՆ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ":"basepagename","BASEPAGENAME":"basepagename","ՀԻՄՆԱԿԱՆ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ԵՆԹԱԷՋԻ_ԱՆՎԱՆՈՒՄԸ":"subpagename","SUBPAGENAME":"subpagename","ԵՆԹԱԷՋԻ_ԱՆՎԱՆՈՒՄԸ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ՔՆՆԱՐԿՄԱՆ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ":"talkpagename","TALKPAGENAME":"talkpagename","ՔՆՆԱՐԿՄԱՆ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ_2":"talkpagenamee", +"TALKPAGENAMEE":"talkpagenamee","ՀՈԴՎԱԾԻ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ՀՈԴՎԱԾԻ_ԷՋԻ_ԱՆՎԱՆՈՒՄԸ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ՏԱՐԲԵՐԱԿԻ_ՀԱՄԱՐԸ":"revisionid","REVISIONID":"revisionid","ՏԱՐԲԵՐԱԿԻ_ՕՐԸ":"revisionday","REVISIONDAY":"revisionday","ՏԱՐԲԵՐԱԿԻ_ՕՐԸ_2":"revisionday2","REVISIONDAY2":"revisionday2","ՏԱՐԲԵՐԱԿԻ_ԱՄԻՍԸ":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ՏԱՐԲԵՐԱԿԻ_ՏԱՐԻՆ":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ԱՆՎԱՆԱՏԱՐԱԾՔ":"namespace","NAMESPACE":"namespace","ՑՈՒՅՑ_ՏԱԼ_ՎԵՐՆԱԳԻՐԸ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!", +"ԸՆԹԱՑԻՔ_ԱՄԻՍԸ":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","ԸՆԹԱՑԻՔ_ԱՄՍՎԱ_ԱՆՈՒՆԸ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","ԸՆԹԱՑԻՔ_ԱՄՍՎԱ_ԱՆՈՒՆԸ_ՍԵՌ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ԸՆԹԱՑԻՔ_ԱՄՍՎԱ_ԱՆՎԱՆ_ՀԱՊԱՎՈՒՄԸ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ԸՆԹԱՑԻՔ_ՕՐԸ":"currentday","CURRENTDAY":"currentday","ԸՆԹԱՑԻՔ_ՕՐԸ_2":"currentday2","CURRENTDAY2":"currentday2","ԸՆԹԱՑԻՔ_ՕՐՎԱ_ԱՆՈՒՆԸ":"currentdayname","CURRENTDAYNAME":"currentdayname","ԸՆԹԱՑԻՔ_ՏԱՐԻՆ":"currentyear","CURRENTYEAR":"currentyear","ԸՆԹԱՑԻՔ_ԺԱՄԱՆԱԿԸ":"currenttime","CURRENTTIME":"currenttime","ԸՆԹԱՑԻՔ_ԺԱՄԸ":"currenthour","CURRENTHOUR":"currenthour","ՏԵՂԱԿԱՆ_ԱՄԻՍԸ":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth", +"LOCALMONTH1":"localmonth1","ՏԵՂԱԿԱՆ_ԱՄՍՎԱ_ԱՆՈՒՆԸ":"localmonthname","LOCALMONTHNAME":"localmonthname","ՏԵՂԱԿԱՆ_ԱՄՍՎԱ_ԱՆՈՒՆԸ_ՍԵՌ":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ՏԵՂԱԿԱՆ_ԱՄՍՎԱ_ԱՆՎԱՆ_ՀԱՊԱՎՈՒՄԸ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ՏԵՂԱԿԱՆ_ՕՐԸ":"localday","LOCALDAY":"localday","ՏԵՂԱԿԱՆ_ՕՐԸ_2":"localday2","LOCALDAY2":"localday2","ՏԵՂԱԿԱՆ_ՕՐՎԱ_ԱՆՈՒՆԸ":"localdayname","LOCALDAYNAME":"localdayname","ՏԵՂԱԿԱՆ_ՏԱՐԻՆ":"localyear","LOCALYEAR":"localyear","ՏԵՂԱԿԱՆ_ԺԱՄԱՆԱԿԸ":"localtime","LOCALTIME":"localtime","ՏԵՂԱԿԱՆ_ԺԱՄԸ":"localhour","LOCALHOUR":"localhour","ԿԱՅՔԻ_ԱՆՈՒՆԸ":"sitename","SITENAME":"sitename","ԸՆԹԱՑԻՔ_ՇԱԲԱԹԸ":"currentweek","CURRENTWEEK":"currentweek","ԸՆԹԱՑԻՔ_ՇԱԲԱԹՎԱ_ՕՐԸ":"currentdow","CURRENTDOW":"currentdow","ՏԵՂԱԿԱՆ_ՇԱԲԱԹՎԸ":"localweek", +"LOCALWEEK":"localweek","ՏԵՂԱԿԱՆ_ՇԱԲԱԹՎԱ_ՕՐԸ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ԸՆԹԱՑԻՔ_ՏԱՐԲԵՐԱԿԸ":"currentversion","CURRENTVERSION":"currentversion","ԸՆԹԱՑԻՔ_ԺԱՄԱՆԱԿԻ_ԴՐՈՇՄ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ՏԵՂԱԿԱՆ_ԺԱՄԱՆԱԿԻ_ԴՐՈՇՄ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","ՆԱՄԱԿԻ_ՈՒՂՂՈՒԹՅՈՒՆԸ":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ՊԱՐՈՒՆԱԿՈՒԹՅԱՆ_ԼԵԶՈՒՆ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zաբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև«»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hz.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hz.json new file mode 100644 index 0000000..195d9f8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-hz.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry" +,"PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE" +:"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname", +"LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ia.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ia.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ia.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-id.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-id.json new file mode 100644 index 0000000..feaf106 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-id.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa": +"language","#bhs":"language","#language":"language","isikiri":"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","pendingchangelevel":"pendingchangelevel","#target":"target","#babel":"babel","#coordinates":"coordinates","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1": +"timel","#timel":"timel","#hitung":"expr","#expr":"expr","#rel2abs":"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort", +"HALAMANDIKATEGORI":"pagesincategory","HALDIKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME": +"rootpagename","ROOTPAGENAMEE":"rootpagenamee","NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE":"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid", +"HARIREVISI":"revisionday","HAREV":"revisionday","REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace","RUANGNAMAE":"namespacee","RUNAME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace", +"RUTAMA":"subjectspace","RUTIKEL":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","JUMLAHARTIKEL":"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits", +"NUMBEROFEDITS":"numberofedits","JUDULTAMPILAN":"displaytitle","JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME": +"currentdayname","TAHUNKINI":"currentyear","TAKIN":"currentyear","CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN":"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname", +"LOCALDAYNAME":"localdayname","TAHUNLOKAL":"localyear","TALOK":"localyear","LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK":"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK": +"directionmark","DIRMARK":"directionmark","BAHASAISI":"contentlanguage","BHSISI":"contentlanguage","BASI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ie.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ie.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ie.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ig.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ig.json new file mode 100644 index 0000000..99ef1f4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ig.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#ȯ_bú":"if","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#ógè":"time","#time":"time","#ógèl":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#óshíshíébéonọr":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT": +"pagesincategory","ÀSÁIHÜ":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","ÁHÀNÍLÉNKÈIHÜ":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE": +"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","ZIÍSHÍ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1": +"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ii.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ii.json new file mode 100644 index 0000000..c195b32 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ii.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__无目录__":"notoc","__notoc__":"notoc","__無目錄__":"notoc","__无图库__":"nogallery","__nogallery__":"nogallery","__無圖庫__":"nogallery","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__強制目錄__":"forcetoc","__目录__":"toc","__toc__":"toc","__目錄__":"toc","__无编辑段落__": +"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__無段落編輯__":"noeditsection","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換標題__":"notitleconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert","__不轉換內容__":"nocontentconvert"},{"__新段落链接__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__无新段落链接__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__隱藏分類__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__索引__":"index","__INDEX__":"index","__无索引__":"noindex","__NOINDEX__":"noindex","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__靜態重新導向__":"staticredirect","__NOGLOBAL__":"noglobal","__消除歧义__": +"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"名称空间":"ns","ns":"ns","命名空間":"ns","名字空间":"ns","名称空间e":"nse","nse":"nse","命名空間e":"nse","名字空间e":"nse","url编码":"urlencode","urlencode":"urlencode","小写首字":"lcfirst","lcfirst":"lcfirst","大写首字":"ucfirst","ucfirst":"ucfirst","小写":"lc","lc":"lc","大写":"uc","uc":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","完整url":"fullurl","fullurl":"fullurl","完整url等同":"fullurle","fullurle":"fullurle","规范url":"canonicalurl","canonicalurl":"canonicalurl","规范url等同":"canonicalurle","canonicalurle":"canonicalurle","格式化数字":"formatnum","formatnum":"formatnum","语法":"grammar","grammar":"grammar","性别":"gender","gender":"gender","性別":"gender","性":"gender","复数":"plural","plural":"plural","bidi":"bidi","#语言": +"language","#language":"language","#語言":"language","左填充":"padleft","padleft":"padleft","右填充":"padright","padright":"padright","锚编码":"anchorencode","anchorencode":"anchorencode","文件路径":"filepath","filepath":"filepath","页面id":"pageid","pageid":"pageid","頁面id":"pageid","界面":"int","int":"int","#特殊":"special","#special":"special","#特殊等同":"speciale","#speciale":"speciale","#标记":"tag","#tag":"tag","#格式化日期":"formatdate","#日期格式化":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#目标":"target","#目標":"target","#target":"target","#巴别":"babel","#巴別":"babel","#babel":"babel","#coordinates":"coordinates","#调用":"invoke","#調動":"invoke","#invoke":"invoke","#related":"related","#若":"if","#如果":"if","#非空式":"if","#if":"if","#若相等":"ifeq","#如果相等":"ifeq","#相同式":"ifeq","#匹配式":"ifeq","#ifeq":"ifeq","#开关":"switch","#转换":"switch","#多选式":"switch" +,"#多条件式":"switch","#双射式":"switch","#轉換":"switch","#switch":"switch","#若有":"ifexist","#如有":"ifexist","#存在式":"ifexist","#ifexist":"ifexist","#若表达式":"ifexpr","#若表達式":"ifexpr","#ifexpr":"ifexpr","#如果错误":"iferror","#错误式":"iferror","#如果錯誤":"iferror","#iferror":"iferror","#时间":"time","#時間":"time","#time":"time","#时间l":"timel","#時間l":"timel","#timel":"timel","#表达式":"expr","#计算式":"expr","#表達式":"expr","#expr":"expr","#rel2abs":"rel2abs","#标题组成部分":"titleparts","#titleparts":"titleparts","#分类树":"categorytree","#分類樹":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","无外部语言连接":"noexternallanglinks","隱藏跨語言連結":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#属性":"property","#屬性":"property","#property":"property","#statements": +"statements","#commaseparatedlist":"commaSeparatedList","条目路径":"articlepath","articlepath":"articlepath","服务器":"server","server":"server","伺服器":"server","服务器名":"servername","servername":"servername","伺服器名稱":"servername","脚本路径":"scriptpath","scriptpath":"scriptpath","样式路径":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wb报告名":"wbreponame","wb報表名稱":"wbreponame","wbreponame":"wbreponame"},{"组中用户数":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","默认排序":"defaultsort","默认排序关键字":"defaultsort","默认分类排序":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","分类中页面数":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","页面大小":"pagesize","PAGESIZE":"pagesize","保护级别":"protectionlevel","PROTECTIONLEVEL":"protectionlevel", +"PROTECTIONEXPIRY":"protectionexpiry","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","頁面名稱":"pagename","页面名等同":"pagenamee","页面名称等同":"pagenamee","PAGENAMEE":"pagenamee","页面全称":"fullpagename","完整页面名称":"fullpagename","FULLPAGENAME":"fullpagename","完整页面名称等同":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","子页面名称":"subpagename","SUBPAGENAME":"subpagename","子页面名称等同":"subpagenamee","SUBPAGENAMEE":"subpagenamee","根页面名称":"rootpagename","ROOTPAGENAME":"rootpagename","根頁面名稱":"rootpagename","根页面名称等同":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","根頁面名稱E":"rootpagenamee","基础页面名称":"basepagename","BASEPAGENAME":"basepagename","基础页面名称等同":"basepagenamee","BASEPAGENAMEE":"basepagenamee","讨论页面名称":"talkpagename","对话页面名称":"talkpagename","TALKPAGENAME":"talkpagename", +"讨论页面名称等同":"talkpagenamee","对话页面名称等同":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","主名字空间页面名称":"subjectpagename","条目页面名称":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主名字空间页面名称等同":"subjectpagenamee","条目页面名称等同":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","修订ID":"revisionid","REVISIONID":"revisionid","修订日":"revisionday","REVISIONDAY":"revisionday","修订日2":"revisionday2","REVISIONDAY2":"revisionday2","修订月":"revisionmonth","REVISIONMONTH":"revisionmonth","修订月1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","修订年":"revisionyear","REVISIONYEAR":"revisionyear","修订时间戳":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","修订用户":"revisionuser","REVISIONUSER":"revisionuser","修訂使用者":"revisionuser","级联来源": +"cascadingsources","CASCADINGSOURCES":"cascadingsources","名字空间":"namespace","NAMESPACE":"namespace","命名空間":"namespace","名字空间等同":"namespacee","NAMESPACEE":"namespacee","名字空间编号":"namespacenumber","NAMESPACENUMBER":"namespacenumber","命名空間數":"namespacenumber","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","對話空間":"talkspace","讨论空间等同":"talkspacee","讨论名字空间等同":"talkspacee","TALKSPACEE":"talkspacee","主名字空间":"subjectspace","条目名字空间":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主名字空间等同":"subjectspacee","条目名字空间等同":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","文章數":"numberofarticles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles","檔案數":"numberoffiles","用户数": +"numberofusers","NUMBEROFUSERS":"numberofusers","使用者人數量":"numberofusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","活躍使用者人數":"numberofactiveusers","页面数":"numberofpages","NUMBEROFPAGES":"numberofpages","頁面數":"numberofpages","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","管理員數":"numberofadmins","编辑数":"numberofedits","NUMBEROFEDITS":"numberofedits","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","顯示標題":"displaytitle","!":"!","=":"=","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","本月1":"currentmonth1","CURRENTMONTH1":"currentmonth1","本月名":"currentmonthname","本月名称":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","本月名属格":"currentmonthnamegen","本月名称属格":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月简称":"currentmonthabbrev" +,"CURRENTMONTHABBREV":"currentmonthabbrev","本月縮寫":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","今天2":"currentday2","CURRENTDAY2":"currentday2","星期":"currentdayname","今天名":"currentdayname","今天名称":"currentdayname","CURRENTDAYNAME":"currentdayname","今年":"currentyear","CURRENTYEAR":"currentyear","当前时间":"currenttime","此时":"currenttime","CURRENTTIME":"currenttime","目前時間":"currenttime","当前小时":"currenthour","CURRENTHOUR":"currenthour","本地月":"localmonth","本地月2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","本地月1":"localmonth1","LOCALMONTH1":"localmonth1","本地月份名":"localmonthname","LOCALMONTHNAME":"localmonthname","本地月历":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","本地月缩写":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","本地日":"localday","LOCALDAY":"localday","本地日2":"localday2","LOCALDAY2":"localday2", +"本地日名":"localdayname","LOCALDAYNAME":"localdayname","本地年":"localyear","LOCALYEAR":"localyear","本地时间":"localtime","LOCALTIME":"localtime","本地小时":"localhour","LOCALHOUR":"localhour","站点名称":"sitename","SITENAME":"sitename","網站名稱":"sitename","本周":"currentweek","CURRENTWEEK":"currentweek","当前DOW":"currentdow","CURRENTDOW":"currentdow","本地周":"localweek","LOCALWEEK":"localweek","本地DOW":"localdow","LOCALDOW":"localdow","修订大小":"revisionsize","REVISIONSIZE":"revisionsize","当前版本":"currentversion","CURRENTVERSION":"currentversion","目前版本":"currentversion","当前时间戳":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","本地时间戳":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","方向标记":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","內容語言": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^()(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ik.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ik.json new file mode 100644 index 0000000..1547270 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ik.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zġḷł̣ñŋ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ilo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ilo.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ilo.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-inh.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-inh.json new file mode 100644 index 0000000..94e42e2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-inh.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender","множественное_число":"plural","plural":"plural","bidi": +"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#деревокатегорий":"categorytree","#categorytree":"categorytree","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch", +"#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages", +"КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT": +"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename", +"FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE": +"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2": +"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow", +"CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-io.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-io.json new file mode 100644 index 0000000..dccf908 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-io.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__ni__":"notoc","__neindekso__":"notoc","__nt__":"notoc","__notoc__":"notoc","__ng__":"nogallery","__senbildaro__":"nogallery","__sb__":"nogallery","__sg__":"nogallery","__sengalerio__":"nogallery","__nogallery__":"nogallery","__fi__":"forcetoc","__fortuindekson__":"forcetoc","__ft__":"forcetoc","__forcetoc__":"forcetoc", +"__i__":"toc","__t__":"toc","__indekso__":"toc","__toc__":"toc","__srs__":"noeditsection","__nes__":"noeditsection","__senredaktisekciojn__":"noeditsection","__senredaktisekcion__":"noeditsection","__noeditsection__":"noeditsection","__nekonvertutitolon__":"notitleconvert","__nkt__":"notitleconvert","__ntc__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nekonvertuenhavon__":"nocontentconvert","__nkh__":"nocontentconvert","__ncc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIGILOALNOVASEKCIO__":"newsectionlink","__NSL__":"newsectionlink","__LNS__":"newsectionlink","__LANS__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__SENLIGILOALNOVASEKCIO__":"nonewsectionlink","__NNSL__":"nonewsectionlink","__SLNS__":"nonewsectionlink","__SLANS__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KK__":"hiddencat","__KAŜITAKATEGORIO__":"hiddencat","__KASXITAKATEGORIO__": +"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKSU__":"index","__INDEKSI__":"index","__INDEX__":"index","__NEINDEKSU__":"noindex","__NIU__":"noindex","__NOINDEX__":"noindex","__STATIKAALIDIREKTO__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nss":"nse","nso":"nse","nse":"nse","urlencode":"urlencode","malmajuskleunua":"lcfirst","minuskleunua":"lcfirst","mmu":"lcfirst","lcfirst":"lcfirst","majuskleunua":"ucfirst","malminuskleunua":"ucfirst","mu":"ucfirst","ucfirst":"ucfirst","malmajuskle":"lc","minuskle":"lc","lc":"lc","majuskle":"uc","malminuskle":"uc","uc":"uc","lokattt":"localurl","localurl":"localurl","lokatttt":"localurle","localurle":"localurle","plenaligilo":"fullurl","plenlig":"fullurl","tutattt":"fullurl","fullurl":"fullurl","plenaligiloo":"fullurle","plenligg":"fullurle" +,"tutatttt":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","gramatiko":"grammar","grammar":"grammar","sekso":"gender","gender":"gender","plurala":"plural","plural":"plural","bidi":"bidi","#lingvo":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","dosiero-voro":"filepath","dosiervojo":"filepath","filepath":"filepath","identigilodepaĝo":"pageid","paĝid":"pageid","pageid":"pageid","ene":"int","int":"int","#speciala":"special","#special":"special","#speciale":"speciale","#marko":"tag","#etikedo":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babelo":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seekv":"ifeq","#seekvacio":"ifeq","#seeglas":"ifeq","#laŭsamvalorade":"ifeq","#lauxsamvalorade":"ifeq","#ifeq":"ifeq","#ŝaltu":"switch","#ŝalti": +"switch","#sxaltu":"switch","#sxalti":"switch","#ŝalte":"switch","#sxalte":"switch","#switch":"switch","#seekzistas":"ifexist","#laŭasignade":"ifexist","#lauxasignade":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#seeksprimo":"ifexpr","#seesprimo":"ifexpr","#laŭevaluade":"ifexpr","#ifexpr":"ifexpr","#seeraras":"iferror","#laŭerarade":"iferror","#lauxerarade":"iferror","#iferror":"iferror","#tempo":"time","#datformu":"time","#time":"time","#tempoo":"timel","#lokaĵdatdonu":"timel","#lokajxdatdonu":"timel","#timel":"timel","#espr":"expr","#esprimo":"expr","#esprime":"expr","#expr":"expr","#absolutvojdonu":"rel2abs","#malrelativige":"rel2abs","#rel2abs":"rel2abs","#titolpartdonu":"titleparts","#titleparts":"titleparts","#kategoriarbo":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","neniuligiloalalialingvo":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eco": +"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikolapado":"articlepath","artikolavojo":"articlepath","articlepath":"articlepath","servilo":"server","server":"server","nomodeservilo":"servername","servilanomo":"servername","servilonomo":"servername","servername":"servername","skripto-vojo":"scriptpath","skriptovojo":"scriptpath","skriptvojo":"scriptpath","scriptpath":"scriptpath","stilo-vojo":"stylepath","stilovojo":"stylepath","stilvojo":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAŬLTORDIGO":"defaultsort","DEFAUXLTORDIGO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAĜOJENKATEGORIO":"pagesincategory","PAGXOJENKATEGORIO":"pagesincategory","PAĜOJENKAT":"pagesincategory","PAGXOJENKAT":"pagesincategory","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","PAĜOPEZO":"pagesize","PAGXOPEZO":"pagesize","PEZODEPAĜO":"pagesize","PEZODEPAGXO":"pagesize","PAGESIZE":"pagesize","PROTEKTONIVELO":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAĜONOMO":"pagename","PAGXONOMO":"pagename","PAĜNOMO":"pagename","PAGXNOMO":"pagename","PAGENAME":"pagename","PAĜONOMOO":"pagenamee","PAGXONOMOO":"pagenamee","PAĜNOMOO":"pagenamee","PAGXNOMOO":"pagenamee","PAGENAMEE":"pagenamee","TUTAPAĜONOMO":"fullpagename","TUTAPAGXONOMO":"fullpagename","TUTAPAĜNOMO":"fullpagename","TUTAPAGXNOMO":"fullpagename","FULLPAGENAME":"fullpagename","TUTAPAĜONOMOO":"fullpagenamee","TUTAPAGXONOMOO":"fullpagenamee","TUTAPAĜNOMOO":"fullpagenamee","TUTAPAGXNOMOO":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","SUBPAĜONOMO":"subpagename","SUBPAGXONOMO":"subpagename","SUBPAĜNOMO":"subpagename","SUBPAGXNOMO":"subpagename","SUBPAGENAME":"subpagename","SUBPAĜONOMOO":"subpagenamee", +"SUBPAGXONOMOO":"subpagenamee","SUBPAĜNOMOO":"subpagenamee","SUBPAGXNOMOO":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","PATRAPAĜONOMO":"basepagename","PATRAPAGXONOMO":"basepagename","PATRAPAĜNOMO":"basepagename","PATRAPAGXNOMO":"basepagename","BASEPAGENAME":"basepagename","PATRAPAĜONOMOO":"basepagenamee","PATRAPAGXONOMOO":"basepagenamee","PATRAPAĜNOMOO":"basepagenamee","PATRAPAGXNOMOO":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUTPAĜONOMO":"talkpagename","DISKUTPAGXONOMO":"talkpagename","DISKUTPAĜNOMO":"talkpagename","DISKUTPAGXNOMO":"talkpagename","TALKPAGENAME":"talkpagename","DISKUTPAĜONOMOO":"talkpagenamee","DISKUTPAGXONOMOO":"talkpagenamee","DISKUTPAĜNOMOO":"talkpagenamee","DISKUTPAGXNOMOO":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID": +"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","JARODEREVIZIO":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NOMSPACO":"namespace","NAMESPACE":"namespace","NOMSPACOO":"namespacee","NAMESPACEE":"namespacee","NUMERODENOMSPACO":"namespacenumber","NOMSPACNUMERO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUTNOMSPACO":"talkspace","TALKSPACE":"talkspace","DISKUTNOMSPACOO":"talkspacee","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBRODEARTIKOLOJ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBRODEDOSIEROJ":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBRODEUZANTOJ":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBRODEAKTIVAJUZANTOJ": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBRODEPAĜOJ":"numberofpages","NOMBRODEPAGXOJ":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBRODEADMINOJ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBRODEREDAKTOJ":"numberofedits","NUMBEROFEDITS":"numberofedits","MONTRUTITOLON":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","NUNAMONATO":"currentmonth","NUNAMONATO2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","NUNAMONATO1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NUNAMONATNOMO":"currentmonthname","NUNAMONATONOMO":"currentmonthname","NUNAMONATANOMO":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NUNAMONATNOMOGEN":"currentmonthnamegen","NUNAMONATONOMOGEN":"currentmonthnamegen","NUNAMONATANOMOGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NUNAMONATNOMOMAL":"currentmonthabbrev","NUNAMONATONOMOMAL":"currentmonthabbrev","NUNAMONATANOMOMAL":"currentmonthabbrev", +"CURRENTMONTHABBREV":"currentmonthabbrev","NUNATAGO":"currentday","CURRENTDAY":"currentday","NUNATAGO2":"currentday2","CURRENTDAY2":"currentday2","NUNATAGNOMO":"currentdayname","CURRENTDAYNAME":"currentdayname","NUNAJARO":"currentyear","CURRENTYEAR":"currentyear","NUNATEMPO":"currenttime","CURRENTTIME":"currenttime","NUNAHORO":"currenthour","CURRENTHOUR":"currenthour","LOKAMONATO":"localmonth","LOKAMONATO2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKAMONATO1":"localmonth1","LOCALMONTH1":"localmonth1","LOKAMONATNOMO":"localmonthname","LOKAMONATONOMO":"localmonthname","LOKAMONATANOMO":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKAMONATNOMOGEN":"localmonthnamegen","LOKAMONATONOMOGEN":"localmonthnamegen","LOKAMONATANOMOGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKAMONATNOMOMAL":"localmonthabbrev","LOKAMONATONOMOMAL":"localmonthabbrev","LOKAMONATANOMOMAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKATAGO": +"localday","LOCALDAY":"localday","LOKATAGO2":"localday2","LOCALDAY2":"localday2","LOKATAGNOMO":"localdayname","LOKATAGONOMO":"localdayname","LOKATAGANOMO":"localdayname","LOCALDAYNAME":"localdayname","LOKAJARO":"localyear","LOCALYEAR":"localyear","LOKATEMPO":"localtime","LOCALTIME":"localtime","LOKAHORO":"localhour","LOCALHOUR":"localhour","TTT-NOMO":"sitename","RETPAĜNOMO":"sitename","RETPAGXNOMO":"sitename","RETEJNOMO":"sitename","SITENAME":"sitename","NUNASEMAJNO":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOKASEMAJNO":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","NUNAVERSIO":"currentversion","CURRENTVERSION":"currentversion","NUNATEMPINDIKO":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKATEMPINDIKO":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ENHAVA-LINGVO":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-is.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-is.json new file mode 100644 index 0000000..c692561 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-is.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__emsafn__":"nogallery","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__": +"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","málfræði":"grammar","grammar":"grammar","gender":"gender","fltala":"plural","plural":"plural","bidi":"bidi","#tungumál":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#kerfissíða":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat": +"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","vefþj":"server","server":"server","vefþjnf":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"FJLSÍÐA":"numberofpages","NUMBEROFPAGES":"numberofpages","FJLNOT":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","FJLGREINA": +"numberofarticles","NUMBEROFARTICLES":"numberofarticles","FJLSKJALA":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","FJLBREYT":"numberofedits","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","SPJALLSVÆÐI":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","SÍÐUNAFN":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLTSÍÐUNF":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename", +"ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAFNSVÆÐI":"namespace","NAMESPACE":"namespace","SÝNATITIL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","NÚDAGUR": +"currentday","CURRENTDAY":"currentday","NÚDAGUR2":"currentday2","CURRENTDAY2":"currentday2","NÚDAGNAFN":"currentdayname","CURRENTDAYNAME":"currentdayname","NÚÁR":"currentyear","CURRENTYEAR":"currentyear","NÚTÍMI":"currenttime","CURRENTTIME":"currenttime","NÚKTÍMI":"currenthour","CURRENTHOUR":"currenthour","STMÁN":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","STMÁNNAFN":"localmonthname","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","STMÁNST":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","STDAGUR":"localday","LOCALDAY":"localday","STDAGUR2":"localday2","LOCALDAY2":"localday2","STDAGNAFN":"localdayname","LOCALDAYNAME":"localdayname","STÁR":"localyear","LOCALYEAR":"localyear","STTÍMI":"localtime","LOCALTIME":"localtime","STKTÍMI":"localhour","LOCALHOUR":"localhour","SITENAME":"sitename","NÚVIKA":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","STVIKA":"localweek", +"LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([áðéíóúýþæöa-z-–]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-it.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-it.json new file mode 100644 index 0000000..03b7a4a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-it.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag", +"#formatodata":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#alberocategorie":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath": +"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2", +"CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME" +:"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NOMESITO":"sitename","SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-iu.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-iu.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-iu.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ja.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ja.json new file mode 100644 index 0000000..1873a39 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ja.json @@ -0,0 +1,17 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__目次非表示__":"notoc","__目次非表示__":"notoc","__notoc__":"notoc","__ギャラリー非表示__":"nogallery","__ギャラリー非表示__":"nogallery","__nogallery__":"nogallery","__目次強制__":"forcetoc","__目次強制__":"forcetoc","__forcetoc__":"forcetoc","__目次__":"toc", +"__目次__":"toc","__toc__":"toc","__節編集非表示__":"noeditsection","__セクション編集非表示__":"noeditsection","__セクション編集非表示__":"noeditsection","__noeditsection__":"noeditsection","__タイトル変換無効__":"notitleconvert","__タイトルコンバート拒否__":"notitleconvert","__タイトルコンバート拒否__":"notitleconvert","__タイトル非表示__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__内容変換無効__":"nocontentconvert","__内容変換抑制__":"nocontentconvert","__内容変換抑制__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__新しい節リンク__":"newsectionlink","__新しいセクションリンク__":"newsectionlink","__新セクションリンク__":"newsectionlink","__新しいセクションリンク__":"newsectionlink","__新セクションリンク__": +"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__新しい節リンク非表示__":"nonewsectionlink","__新しいセクションリンク非表示__":"nonewsectionlink","__新しいセクションリンク非表示__":"nonewsectionlink","__新セクションリンク非表示__":"nonewsectionlink","__新セクションリンク非表示__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__カテゴリ非表示__":"hiddencat","__カテ非表示__":"hiddencat","__非表示カテ__":"hiddencat","__隠しカテゴリ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__インデックス__":"index","__インデックス__":"index","__INDEX__":"index","__インデックス拒否__":"noindex","__インデックス拒否__":"noindex","__NOINDEX__":"noindex","__静的転送__":"staticredirect","__二重転送解消無効__":"staticredirect","__二重転送解消無効__":"staticredirect", +"__二重転送修正無効__":"staticredirect","__二重転送修正無効__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"名前空間":"ns","名前空間:":"ns","名空":"ns","名空:":"ns","ns":"ns","名前空間e":"nse","nse":"nse","urlエンコード":"urlencode","urlエンコード:":"urlencode","urlencode":"urlencode","先頭小文字":"lcfirst","lcfirst":"lcfirst","先頭大文字":"ucfirst","ucfirst":"ucfirst","小文字":"lc","lc":"lc","大文字":"uc","uc":"uc","ローカルurl":"localurl","ローカルurl:":"localurl","localurl":"localurl","ローカルurle":"localurle","ローカルurle:":"localurle","localurle":"localurle","完全なurl":"fullurl","フルurl":"fullurl","完全なurl:":"fullurl","フルurl:":"fullurl","fullurl":"fullurl","完全なurle":"fullurle", +"フルurle":"fullurle","完全なurle:":"fullurle","フルurle:":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","数整形":"formatnum","formatnum":"formatnum","文法":"grammar","grammar":"grammar","性別":"gender","性別:":"gender","gender":"gender","複数":"plural","複数:":"plural","plural":"plural","bidi":"bidi","#言語":"language","#言語:":"language","#language":"language","補充左":"padleft","padleft":"padleft","補充右":"padright","padright":"padright","アンカー用エンコード":"anchorencode","anchorencode":"anchorencode","ファイルパス":"filepath","ファイルパス:":"filepath","filepath":"filepath","ページid":"pageid","ページid":"pageid","pageid":"pageid","インターフェース":"int","インタ":"int","インターフェース:":"int","インタ:":"int","インターフェイス":"int","インターフェイス:":"int","int":"int","#特別":"special", +"#special":"special","#speciale":"speciale","#タグ":"tag","#tag":"tag","#日付整形":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#バベル":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#もし":"if","#if":"if","#もし等しい":"ifeq","#ifeq":"ifeq","#切り替え":"switch","#switch":"switch","#もし存在":"ifexist","#ifexist":"ifexist","#もし式":"ifexpr","#ifexpr":"ifexpr","#もしエラー":"iferror","#iferror":"iferror","#時間":"time","#time":"time","#時間地方":"timel","#timel":"timel","#式":"expr","#expr":"expr","#参照から絶対":"rel2abs","#rel2abs":"rel2abs","#タイトル部分":"titleparts","#titleparts":"titleparts","#カテゴリツリー":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements": +"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","記事パス":"articlepath","articlepath":"articlepath","サーバー":"server","サーバ":"server","server":"server","サーバー名":"servername","サーバーネーム":"servername","サーバ名":"servername","サーバネーム":"servername","servername":"servername","スクリプトパス":"scriptpath","scriptpath":"scriptpath","スタイルパス":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"グループ人数":"numberingroup","グループ所属人数":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","デフォルトソート":"defaultsort","デフォルトソート:":"defaultsort","デフォルトソートキー":"defaultsort","デフォルトカテゴリソート":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","カテゴリ内ページ数":"pagesincategory", +"PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ページサイズ":"pagesize","PAGESIZE":"pagesize","保護レベル":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ページ名":"pagename","PAGENAME":"pagename","ページ名E":"pagenamee","ページ名E":"pagenamee","PAGENAMEE":"pagenamee","完全なページ名":"fullpagename","フルページ名":"fullpagename","完全な記事名":"fullpagename","完全記事名":"fullpagename","FULLPAGENAME":"fullpagename","完全なページ名E":"fullpagenamee","フルページ名E":"fullpagenamee","フルページ名E":"fullpagenamee","完全なページ名E":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","サブページ名":"subpagename","下位ページ名":"subpagename","SUBPAGENAME":"subpagename","サブページ名E":"subpagenamee","サブページ名E":"subpagenamee","下位ページ名E":"subpagenamee","下位ページ名E":"subpagenamee","SUBPAGENAMEE": +"subpagenamee","ルートページ名":"rootpagename","ROOTPAGENAME":"rootpagename","ルートページ名E":"rootpagenamee","ルートページ名E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","親ページ名":"basepagename","BASEPAGENAME":"basepagename","親ページ名E":"basepagenamee","親ページ名E":"basepagenamee","BASEPAGENAMEE":"basepagenamee","トークページ名":"talkpagename","会話ページ名":"talkpagename","TALKPAGENAME":"talkpagename","トークページ名E":"talkpagenamee","会話ページ名E":"talkpagenamee","会話ページ名E":"talkpagenamee","トークページ名E":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","主ページ名":"subjectpagename","記事ページ名":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主ページ名E":"subjectpagenamee","記事ページ名E":"subjectpagenamee","主ページ名E":"subjectpagenamee","記事ページ名E":"subjectpagenamee","SUBJECTPAGENAMEE": +"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","版のID":"revisionid","リビジョンID":"revisionid","差分ID":"revisionid","リビジョンID":"revisionid","差分ID":"revisionid","REVISIONID":"revisionid","版の日":"revisionday","リビジョン日":"revisionday","差分日":"revisionday","REVISIONDAY":"revisionday","版の日2":"revisionday2","リビジョン日2":"revisionday2","差分日2":"revisionday2","リビジョン日2":"revisionday2","差分日2":"revisionday2","REVISIONDAY2":"revisionday2","版の月":"revisionmonth","リビジョン月":"revisionmonth","差分月":"revisionmonth","REVISIONMONTH":"revisionmonth","版の月1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","版の年":"revisionyear","リビジョン年":"revisionyear","差分年":"revisionyear","REVISIONYEAR":"revisionyear","版のタイムスタンプ":"revisiontimestamp","リビジョンタイムスタンプ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp", +"版の利用者":"revisionuser","リビジョンユーザー":"revisionuser","リビジョンユーザ":"revisionuser","リビジョン利用者":"revisionuser","差分利用者":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","名前空間":"namespace","NAMESPACE":"namespace","名前空間E":"namespacee","名前空間E":"namespacee","NAMESPACEE":"namespacee","名前空間番号":"namespacenumber","NAMESPACENUMBER":"namespacenumber","トーク空間":"talkspace","ノート空間":"talkspace","会話空間":"talkspace","トークスペース":"talkspace","TALKSPACE":"talkspace","トーク空間E":"talkspacee","トーク空間E":"talkspacee","ノート空間E":"talkspacee","会話空間E":"talkspacee","ノート空間E":"talkspacee","会話空間E":"talkspacee","トークスペースE":"talkspacee","トークスペースE":"talkspacee","TALKSPACEE":"talkspacee","主空間":"subjectspace","標準空間":"subjectspace","記事空間": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主空間E":"subjectspacee","標準空間E":"subjectspacee","標準空間E":"subjectspacee","記事空間E":"subjectspacee","記事空間E":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","記事数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ファイル数":"numberoffiles","NUMBEROFFILES":"numberoffiles","利用者数":"numberofusers","NUMBEROFUSERS":"numberofusers","活動利用者数":"numberofactiveusers","有効な利用者数":"numberofactiveusers","有効利用者数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ページ数":"numberofpages","NUMBEROFPAGES":"numberofpages","管理者数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","編集回数":"numberofedits","NUMBEROFEDITS":"numberofedits","表示タイトル":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","現在の月":"currentmonth","協定月": +"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","現在の月1":"currentmonth1","協定月1":"currentmonth1","協定月1":"currentmonth1","CURRENTMONTH1":"currentmonth1","現在の月名":"currentmonthname","協定月名":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","現在の月属格":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","現在の月省略形":"currentmonthabbrev","省略協定月":"currentmonthabbrev","協定月省略":"currentmonthabbrev","協定月省略形":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","現在の日":"currentday","協定日":"currentday","CURRENTDAY":"currentday","現在の日2":"currentday2","協定日2":"currentday2","協定日2":"currentday2","CURRENTDAY2":"currentday2","現在の曜日名":"currentdayname","協定曜日":"currentdayname","CURRENTDAYNAME":"currentdayname","現在の年":"currentyear","協定年":"currentyear","CURRENTYEAR":"currentyear","現在の時刻" +:"currenttime","協定時間":"currenttime","協定時刻":"currenttime","CURRENTTIME":"currenttime","現在の時":"currenthour","協定時":"currenthour","CURRENTHOUR":"currenthour","地方時の月":"localmonth","現地月":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","地方時の月1":"localmonth1","現地月1":"localmonth1","現地月1":"localmonth1","LOCALMONTH1":"localmonth1","地方時の月名1":"localmonthname","現地月名":"localmonthname","LOCALMONTHNAME":"localmonthname","地方時の月属格":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","地方時の月省略形":"localmonthabbrev","省略現地月":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","地方時の日":"localday","現地日":"localday","ローカルデイ":"localday","LOCALDAY":"localday","地方時の日2":"localday2","現地日2":"localday2","現地日2":"localday2","LOCALDAY2":"localday2","地方時の曜日名":"localdayname","現地曜日": +"localdayname","ローカルデイネーム":"localdayname","LOCALDAYNAME":"localdayname","地方時の年":"localyear","現地年":"localyear","ローカルイヤー":"localyear","LOCALYEAR":"localyear","地方時の時刻":"localtime","現地時間":"localtime","ローカルタイム":"localtime","LOCALTIME":"localtime","地方時の時":"localhour","現地時":"localhour","LOCALHOUR":"localhour","サイト名":"sitename","サイトネーム":"sitename","SITENAME":"sitename","現在の週":"currentweek","CURRENTWEEK":"currentweek","現在の曜日番号":"currentdow","CURRENTDOW":"currentdow","地方時の週":"localweek","現地週":"localweek","ローカルウィーク":"localweek","LOCALWEEK":"localweek","地方時の曜日番号":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","現在のバージョン":"currentversion","ウィキバージョン":"currentversion","MediaWikiバージョン":"currentversion","メディアウィキバージョン":"currentversion", +"CURRENTVERSION":"currentversion","現在のタイムスタンプ":"currenttimestamp","協定タイムスタンプ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","地方時のタイムスタンプ":"localtimestamp","現地タイムスタンプ":"localtimestamp","ローカルタイムスタンプ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","方向印":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","内容言語":"contentlanguage","記事言語":"contentlanguage","プロジェクト言語":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/", +"linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-jam.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-jam.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-jam.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-jbo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-jbo.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-jbo.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-jv.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-jv.json new file mode 100644 index 0000000..16771f6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-jv.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa":"language","#bhs":"language","#language":"language","isikiri": +"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1":"timel","#timel":"timel","#hitung":"expr","#expr":"expr","#rel2abs": +"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHARTIKEL":"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES": +"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits","NUMBEROFEDITS":"numberofedits","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","HALAMANDIKATEGORI":"pagesincategory","HALDIKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","RUANGNAMAE":"namespacee","RUNAME": +"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace","RUTAMA":"subjectspace","RUTIKEL":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename", +"ROOTPAGENAMEE":"rootpagenamee","NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE": +"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid","HARIREVISI":"revisionday","HAREV":"revisionday","REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace","JUDULTAMPILAN":"displaytitle","JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle", +"!":"!","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME":"currentdayname","TAHUNKINI":"currentyear","TAKIN":"currentyear","CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN": +"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname","LOCALDAYNAME":"localdayname","TAHUNLOKAL":"localyear","TALOK":"localyear","LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK": +"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","BAHASAISI":"contentlanguage","BHSISI":"contentlanguage","BASI":"contentlanguage", +"CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ka.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ka.json new file mode 100644 index 0000000..1ebb956 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ka.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__უგალერეო__":"nogallery","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{ +"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#სპეციალური":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate": +"formatdate","#dateformat":"formatdate","#მიზანი":"target","#სამიზნე":"target","#target":"target","#ბაბილონი":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#დრო":"time","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#კატეგორიის_ხე":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","გვერდის_სახელი":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","გვერდის_სრული_სახელი":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday" +,"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","სახელთა_სივრცე":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","მიმდინარე_თვე":"currentmonth","მიმდინარე_თვე2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2": +"currentmonth","მიმდინარე_თვე1":"currentmonth1","CURRENTMONTH1":"currentmonth1","მიმდინარე_თვის_სახელი":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","მიმდინარე_თვის_სახელის_აბრევიატურა":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","მიმდინარე_დღე":"currentday","CURRENTDAY":"currentday","მიმდინარე_დღე2":"currentday2","CURRENTDAY2":"currentday2","მიმდინარე_დღის_სახელი":"currentdayname","CURRENTDAYNAME":"currentdayname","მიმდინარე_წელი":"currentyear","CURRENTYEAR":"currentyear","მიმდინარე_დრო":"currenttime","CURRENTTIME":"currenttime","მიმდინარე_საათი":"currenthour","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2": +"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","საიტის_სახელი":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zაბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰ“»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kaa.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kaa.json new file mode 100644 index 0000000..bc780b5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kaa.json @@ -0,0 +1,17 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__mazmunsiz__":"notoc","__msiz__":"notoc","__мазмұнсыз__":"notoc","__мсыз__":"notoc","__notoc__":"notoc","__qoýmasiz__":"nogallery","__qsiz__":"nogallery","__қоймасыз__":"nogallery","__қсыз__":"nogallery","__nogallery__":"nogallery","__mazmundatqizw__":"forcetoc","__mqizw__":"forcetoc", +"__мазмұндатқызу__":"forcetoc","__мқызу__":"forcetoc","__forcetoc__":"forcetoc","__mazmuni__":"toc","__mzmn__":"toc","__мазмұны__":"toc","__мзмн__":"toc","__toc__":"toc","__bölidimöndemew__":"noeditsection","__bölimöndetkizbew__":"noeditsection","__бөлідімөндемеу__":"noeditsection","__бөлімөндеткізбеу__":"noeditsection","__noeditsection__":"noeditsection","__taqiripatintürlendirgizbew__":"notitleconvert","__tatjoq__":"notitleconvert","__atawalmastirğizbaw__":"notitleconvert","__aabaw__":"notitleconvert","__тақырыпатынтүрлендіргізбеу__":"notitleconvert","__татжоқ__":"notitleconvert","__атауалмастырғызбау__":"notitleconvert","__аабау__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__mağlumatintürlendirgizbew__":"nocontentconvert","__matjoq__":"nocontentconvert","__mağlumatalmastirğizbaw__":"nocontentconvert","__mabaw__": +"nocontentconvert","__мағлұматынтүрлендіргізбеу__":"nocontentconvert","__матжоқ__":"nocontentconvert","__мағлұматалмастырғызбау__":"nocontentconvert","__мабау__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__JAÑABÖLİMSİLTEMESİ__":"newsectionlink","__ЖАҢАБӨЛІМСІЛТЕМЕСІ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__JASIRINSANAT__":"hiddencat","__ЖАСЫРЫНСАНАТ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ea":"ns","esimaya":"ns","еа":"ns","есімая":"ns","ns":"ns","nse":"nse","jaýdimuqamdaw":"urlencode","жайдымұқамдау":"urlencode","urlencode":"urlencode","kä1": +"lcfirst","kişiärippen1":"lcfirst","кә1":"lcfirst","кішіәріппен1":"lcfirst","lcfirst":"lcfirst","bä1":"ucfirst","basärippen1":"ucfirst","бә1":"ucfirst","басәріппен1":"ucfirst","ucfirst":"ucfirst","kä":"lc","kişiärippen":"lc","кә":"lc","кішіәріппен":"lc","lc":"lc","bä":"uc","basärippen":"uc","бә":"uc","басәріппен":"uc","uc":"uc","jergiliktijaý":"localurl","жергіліктіжай":"localurl","localurl":"localurl","jergiliktijaý2":"localurle","жергіліктіжай2":"localurle","localurle":"localurle","toliqjaýi":"fullurl","toliqjaý":"fullurl","толықжайы":"fullurl","толықжай":"fullurl","fullurl":"fullurl","toliqjaýi2":"fullurle","toliqjaý2":"fullurle","толықжайы2":"fullurle","толықжай2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","sanpişimi":"formatnum","санпішімі":"formatnum","formatnum":"formatnum","septigi":"grammar", +"septik":"grammar","септігі":"grammar","септік":"grammar","grammar":"grammar","gender":"gender","köpşetüri":"plural","köpşe":"plural","көпшетүрі":"plural","көпше":"plural","plural":"plural","bidi":"bidi","#til":"language","#тіл":"language","#language":"language","solğaiğis":"padleft","soliğis":"padleft","солғаығыс":"padleft","солығыс":"padleft","padleft":"padleft","oñğaiğis":"padright","oñiğis":"padright","оңғаығыс":"padright","оңығыс":"padright","padright":"padright","jäkirdimuqamdaw":"anchorencode","жәкірдімұқамдау":"anchorencode","anchorencode":"anchorencode","faýlmekeni":"filepath","файлмекені":"filepath","filepath":"filepath","pageid":"pageid","işki":"int","ішкі":"int","int":"int","#arnaýı":"special","#арнайы":"special","#special":"special","#speciale":"speciale","#belgi":"tag","#белгі":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate", +"#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","сервер":"server","server":"server","serveratawi":"servername","сервератауы":"servername","servername":"servername","ämirjoli":"scriptpath","әміржолы":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"BETSANI":"numberofpages","БЕТСАНЫ":"numberofpages","NUMBEROFPAGES":"numberofpages", +"QATISWŞISANI":"numberofusers","ҚАТЫСУШЫСАНЫ":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","MAQALASANI":"numberofarticles","МАҚАЛАСАНЫ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","FAÝLSANI":"numberoffiles","ФАЙЛСАНЫ":"numberoffiles","NUMBEROFFILES":"numberoffiles","ÄKİMŞİSANI":"numberofadmins","ӘКІМШІСАНЫ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ÖÑDEMESANI":"numberofedits","TÜZETWSANI":"numberofedits","ӨҢДЕМЕСАНЫ":"numberofedits","ТҮЗЕТУСАНЫ":"numberofedits","NUMBEROFEDITS":"numberofedits","ÄDEPKİSURIPTAW":"defaultsort","ÄDEPKİSANATSURIPTAW":"defaultsort","ÄDEPKİSURIPTAWKİLTİ":"defaultsort","ÄDEPKİSURIP":"defaultsort","ӘДЕПКІСҰРЫПТАУ":"defaultsort","ӘДЕПКІСАНАТСҰРЫПТАУ":"defaultsort","ӘДЕПКІСҰРЫПТАУКІЛТІ":"defaultsort", +"ӘДЕПКІСҰРЫП":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SANATTAĞIBETTER":"pagesincategory","САНАТТАҒЫБЕТТЕР":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BETMÖLŞERİ":"pagesize","БЕТМӨЛШЕРІ":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ESİMAYASI2":"namespacee","ЕСІМАЯСЫ2":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALQILAWAYASI":"talkspace","ТАЛҚЫЛАУАЯСЫ":"talkspace","TALKSPACE":"talkspace","TALQILAWAYASI2":"talkspacee","ТАЛҚЫЛАУАЯСЫ2":"talkspacee","TALKSPACEE":"talkspacee","TAQIRIPBETİ":"subjectspace","MAQALABETİ":"subjectspace","ТАҚЫРЫПБЕТІ":"subjectspace","МАҚАЛАБЕТІ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","TAQIRIPBETİ2":"subjectspacee","MAQALABETİ2": +"subjectspacee","ТАҚЫРЫПБЕТІ2":"subjectspacee","МАҚАЛАБЕТІ2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","BETATAWI":"pagename","БЕТАТАУЫ":"pagename","PAGENAME":"pagename","BETATAWI2":"pagenamee","БЕТАТАУЫ2":"pagenamee","PAGENAMEE":"pagenamee","TOLIQBETATAWI":"fullpagename","ТОЛЫҚБЕТАТАУЫ":"fullpagename","FULLPAGENAME":"fullpagename","TOLIQBETATAWI2":"fullpagenamee","ТОЛЫҚБЕТАТАУЫ2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","NEGİZGİBETATAWI":"basepagename","НЕГІЗГІБЕТАТАУЫ":"basepagename","BASEPAGENAME":"basepagename","NEGİZGİBETATAWI2":"basepagenamee","НЕГІЗГІБЕТАТАУЫ2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","BETŞEATAWI":"subpagename","ASTIÑĞIBETATAWI":"subpagename","БЕТШЕАТАУЫ":"subpagename","АСТЫҢҒЫБЕТАТАУЫ":"subpagename","SUBPAGENAME":"subpagename", +"BETŞEATAWI2":"subpagenamee","ASTIÑĞIBETATAWI2":"subpagenamee","БЕТШЕАТАУЫ2":"subpagenamee","АСТЫҢҒЫБЕТАТАУЫ2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","TALQILAWBETATAWI":"talkpagename","ТАЛҚЫЛАУБЕТАТАУЫ":"talkpagename","TALKPAGENAME":"talkpagename","TALQILAWBETATAWI2":"talkpagenamee","ТАЛҚЫЛАУБЕТАТАУЫ2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","TAQIRIPBETATAWI":"subjectpagename","MAQALABETATAWI":"subjectpagename","ТАҚЫРЫПБЕТАТАУЫ":"subjectpagename","МАҚАЛАБЕТАТАУЫ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","TAQIRIPBETATAWI2":"subjectpagenamee","MAQALABETATAWI2":"subjectpagenamee","ТАҚЫРЫПБЕТАТАУЫ2":"subjectpagenamee","МАҚАЛАБЕТАТАУЫ2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","TÜZETWNÖMİRİ":"revisionid","NUSQANÖMİRİ":"revisionid", +"ТҮЗЕТУНӨМІРІ":"revisionid","НҰСҚАНӨМІРІ":"revisionid","REVISIONID":"revisionid","TÜZETWKÜNİ":"revisionday","NUSQAKÜNİ":"revisionday","ТҮЗЕТУКҮНІ":"revisionday","НҰСҚАКҮНІ":"revisionday","REVISIONDAY":"revisionday","TÜZETWKÜNİ2":"revisionday2","NUSQAKÜNİ2":"revisionday2","ТҮЗЕТУКҮНІ2":"revisionday2","НҰСҚАКҮНІ2":"revisionday2","REVISIONDAY2":"revisionday2","TÜZETWAÝI":"revisionmonth","NUSQAAÝI":"revisionmonth","ТҮЗЕТУАЙЫ":"revisionmonth","НҰСҚААЙЫ":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","TÜZETWJILI":"revisionyear","NUSQAJILI":"revisionyear","ТҮЗЕТУЖЫЛЫ":"revisionyear","НҰСҚАЖЫЛЫ":"revisionyear","REVISIONYEAR":"revisionyear","TÜZETWWAQITITAÑBASI":"revisiontimestamp","NUSQAWAQITTÜÝİNDEMESİ":"revisiontimestamp","ТҮЗЕТУУАҚЫТЫТАҢБАСЫ":"revisiontimestamp","НҰСҚАУАҚЫТТҮЙІНДЕМЕСІ": +"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESİMAYASI":"namespace","ЕСІМАЯСЫ":"namespace","NAMESPACE":"namespace","KÖRİNETİNTAQIRIAPATI":"displaytitle","KÖRSETİLETİNATAW":"displaytitle","КӨРІНЕТІНТАҚЫРЫАПАТЫ":"displaytitle","КӨРСЕТІЛЕТІНАТАУ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","AĞIMDAĞIAÝ":"currentmonth","АҒЫМДАҒЫАЙ":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","AĞIMDAĞIAÝATAWI":"currentmonthname","АҒЫМДАҒЫАЙАТАУЫ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","AĞIMDAĞIAÝİLİKATAWI":"currentmonthnamegen","АҒЫМДАҒЫАЙІЛІКАТАУЫ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","AĞIMDAĞIAÝJÏIR":"currentmonthabbrev","AĞIMDAĞIAÝQISQA":"currentmonthabbrev","АҒЫМДАҒЫАЙЖИЫР": +"currentmonthabbrev","АҒЫМДАҒЫАЙҚЫСҚА":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","AĞIMDAĞIKÜN":"currentday","АҒЫМДАҒЫКҮН":"currentday","CURRENTDAY":"currentday","AĞIMDAĞIKÜN2":"currentday2","АҒЫМДАҒЫКҮН2":"currentday2","CURRENTDAY2":"currentday2","AĞIMDAĞIKÜNATAWI":"currentdayname","АҒЫМДАҒЫКҮНАТАУЫ":"currentdayname","CURRENTDAYNAME":"currentdayname","AĞIMDAĞIJIL":"currentyear","АҒЫМДАҒЫЖЫЛ":"currentyear","CURRENTYEAR":"currentyear","AĞIMDAĞIWAQIT":"currenttime","АҒЫМДАҒЫУАҚЫТ":"currenttime","CURRENTTIME":"currenttime","AĞIMDAĞISAĞAT":"currenthour","АҒЫМДАҒЫСАҒАТ":"currenthour","CURRENTHOUR":"currenthour","JERGİLİKTİAÝ":"localmonth","ЖЕРГІЛІКТІАЙ":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","JERGİLİKTİAÝATAWI":"localmonthname","ЖЕРГІЛІКТІАЙАТАУЫ":"localmonthname", +"LOCALMONTHNAME":"localmonthname","JERGİLİKTİAÝİLİKATAWI":"localmonthnamegen","ЖЕРГІЛІКТІАЙІЛІКАТАУЫ":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","JERGİLİKTİAÝJÏIR":"localmonthabbrev","JERGİLİKTİAÝQISQAŞA":"localmonthabbrev","JERGİLİKTİAÝQISQA":"localmonthabbrev","ЖЕРГІЛІКТІАЙЖИЫР":"localmonthabbrev","ЖЕРГІЛІКТІАЙҚЫСҚАША":"localmonthabbrev","ЖЕРГІЛІКТІАЙҚЫСҚА":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JERGİLİKTİKÜN":"localday","ЖЕРГІЛІКТІКҮН":"localday","LOCALDAY":"localday","JERGİLİKTİKÜN2":"localday2","ЖЕРГІЛІКТІКҮН2":"localday2","LOCALDAY2":"localday2","JERGİLİKTİKÜNATAWI":"localdayname","ЖЕРГІЛІКТІКҮНАТАУЫ":"localdayname","LOCALDAYNAME":"localdayname","JERGİLİKTİJIL":"localyear","ЖЕРГІЛІКТІЖЫЛ":"localyear","LOCALYEAR":"localyear","JERGİLİKTİWAQIT":"localtime", +"ЖЕРГІЛІКТІУАҚЫТ":"localtime","LOCALTIME":"localtime","JERGİLİKTİSAĞAT":"localhour","ЖЕРГІЛІКТІСАҒАТ":"localhour","LOCALHOUR":"localhour","TORAPATAWI":"sitename","ТОРАПАТАУЫ":"sitename","SITENAME":"sitename","AĞIMDAĞIAPTASI":"currentweek","AĞIMDAĞIAPTA":"currentweek","АҒЫМДАҒЫАПТАСЫ":"currentweek","АҒЫМДАҒЫАПТА":"currentweek","CURRENTWEEK":"currentweek","AĞIMDAĞIAPTAKÜNİ":"currentdow","АҒЫМДАҒЫАПТАКҮНІ":"currentdow","CURRENTDOW":"currentdow","JERGİLİKTİAPTASI":"localweek","JERGİLİKTİAPTA":"localweek","ЖЕРГІЛІКТІАПТАСЫ":"localweek","ЖЕРГІЛІКТІАПТА":"localweek","LOCALWEEK":"localweek","JERGİLİKTİAPTAKÜNİ":"localdow","ЖЕРГІЛІКТІАПТАКҮНІ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","BAĞDARLAMANUSQASI":"currentversion","БАҒДАРЛАМАНҰСҚАСЫ":"currentversion","CURRENTVERSION":"currentversion", +"AĞIMDAĞIWAQITTÜÝİNDEMESİ":"currenttimestamp","AĞIMDAĞIWAQITTÜÝİN":"currenttimestamp","АҒЫМДАҒЫУАҚЫТТҮЙІНДЕМЕСІ":"currenttimestamp","АҒЫМДАҒЫУАҚЫТТҮЙІН":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","JERGİLİKTİWAQITTÜÝİNDEMESİ":"localtimestamp","JERGİLİKTİWAQITTÜÝİN":"localtimestamp","ЖЕРГІЛІКТІУАҚЫТТҮЙІНДЕМЕСІ":"localtimestamp","ЖЕРГІЛІКТІУАҚЫТТҮЙІН":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","BAĞITBELGİSİ":"directionmark","БАҒЫТБЕЛГІСІ":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","MAĞLUMATTİLİ":"contentlanguage","МАҒЛҰМАТТІЛІ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^((?:[a-zıʼ’“»]|'(?!'))+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kab.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kab.json new file mode 100644 index 0000000..30830cb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kab.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode", +"initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int", +"#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property", +"#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGE":"pagename","PAGENAME":"pagename", +"NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid", +"REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee", +"ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL": +"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kbd.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kbd.json new file mode 100644 index 0000000..7576fde --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kbd.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюяӀ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kbp.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kbp.json new file mode 100644 index 0000000..30830cb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kbp.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode", +"initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int", +"#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property", +"#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGE":"pagename","PAGENAME":"pagename", +"NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid", +"REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee", +"ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL": +"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kcg.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kcg.json new file mode 100644 index 0000000..c67d1c5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kcg.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry" +,"PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE" +:"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname", +"LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z‌̱áí]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kg.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kg.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kg.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ki.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ki.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ki.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kj.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kj.json new file mode 100644 index 0000000..195d9f8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kj.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry" +,"PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE" +:"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname", +"LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kk.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kk.json new file mode 100644 index 0000000..61945e7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kk.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__мазмұнсыз__":"notoc","__мсыз__":"notoc","__notoc__":"notoc","__қоймасыз__":"nogallery","__қсыз__":"nogallery","__nogallery__":"nogallery","__мазмұндатқызу__":"forcetoc","__мқызу__":"forcetoc","__forcetoc__":"forcetoc","__мазмұны__":"toc","__мзмн__":"toc","__toc__":"toc" +,"__бөлідімөндемеу__":"noeditsection","__бөлімөндеткізбеу__":"noeditsection","__noeditsection__":"noeditsection","__тақырыпатынтүрлендіргізбеу__":"notitleconvert","__татжоқ__":"notitleconvert","__атауалмастырғызбау__":"notitleconvert","__аабау__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__мағлұматынтүрлендіргізбеу__":"nocontentconvert","__матжоқ__":"nocontentconvert","__мағлұматалмастырғызбау__":"nocontentconvert","__мабау__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ЖАҢАБӨЛІМСІЛТЕМЕСІ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__ЖАСЫРЫНСАНАТ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index", +"__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"еа":"ns","есімая":"ns","ns":"ns","nse":"nse","жайдымұқамдау":"urlencode","urlencode":"urlencode","кә1":"lcfirst","кішіәріппен1":"lcfirst","lcfirst":"lcfirst","бә1":"ucfirst","басәріппен1":"ucfirst","ucfirst":"ucfirst","кә":"lc","кішіәріппен":"lc","lc":"lc","бә":"uc","басәріппен":"uc","uc":"uc","жергіліктіжай":"localurl","localurl":"localurl","жергіліктіжай2":"localurle","localurle":"localurle","толықжайы":"fullurl","толықжай":"fullurl","fullurl":"fullurl","толықжайы2":"fullurle","толықжай2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","санпішімі":"formatnum","formatnum":"formatnum","септігі":"grammar","септік":"grammar","grammar":"grammar", +"gender":"gender","көпшетүрі":"plural","көпше":"plural","plural":"plural","bidi":"bidi","#тіл":"language","#language":"language","солғаығыс":"padleft","солығыс":"padleft","padleft":"padleft","оңғаығыс":"padright","оңығыс":"padright","padright":"padright","жәкірдімұқамдау":"anchorencode","anchorencode":"anchorencode","файлмекені":"filepath","filepath":"filepath","pageid":"pageid","ішкі":"int","int":"int","#арнайы":"special","#special":"special","#speciale":"speciale","#белгі":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section" +:"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","сервер":"server","server":"server","сервератауы":"servername","servername":"servername","әміржолы":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"БЕТСАНЫ":"numberofpages","NUMBEROFPAGES":"numberofpages","ҚАТЫСУШЫСАНЫ":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","МАҚАЛАСАНЫ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ФАЙЛСАНЫ":"numberoffiles","NUMBEROFFILES":"numberoffiles","ӘКІМШІСАНЫ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ӨҢДЕМЕСАНЫ":"numberofedits", +"ТҮЗЕТУСАНЫ":"numberofedits","NUMBEROFEDITS":"numberofedits","ӘДЕПКІСҰРЫПТАУ":"defaultsort","ӘДЕПКІСАНАТСҰРЫПТАУ":"defaultsort","ӘДЕПКІСҰРЫПТАУКІЛТІ":"defaultsort","ӘДЕПКІСҰРЫП":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","САНАТТАҒЫБЕТТЕР":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","БЕТМӨЛШЕРІ":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ЕСІМАЯСЫ2":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","ТАЛҚЫЛАУАЯСЫ":"talkspace","TALKSPACE":"talkspace","ТАЛҚЫЛАУАЯСЫ2":"talkspacee","TALKSPACEE":"talkspacee","ТАҚЫРЫПБЕТІ":"subjectspace","МАҚАЛАБЕТІ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ТАҚЫРЫПБЕТІ2": +"subjectspacee","МАҚАЛАБЕТІ2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","БЕТАТАУЫ":"pagename","PAGENAME":"pagename","БЕТАТАУЫ2":"pagenamee","PAGENAMEE":"pagenamee","ТОЛЫҚБЕТАТАУЫ":"fullpagename","FULLPAGENAME":"fullpagename","ТОЛЫҚБЕТАТАУЫ2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","НЕГІЗГІБЕТАТАУЫ":"basepagename","BASEPAGENAME":"basepagename","НЕГІЗГІБЕТАТАУЫ2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","БЕТШЕАТАУЫ":"subpagename","АСТЫҢҒЫБЕТАТАУЫ":"subpagename","SUBPAGENAME":"subpagename","БЕТШЕАТАУЫ2":"subpagenamee","АСТЫҢҒЫБЕТАТАУЫ2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ТАЛҚЫЛАУБЕТАТАУЫ":"talkpagename","TALKPAGENAME":"talkpagename","ТАЛҚЫЛАУБЕТАТАУЫ2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"ТАҚЫРЫПБЕТАТАУЫ":"subjectpagename","МАҚАЛАБЕТАТАУЫ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ТАҚЫРЫПБЕТАТАУЫ2":"subjectpagenamee","МАҚАЛАБЕТАТАУЫ2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ТҮЗЕТУНӨМІРІ":"revisionid","НҰСҚАНӨМІРІ":"revisionid","REVISIONID":"revisionid","ТҮЗЕТУКҮНІ":"revisionday","НҰСҚАКҮНІ":"revisionday","REVISIONDAY":"revisionday","ТҮЗЕТУКҮНІ2":"revisionday2","НҰСҚАКҮНІ2":"revisionday2","REVISIONDAY2":"revisionday2","ТҮЗЕТУАЙЫ":"revisionmonth","НҰСҚААЙЫ":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ТҮЗЕТУЖЫЛЫ":"revisionyear","НҰСҚАЖЫЛЫ":"revisionyear","REVISIONYEAR":"revisionyear","ТҮЗЕТУУАҚЫТЫТАҢБАСЫ":"revisiontimestamp", +"НҰСҚАУАҚЫТТҮЙІНДЕМЕСІ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ЕСІМАЯСЫ":"namespace","NAMESPACE":"namespace","КӨРІНЕТІНТАҚЫРЫАПАТЫ":"displaytitle","КӨРСЕТІЛЕТІНАТАУ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","АҒЫМДАҒЫАЙ":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","АҒЫМДАҒЫАЙАТАУЫ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","АҒЫМДАҒЫАЙІЛІКАТАУЫ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","АҒЫМДАҒЫАЙЖИЫР":"currentmonthabbrev","АҒЫМДАҒЫАЙҚЫСҚА":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","АҒЫМДАҒЫКҮН":"currentday","CURRENTDAY":"currentday","АҒЫМДАҒЫКҮН2":"currentday2","CURRENTDAY2":"currentday2", +"АҒЫМДАҒЫКҮНАТАУЫ":"currentdayname","CURRENTDAYNAME":"currentdayname","АҒЫМДАҒЫЖЫЛ":"currentyear","CURRENTYEAR":"currentyear","АҒЫМДАҒЫУАҚЫТ":"currenttime","CURRENTTIME":"currenttime","АҒЫМДАҒЫСАҒАТ":"currenthour","CURRENTHOUR":"currenthour","ЖЕРГІЛІКТІАЙ":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","ЖЕРГІЛІКТІАЙАТАУЫ":"localmonthname","LOCALMONTHNAME":"localmonthname","ЖЕРГІЛІКТІАЙІЛІКАТАУЫ":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ЖЕРГІЛІКТІАЙЖИЫР":"localmonthabbrev","ЖЕРГІЛІКТІАЙҚЫСҚАША":"localmonthabbrev","ЖЕРГІЛІКТІАЙҚЫСҚА":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ЖЕРГІЛІКТІКҮН":"localday","LOCALDAY":"localday","ЖЕРГІЛІКТІКҮН2":"localday2","LOCALDAY2":"localday2","ЖЕРГІЛІКТІКҮНАТАУЫ":"localdayname", +"LOCALDAYNAME":"localdayname","ЖЕРГІЛІКТІЖЫЛ":"localyear","LOCALYEAR":"localyear","ЖЕРГІЛІКТІУАҚЫТ":"localtime","LOCALTIME":"localtime","ЖЕРГІЛІКТІСАҒАТ":"localhour","LOCALHOUR":"localhour","ТОРАПАТАУЫ":"sitename","SITENAME":"sitename","АҒЫМДАҒЫАПТАСЫ":"currentweek","АҒЫМДАҒЫАПТА":"currentweek","CURRENTWEEK":"currentweek","АҒЫМДАҒЫАПТАКҮНІ":"currentdow","CURRENTDOW":"currentdow","ЖЕРГІЛІКТІАПТАСЫ":"localweek","ЖЕРГІЛІКТІАПТА":"localweek","LOCALWEEK":"localweek","ЖЕРГІЛІКТІАПТАКҮНІ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","БАҒДАРЛАМАНҰСҚАСЫ":"currentversion","CURRENTVERSION":"currentversion","АҒЫМДАҒЫУАҚЫТТҮЙІНДЕМЕСІ":"currenttimestamp","АҒЫМДАҒЫУАҚЫТТҮЙІН":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp", +"ЖЕРГІЛІКТІУАҚЫТТҮЙІНДЕМЕСІ":"localtimestamp","ЖЕРГІЛІКТІУАҚЫТТҮЙІН":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","БАҒЫТБЕЛГІСІ":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","МАҒЛҰМАТТІЛІ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zäçéğıïñöşüýʺʹа-яёәғіқңөұүһٴابپتجحدرزسشعفقكلمنڭەوۇۋۆىيچھ“»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kl.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kl.json new file mode 100644 index 0000000..a9bca0b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kl.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zæøå]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-km.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-km.json new file mode 100644 index 0000000..8449d9f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-km.json @@ -0,0 +1,10 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__លាក់មាតិកា__":"notoc","__លាក់បញ្ជីអត្ថបទ__":"notoc","__គ្មានមាតិកា__":"notoc","__គ្មានបញ្ជីអត្ថបទ__":"notoc","__កុំបង្ហាញមាតិកា__":"notoc","__notoc__":"notoc", +"__លាក់វិចិត្រសាល__":"nogallery","__nogallery__":"nogallery","__បង្ខំមាតិកា__":"forcetoc","__បង្ខំបញ្ជីអត្ថបទ__":"forcetoc","__បង្ខំអោយបង្ហាញមាតិកា__":"forcetoc","__forcetoc__":"forcetoc","__មាតិកា__":"toc","__បញ្ជីអត្ថបទ__":"toc","__toc__":"toc","__ផ្នែកមិនត្រូវកែប្រែ__":"noeditsection","__មិនមានផ្នែកកែប្រែ__":"noeditsection","__លាក់ផ្នែកកែប្រែ__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__តំនភ្ជាប់ផ្នែកថ្មី__":"newsectionlink","__តំណភ្ជាប់ផ្នែកថ្មី__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink", +"__NONEWSECTIONLINK__":"nonewsectionlink","__ចំណាត់ថ្នាក់ក្រុមមិនបានបង្ហាញ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__លិបិក្រម__":"index","__INDEX__":"index","__មិនមានលិបិក្រម__":"noindex","__NOINDEX__":"noindex","__ស្ថិតិទំព័របញ្ជូនបន្ត__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"លឈ":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","urlពេញ":"fullurl","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","វេយ្យាករណ៍":"grammar", +"grammar":"grammar","gender":"gender","ពហុវចនៈ":"plural","plural":"plural","bidi":"bidi","#ភាសា":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","ផ្លូវនៃឯកសារ":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#ពិសេស":"special","#special":"special","#speciale":"speciale","#ប្លាក":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property", +"#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","ម៉ាស៊ីនបម្រើសេវា":"server","server":"server","ឈ្មោះម៉ាស៊ីនបម្រើសេវា":"servername","servername":"servername","ផ្លូវស្រ្គីប":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","ចំនួនទំព័រក្នុងចំនាត់ថ្នាក់ក្រុម":"pagesincategory","ចំនួនទំព័រក្នុងចំណាត់ថ្នាក់ក្រុម":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ទំហំទំព័រ":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ឈ្មោះទំព័រ":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","ឈ្មោះទំព័រពេញ":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ឈ្មោះទំព័ររង":"subpagename","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","ឈ្មោះទំព័រពិភាក្សា":"talkpagename","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP": +"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","លំហឈ្មោះ":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","លំហឈ្មោះទំព័រពិភាក្សា":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ចំនួនអត្ថបទ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ចំនួនឯកសារ":"numberoffiles","NUMBEROFFILES":"numberoffiles","ចំនួនអ្នកប្រើប្រាស់":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ចំនួនទំព័រ":"numberofpages","NUMBEROFPAGES":"numberofpages","ចំនួនអ្នកអភិបាល":"numberofadmins", +"ចំនួនអ្នកថែទាំប្រព័ន្ធ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ចំនួនកំណែប្រែ":"numberofedits","NUMBEROFEDITS":"numberofedits","បង្ហាញចំណងជើង":"displaytitle","បង្ហាញចំនងជើង":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ខែនេះ":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","ឈ្មោះខែនេះ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","ថ្ងៃនេះ":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","ឈ្មោះថ្ងៃនេះ":"currentdayname","CURRENTDAYNAME":"currentdayname","ឆ្នាំនេះ":"currentyear","CURRENTYEAR":"currentyear","ពេលនេះ":"currenttime","CURRENTTIME":"currenttime", +"ម៉ោងនេះ":"currenthour","ម៉ោងឥឡូវ":"currenthour","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localyear","LOCALYEAR":"localyear","ពេលវេលាក្នុងតំបន់":"localtime","LOCALTIME":"localtime","ម៉ោងតំបន់":"localhour","LOCALHOUR":"localhour","ឈ្មោះវិបសាយ":"sitename","ឈ្មោះគេហទំព័រ":"sitename","SITENAME":"sitename","សប្ដាហ៍នេះ":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK": +"directionmark","កូដភាសា":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kn.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kn.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ko.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ko.json new file mode 100644 index 0000000..949065f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ko.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__목차숨김__":"notoc","__notoc__":"notoc","__갤러리숨김__":"nogallery","__화랑숨김__":"nogallery","__nogallery__":"nogallery","__목차보임__":"forcetoc","__목차표시__":"forcetoc","__forcetoc__":"forcetoc","__목차__":"toc","__toc__":"toc","__부분편집숨김__":"noeditsection","__문단편집숨김__": +"noeditsection","__단락편집숨김__":"noeditsection","__noeditsection__":"noeditsection","__제목변환없음__":"notitleconvert","__제변없음__":"notitleconvert","__제목변환안함__":"notitleconvert","__제변안함__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__내용변환없음__":"nocontentconvert","__내변없음__":"nocontentconvert","__내용변환안함__":"nocontentconvert","__내변안함__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__새문단쓰기__":"newsectionlink","__새글쓰기__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__새문단쓰기숨기기__":"nonewsectionlink","__새글쓰기숨기기__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__숨은분류__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__색인__":"index","__INDEX__":"index","__색인안함__":"noindex", +"__색인거부__":"noindex","__NOINDEX__":"noindex","__넘겨주기고정__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"이름":"ns","이름공간":"ns","ns":"ns","이름e":"nse","이름공간e":"nse","nse":"nse","주소인코딩":"urlencode","urlencode":"urlencode","첫소문자":"lcfirst","lcfirst":"lcfirst","첫대문자":"ucfirst","ucfirst":"ucfirst","소문자":"lc","lc":"lc","대문자":"uc","uc":"uc","지역주소":"localurl","localurl":"localurl","지역주소e":"localurle","localurle":"localurle","전체주소":"fullurl","fullurl":"fullurl","전체주소e":"fullurle","fullurle":"fullurle","표준주소":"canonicalurl","canonicalurl":"canonicalurl","표준주소e":"canonicalurle","canonicalurle":"canonicalurle","수형식":"formatnum","formatnum":"formatnum","문법":"grammar","grammar":"grammar","성별":"gender","gender":"gender","복수":"plural","복수형":"plural","plural": +"plural","bidi":"bidi","#언어":"language","#language":"language","대체왼쪽":"padleft","padleft":"padleft","대체오른쪽":"padright","padright":"padright","책갈피인코딩":"anchorencode","anchorencode":"anchorencode","파일경로":"filepath","그림경로":"filepath","filepath":"filepath","문서번호":"pageid","pageid":"pageid","인터페이스":"int","int":"int","#특수기능":"special","#special":"special","#특수기능e":"speciale","#speciale":"speciale","#태그":"tag","#tag":"tag","#날짜형식":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#분류트리":"categorytree","#categorytree":"categorytree","#대상":"target","#target":"target","#바벨":"babel","#babel":"babel","#coordinates":"coordinates","#호출":"invoke","#invoke":"invoke","#관련":"related","#related":"related","#을를":"Eul/Ruel","#eulruel":"Eul/Ruel","#은는":"Eun/Neun","#eunneun":"Eun/Neun","#이가":"E/Ga","#ega":"E/Ga","#과와":"Gwa/Wa","#gwawa":"Gwa/Wa","#아야": +"A/Ya","#aya":"A/Ya","#으로":"Euro/Ro","#euro":"Euro/Ro","#이":"E/","#e":"E/","#만약":"if","#if":"if","#만약일치":"ifeq","#ifeq":"ifeq","#스위치":"switch","#switch":"switch","#만약존재":"ifexist","#ifexist":"ifexist","#만약계산":"ifexpr","#ifexpr":"ifexpr","#만약오류":"iferror","#iferror":"iferror","#시간":"time","#time":"time","#현지시간":"timel","#timel":"timel","#수식":"expr","#expr":"expr","#상대를절대로":"rel2abs","#rel2abs":"rel2abs","#제목부분":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","바깥언어링크없음":"noexternallanglinks","바깥언어고리없음":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#속성":"property","#property":"property","#서술":"statements","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","항목경로":"articlepath","기사경로":"articlepath", +"articlepath":"articlepath","서버":"server","server":"server","서버이름":"servername","servername":"servername","스크립트경로":"scriptpath","scriptpath":"scriptpath","스타일경로":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","위키베이스저장소이름":"wbreponame","wbreponame":"wbreponame"},{"모든문서수":"numberofpages","NUMBEROFPAGES":"numberofpages","사용자수":"numberofusers","계정수":"numberofusers","NUMBEROFUSERS":"numberofusers","활동중인사용자수":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","문서수":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","파일수":"numberoffiles","그림수":"numberoffiles","NUMBEROFFILES":"numberoffiles","관리자수":"numberofadmins","NUMBEROFADMINS":"numberofadmins","권한별사용자수":"numberingroup","그룹별사용자수":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","편집수":"numberofedits","NUMBEROFEDITS": +"numberofedits","기본정렬":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","분류문서수":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","문서크기":"pagesize","PAGESIZE":"pagesize","보호수준":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","이름공간E":"namespacee","NAMESPACEE":"namespacee","이름공간수":"namespacenumber","NAMESPACENUMBER":"namespacenumber","토론이름공간":"talkspace","TALKSPACE":"talkspace","토론이름공간E":"talkspacee","TALKSPACEE":"talkspacee","본문서이름공간":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","본문서이름공간E":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","문서이름":"pagename","PAGENAME":"pagename","문서이름E":"pagenamee","PAGENAMEE":"pagenamee","전체문서이름":"fullpagename","FULLPAGENAME": +"fullpagename","전체문서이름E":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","최상위문서이름":"rootpagename","ROOTPAGENAME":"rootpagename","최상위문서이름E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","상위문서이름":"basepagename","BASEPAGENAME":"basepagename","상위문서이름E":"basepagenamee","BASEPAGENAMEE":"basepagenamee","하위문서이름":"subpagename","SUBPAGENAME":"subpagename","하위문서이름E":"subpagenamee","SUBPAGENAMEE":"subpagenamee","토론문서이름":"talkpagename","TALKPAGENAME":"talkpagename","토론문서이름E":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","본문서이름":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","본문서이름E":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","판번호":"revisionid","REVISIONID":"revisionid","판일":"revisionday","REVISIONDAY":"revisionday","판일2":"revisionday2","REVISIONDAY2": +"revisionday2","판월":"revisionmonth","REVISIONMONTH":"revisionmonth","판월1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","판년":"revisionyear","REVISIONYEAR":"revisionyear","판타임스탬프":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","판사용자":"revisionuser","REVISIONUSER":"revisionuser","연쇄식원본":"cascadingsources","계단식원본":"cascadingsources","CASCADINGSOURCES":"cascadingsources","이름공간":"namespace","NAMESPACE":"namespace","보일제목":"displaytitle","표시제목":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","현재월":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","현재월1":"currentmonth1","CURRENTMONTH1":"currentmonth1","현재월이름":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","현재월이름소유격":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","현재월이름약자":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev", +"현재일":"currentday","CURRENTDAY":"currentday","현재일2":"currentday2","CURRENTDAY2":"currentday2","현재요일":"currentdayname","CURRENTDAYNAME":"currentdayname","현재년":"currentyear","CURRENTYEAR":"currentyear","현재시각":"currenttime","현재시분":"currenttime","CURRENTTIME":"currenttime","현재시":"currenthour","CURRENTHOUR":"currenthour","지역월":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","지역월1":"localmonth1","LOCALMONTH1":"localmonth1","지역월이름":"localmonthname","LOCALMONTHNAME":"localmonthname","지역월이름소유격":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","지역월이름약자":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","지역일":"localday","LOCALDAY":"localday","지역일2":"localday2","LOCALDAY2":"localday2","지역요일":"localdayname","LOCALDAYNAME":"localdayname","지역년":"localyear","LOCALYEAR":"localyear","지역시분":"localtime","지역시각":"localtime", +"LOCALTIME":"localtime","지역시":"localhour","LOCALHOUR":"localhour","사이트이름":"sitename","SITENAME":"sitename","현재주":"currentweek","CURRENTWEEK":"currentweek","현재요일숫자":"currentdow","CURRENTDOW":"currentdow","지역주":"localweek","LOCALWEEK":"localweek","지역요일숫자":"localdow","LOCALDOW":"localdow","판크기":"revisionsize","REVISIONSIZE":"revisionsize","현재버전":"currentversion","CURRENTVERSION":"currentversion","현재타임스탬프":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","지역타임스탬프":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","명령검토":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","기본언어":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-koi.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-koi.json new file mode 100644 index 0000000..1cb65c6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-koi.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender", +"множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist": +"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ": +"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY": +"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour", +"МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename", +"ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kr.json new file mode 100644 index 0000000..195d9f8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kr.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry" +,"PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE" +:"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname", +"LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-krc.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-krc.json new file mode 100644 index 0000000..d468a0e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-krc.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__башласыз__":"notoc","__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","_галлереясыз__":"nogallery","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc", +"__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__":"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex", +"__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum", +"formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender","множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related", +"#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath", +"путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename", +"ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee", +"ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE": +"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS": +"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear", +"ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME": +"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"} +],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ks.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ks.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ks.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ksh.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ksh.json new file mode 100644 index 0000000..06e6fd8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ksh.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__kein_jallerie__":"nogallery","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__":"forcetoc","__enhallt__":"toc", +"__inhaltsverzeichnis__":"toc","__toc__":"toc","__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSHTOCHE_SAACHJRUPP__":"hiddencat","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__":"noindex","__NOINDEX__":"noindex" +,"__PERMANENTE_WEITERLEITUNG__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","lokale_url":"localurl","localurl":"localurl","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zahlenformat":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#shprooch":"language","#sproch": +"language","#sprache":"language","#language":"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright":"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorienbaum":"categorytree", +"#kategoriebaum":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx","#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE":"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SEITENNAME":"pagename","PAGENAME":"pagename","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSSIONSSEITE":"talkpagename","DISK": +"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","HAUPTSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR":"revisionyear", +"VERSIONSJAHR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","NAMENSRAUM":"namespace","NAMESPACE":"namespace","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DATEIANZAHL": +"numberoffiles","NUMBEROFFILES":"numberoffiles","BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTIVE_BENUTZER":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JETZIGER_MONATSNAME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JETZIGER_KALENDERTAG": +"currentday","JETZIGER_TAG":"currentday","CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2","JETZIGER_TAG_2":"currentday2","CURRENTDAY2":"currentday2","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV":"localmonthnamegen","LOKALER_MONATSNAME_GEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2", +"LOKALER_TAG_2":"localday2","LOCALDAY2":"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME":"localdayname","LOKALES_JAHR":"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize","JETZIGE_VERSION":"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK": +"directionmark","INHALTSSPRACHE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöüėëijßəğåůæœça-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ku.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ku.json new file mode 100644 index 0000000..a894669 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ku.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"_naveroktune_":"notoc","__notoc__":"notoc","_galerîtune_":"nogallery","__nogallery__":"nogallery","__forcetoc__":"forcetoc","_naverok_":"toc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__": +"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","rêziman.":"grammar","grammar":"grammar","zayend.":"gender","gender":"gender","pirrjimar":"plural","plural":"plural","bidi":"bidi","#ziman":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#taybet":"special","#special":"special","#speciale":"speciale","#tag":"tag", +"#formatdate":"formatdate","#dateformat":"formatdate","#dara_kategoriyan":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"HEJMARA_RÛPELAN":"numberofpages","NUMBEROFPAGES":"numberofpages","HEJMARA_BIKARHÊNERAN":"numberofusers","NUMBEROFUSERS":"numberofusers", +"HEJMARA_BIKARHÊNERÊN_ÇALAK":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","HEJMARA_GOTARAN":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","HEJMARA_DOSYEYAN":"numberoffiles","NUMBEROFFILES":"numberoffiles","HEJMARA_RÊVEBERAN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","HEJMARA_GUHERTINAN":"numberofedits","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME": +"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","BINRÛPEL":"subpagename","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MEHA_NIHA_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","ROJA_NIHA":"currentday","CURRENTDAY":"currentday","ROJA_NIHA2":"currentday2","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","DEMA_NIHA":"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","NAVÊ_PROJEYÊ":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIYONA_NIHA":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK": +"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zçêîşûẍḧÇÊÎŞÛẌḦ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kv.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kv.json new file mode 100644 index 0000000..1cb65c6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kv.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender", +"множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist": +"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ": +"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY": +"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour", +"МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename", +"ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kw.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kw.json new file mode 100644 index 0000000..f47656b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-kw.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__KLASSKUDHYS__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__MENEGVA__":"index","__INDEX__":"index","__HEBMENEGVA__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","urlleun":"fullurl","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","gramasek":"grammar","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#yeth":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","hynsanfolen":"filepath","filepath":"filepath","idanfolen":"pageid","pageid":"pageid","int":"int","#arbennek":"special","#special":"special","#speciale": +"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#gwedhenglass":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#mar":"if","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#termyn":"time","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","servyer":"server","server":"server","hanowanservyer":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NIVERAFOLENNOW":"numberofpages","NUMBEROFPAGES": +"numberofpages","NIVERADHEVNYDHYORYON":"numberofusers","NUMBEROFUSERS":"numberofusers","NIVERADHEVNYDHYORYONVYW":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NIVERAERTHYGLOW":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NIVERARESTRENNOW":"numberoffiles","NUMBEROFFILES":"numberoffiles","NIVERAVENYSTRORYON":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NIVERYNBAGAS":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NIVERAJANJYOW":"numberofedits","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","RESTRENNOWYNKLASS":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","MYNSANRESTREN":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE": +"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","HANOWANFOLEN":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","HANOWLEUNANFOLEN":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISKWEDHESANTITEL" +:"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","HANOWANWIASVA":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion", +"CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ky.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ky.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ky.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-la.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-la.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-la.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lad.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lad.json new file mode 100644 index 0000000..bdd056d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lad.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__sin_tdc__":"notoc","__notdc__":"notoc","__notoc__":"notoc","__sin_galería__":"nogallery","__nogalería__":"nogallery","__nogaleria__":"nogallery","__nogallery__":"nogallery","__forzar_tdc__":"forcetoc","__forzartdc__":"forcetoc","__forzartoc__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc","__toc__":"toc", +"__no_editar_sección__":"noeditsection","__noeditarsección__":"noeditsection","__noeditarseccion__":"noeditsection","__noeditsection__":"noeditsection","__noconvertirtitulo__":"notitleconvert","__noconvertirtítulo__":"notitleconvert","__noct___":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__noconvertircontenido__":"nocontentconvert","__nocc___":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__VINCULARANUEVASECCION__":"newsectionlink","__ENLACECREARSECCIÓN__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NOVINCULARANUEVASECCION__":"nonewsectionlink","__SINENLACECREARSECCIÓN__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATEGORÍAOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECCIÓNESTÁTICA__":"staticredirect", +"__REDIRECCIONESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIGUACION__":"disambiguation","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"en":"ns","ns":"ns","nse":"nse","codificarurl":"urlencode","urlencode":"urlencode","primerominus":"lcfirst","primerominús":"lcfirst","lcfirst":"lcfirst","primeromayus":"ucfirst","primeromayús":"ucfirst","ucfirst":"ucfirst","minus":"lc","minús":"lc","lc":"lc","mayus":"uc","mayús":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","urllocalc":"localurle","localurle":"localurle","urlcompleta":"fullurl","fullurl":"fullurl","urlcompletac":"fullurle","fullurle":"fullurle","urlcanonica":"canonicalurl","canonicalurl":"canonicalurl","urlcanonicac":"canonicalurle","canonicalurle":"canonicalurle","formatonúmero":"formatnum","formatonumero":"formatnum","formatnum":"formatnum","gramatica":"grammar","gramática":"grammar","grammar":"grammar","género":"gender","genero":"gender","gender": +"gender","plural":"plural","bidi":"bidi","#idioma":"language","#language":"language","rellenarizquierda":"padleft","rellenarizq":"padleft","padleft":"padleft","rellenarderecha":"padright","rellenarder":"padright","padright":"padright","anchorencode":"anchorencode","rutaarchivo":"filepath","rutarchivo":"filepath","rutadearchivo":"filepath","filepath":"filepath","iddepágina":"pageid","idpágina":"pageid","iddepagina":"pageid","idpagina":"pageid","pageid":"pageid","int":"int","#especial":"special","#special":"special","#especialc":"speciale","#speciale":"speciale","#etiqueta":"tag","#tag":"tag","#formatodefecha":"formatdate","#formatearfecha":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#árvoledekateggorías":"categorytree","#árboldecategorías":"categorytree","#arboldecategorias":"categorytree","#categorytree":"categorytree","#destino":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#invocar":"invoke","#invoke":"invoke","#related": +"related","#si":"if","#if":"if","#siigual":"ifeq","#ifeq":"ifeq","#asegún":"switch","#según":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierror":"iferror","#iferror":"iferror","#tiempo":"time","#time":"time","#tiempol":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","nointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propiedad":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","rutaartículo":"articlepath","rutaarticulo":"articlepath","articlepath":"articlepath","servidor":"server","server":"server","nombreservidor":"servername","servername":"servername","rutascript":"scriptpath","rutadescript":"scriptpath","scriptpath":"scriptpath","rutaestilo":"stylepath","rutadeestilo":"stylepath","stylepath": +"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NÚMERODEPÁGINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NÚMERODEARTÍCULOS":"numberofarticles","NUMERODEARTICULOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NÚMERODEARCHIVOS":"numberoffiles","NUMERODEARCHIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NÚMEROADMINIISTRADORES":"numberofadmins","NÚMEROADMINS":"numberofadmins","NUMEROADMINS":"numberofadmins","NUMEROADMINISTRADORES":"numberofadmins","NUMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINS":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NÚMEROENGRUPO": +"numberingroup","NUMEROENGRUPO":"numberingroup","NUMENGRUPO":"numberingroup","NÚMENGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NÚMERODEEDICIONES":"numberofedits","NUMERODEEDICIONES":"numberofedits","NUMBEROFEDITS":"numberofedits","ORDENAR":"defaultsort","CLAVEDEORDENPREDETERMINADO":"defaultsort","ORDENDECATEGORIAPREDETERMINADO":"defaultsort","ORDENDECATEGORÍAPREDETERMINADO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PÁGINASENCATEGORÍA":"pagesincategory","PÁGINASENCAT":"pagesincategory","PAGSENCAT":"pagesincategory","PAGINASENCATEGORIA":"pagesincategory","PAGINASENCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMAÑOPÁGINA":"pagesize","TAMAÑODEPÁGINA":"pagesize","TAMAÑOPAGINA":"pagesize","TAMAÑODEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECCIÓN":"protectionlevel","NIVELDEPROTECCION":"protectionlevel", +"PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ESPACIODENOMBREC":"namespacee","NAMESPACEE":"namespacee","NÚMERODELESPACIO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACIODEDISCUSION":"talkspace","ESPACIODEDISCUSIÓN":"talkspace","TALKSPACE":"talkspace","ESPACIODEDISCUSIONC":"talkspacee","TALKSPACEE":"talkspacee","ESPACIODEASUNTO":"subjectspace","ESPACIODETEMA":"subjectspace","ESPACIODEARTÍCULO":"subjectspace","ESPACIODEARTICULO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACIODETEMAC":"subjectspacee","ESPACIODEASUNTOC":"subjectspacee","ESPACIODEARTICULOC":"subjectspacee","ESPACIODEARTÍCULOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREDEPAGINA":"pagename","NOMBREDEPÁGINA":"pagename","PAGENAME":"pagename","NOMBREDEPAGINAC":"pagenamee","NOMBREDEPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMBREDEHOJACOMPLETA":"fullpagename","NOMBREDEPÁGINACOMPLETA": +"fullpagename","NOMBREDEPAGINACOMPLETA":"fullpagename","NOMBREDEPÁGINAENTERA":"fullpagename","NOMBREDEPAGINAENTERA":"fullpagename","NOMBRECOMPLETODEPÁGINA":"fullpagename","NOMBRECOMPLETODEPAGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMBRECOMPLETODEPAGINAC":"fullpagenamee","NOMBRECOMPLETODEPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMBREDEPAGINARAIZ":"rootpagename","NOMBREDEPÁGINARAÍZ":"rootpagename","ROOTPAGENAME":"rootpagename","NOMBREDEPAGINARAIZC":"rootpagenamee","NOMBREDEPÁGINARAÍZC":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBREDEPAGINABASE":"basepagename","NOMBREDEPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMBREDEPAGINABASEC":"basepagenamee","NOMBREDEPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMBREDEHOJICA":"subpagename","NOMBREDESUBPAGINA":"subpagename","NOMBREDESUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMBREDESUBPAGINAC":"subpagenamee","NOMBREDESUBPÁGINAC":"subpagenamee", +"SUBPAGENAMEE":"subpagenamee","NOMBREDEPÁGINADEDISCUSIÓN":"talkpagename","NOMBREDEPAGINADEDISCUSION":"talkpagename","NOMBREDEPAGINADISCUSION":"talkpagename","NOMBREDEPÁGINADISCUSIÓN":"talkpagename","TALKPAGENAME":"talkpagename","NOMBREDEPÁGINADEDISCUSIÓNC":"talkpagenamee","NOMBREDEPAGINADEDISCUSIONC":"talkpagenamee","NOMBREDEPAGINADISCUSIONC":"talkpagenamee","NOMBREDEPÁGINADISCUSIÓNC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMBREDEPAGINADETEMA":"subjectpagename","NOMBREDEPÁGINADETEMA":"subjectpagename","NOMBREDEPÁGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEARTICULO":"subjectpagename","NOMBREDEPÁGINADEARTÍCULO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMBREDEPAGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEARTICULOC": +"subjectpagenamee","NOMBREDEPÁGINADEARTÍCULOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDEREVISION":"revisionid","IDREVISION":"revisionid","IDDEREVISIÓN":"revisionid","IDREVISIÓN":"revisionid","REVISIONID":"revisionid","DIADEREVISION":"revisionday","DIAREVISION":"revisionday","DÍADEREVISIÓN":"revisionday","DÍAREVISIÓN":"revisionday","REVISIONDAY":"revisionday","DIADEREVISION2":"revisionday2","DIAREVISION2":"revisionday2","DÍADEREVISIÓN2":"revisionday2","DÍAREVISIÓN2":"revisionday2","REVISIONDAY2":"revisionday2","MESDEREVISION":"revisionmonth","MESDEREVISIÓN":"revisionmonth","MESREVISION":"revisionmonth","MESREVISIÓN":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDEREVISION1":"revisionmonth1","MESDEREVISIÓN1":"revisionmonth1","MESREVISION1":"revisionmonth1","MESREVISIÓN1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","AÑODEREVISION":"revisionyear","AÑODEREVISIÓN":"revisionyear","AÑOREVISION": +"revisionyear","AÑOREVISIÓN":"revisionyear","REVISIONYEAR":"revisionyear","MARCADEHORADEREVISION":"revisiontimestamp","MARCADEHORADEREVISIÓN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODEREVISION":"revisionuser","USUARIODEREVISIÓN":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACIODENOMBRE":"namespace","NAMESPACE":"namespace","MOSTRARTÍTULO":"displaytitle","MOSTRARTITULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","MESACTUAL":"currentmonth","MESACTUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MESACTUALCOMPLETO":"currentmonthname","NOMBREMESACTUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MESACTUALGENITIVO":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESACTUALABREVIADO":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DÍAACTUAL":"currentday","DIAACTUAL": +"currentday","DÍA_ACTUAL":"currentday","DIA_ACTUAL":"currentday","CURRENTDAY":"currentday","DÍAACTUAL2":"currentday2","DIAACTUAL2":"currentday2","DÍA_ACTUAL2":"currentday2","DIA_ACTUAL2":"currentday2","CURRENTDAY2":"currentday2","NOMBREDÍAACTUAL":"currentdayname","NOMBREDIAACTUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","AÑOACTUAL":"currentyear","AÑO_ACTUAL":"currentyear","CURRENTYEAR":"currentyear","HORA_MINUTOS_ACTUAL":"currenttime","HORAMINUTOSACTUAL":"currenttime","TIEMPOACTUAL":"currenttime","CURRENTTIME":"currenttime","HORAACTUAL":"currenthour","HORA_ACTUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","MESLOCALCOMPLETO":"localmonthname","NOMBREMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","MESLOCALGENITIVO":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREVIADO": +"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DÍALOCAL":"localday","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","DÍALOCAL2":"localday2","LOCALDAY2":"localday2","NOMBREDIALOCAL":"localdayname","NOMBREDÍALOCAL":"localdayname","LOCALDAYNAME":"localdayname","AÑOLOCAL":"localyear","LOCALYEAR":"localyear","HORAMINUTOSLOCAL":"localtime","TIEMPOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMBREDELSITIO":"sitename","SITENAME":"sitename","SEMANAACTUAL":"currentweek","CURRENTWEEK":"currentweek","DDSACTUAL":"currentdow","DIADESEMANAACTUAL":"currentdow","DÍADESEMANAACTUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DDSLOCAL":"localdow","DIADESEMANALOCAL":"localdow","DÍADESEMANALOCAL":"localdow","LOCALDOW":"localdow","TAMAÑODEREVISIÓN":"revisionsize","TAMAÑODEREVISION":"revisionsize","REVISIONSIZE":"revisionsize","VERSIONACTUAL":"currentversion","VERSIÓNACTUAL": +"currentversion","CURRENTVERSION":"currentversion","MARCADEHORAACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MARCADEHORALOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","IDIOMADELCONTENIDO":"contentlanguage","IDIOMADELCONT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záéíóúñ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lb.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lb.json new file mode 100644 index 0000000..99ff72c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lb.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__":"forcetoc","__inhaltsverzeichnis__":"toc","__toc__":"toc", +"__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSTOPPT_KATEGORIE__":"hiddencat","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__":"noindex","__NOINDEX__":"noindex","__PERMANENTE_WEITERLEITUNG__":"staticredirect", +"__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","lokale_url":"localurl","localurl":"localurl","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zueleformat":"formatnum","zahlenformat":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#sprooch":"language","#sprache":"language" +,"#language":"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright":"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorienbaum":"categorytree","#kategoriebaum":"categorytree", +"#categorytree":"categorytree","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx","#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eegeschaft":"property","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE":"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","Säitennumm":"pagename","SEITENNAME":"pagename","PAGENAME":"pagename","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee", +"DISKUSSIONSSEITE":"talkpagename","DISK":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","Haaptsäit":"subjectpagename","HAUPTSEITE":"subjectpagename","HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1", +"REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR":"revisionyear","VERSIONSJAHR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","Nummraum":"namespace","NAMENSRAUM":"namespace","NAMESPACE":"namespace","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","Haaptnummraum":"subjectspace","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee", +"ARTICLESPACEE":"subjectspacee","Artikelen":"numberofarticles","ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","Zuel_vu_Fichieren":"numberoffiles","DATEIANZAHL":"numberoffiles","NUMBEROFFILES":"numberoffiles","Benotzerzuel":"numberofusers","BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","Aktiv_Benotzer":"numberofactiveusers","AKTIVE_BENUTZER":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","Säitenzuel":"numberofpages","SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JETZIGER_MONATSNAME":"currentmonthname", +"CURRENTMONTHNAME":"currentmonthname","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JETZIGER_KALENDERTAG":"currentday","JETZIGER_TAG":"currentday","CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2","JETZIGER_TAG_2":"currentday2","CURRENTDAY2":"currentday2","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV": +"localmonthnamegen","LOKALER_MONATSNAME_GEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2","LOKALER_TAG_2":"localday2","LOCALDAY2":"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME":"localdayname","LOKALES_JAHR":"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize", +"AKTUELL_VERSIOUN":"currentversion","JETZIGE_VERSION":"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHALTSSPRACHE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöüßa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lbe.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lbe.json new file mode 100644 index 0000000..3e3096c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lbe.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender", +"множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist": +"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ": +"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY": +"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour", +"МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename", +"ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюяӀ1“»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lez.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lez.json new file mode 100644 index 0000000..7d7ca15 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lez.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__mündəricatyox__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__qalereyayox__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc", +"__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__":"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex", +"__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar": +"grammar","пол":"gender","gender":"gender","множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#деревокатегорий":"categorytree","#categorytree":"categorytree","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke", +"#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort", +"СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename", +"НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME": +"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!" +,"ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR": +"currenthour","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME": +"sitename","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lfn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lfn.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lfn.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lg.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lg.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lg.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-li.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-li.json new file mode 100644 index 0000000..1f11fdf --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-li.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__geeninhoud__":"notoc","__notoc__":"notoc","__geen_galerij__":"nogallery","__nogallery__":"nogallery","__inhoud_dwingen__":"forcetoc","__forceerinhoud__":"forcetoc","__forcetoc__":"forcetoc","__inhoud__":"toc","__toc__":"toc","__nietbewerkbaresectie__":"noeditsection","__noeditsection__":"noeditsection", +"__geenpaginanaamconversie__":"notitleconvert","__geentitelconversie__":"notitleconvert","__geentc__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__geeninhoudconversie__":"nocontentconvert","__geenic__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NIEUWESECTIELINK__":"newsectionlink","__NIEUWESECTIEKOPPELING__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__GEENNIEUWKOPJEKOPPELING__":"nonewsectionlink","__GEENNIEUWESECTIELINK__":"nonewsectionlink","__GEENNIEUWKOPJEVERWIJZING__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERBORGENCAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__GEENINDEX__":"noindex","__NOINDEX__":"noindex","__STATISCHEDOORVERWIJZING__":"staticredirect","__STATISCHEREDIRECT__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__": +"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nre":"nse","nse":"nse","urlcoderen":"urlencode","codeerurl":"urlencode","urlencode":"urlencode","kleerste":"lcfirst","lcfirst":"lcfirst","gleerste":"ucfirst","hleerste":"ucfirst","ucfirst":"ucfirst","kl":"lc","lc":"lc","hl":"uc","uc":"uc","lokaleurl":"localurl","localurl":"localurl","lokaleurle":"localurle","localurle":"localurle","volledigeurl":"fullurl","fullurl":"fullurl","volledigeurle":"fullurle","fullurle":"fullurle","canoiekeurl":"canonicalurl","canonicalurl":"canonicalurl","canoniekeurle":"canonicalurle","canonicalurle":"canonicalurle","formatteernum":"formatnum","numformatteren":"formatnum","formatnum":"formatnum","grammatica":"grammar","grammar":"grammar","geslacht":"gender","gender":"gender","meervoud":"plural","plural":"plural","bidi":"bidi","#taal":"language","#language":"language","linksopvullen":"padleft","padleft":"padleft","rechtsopvullen":"padright", +"padright":"padright","ankercoderen":"anchorencode","codeeranker":"anchorencode","anchorencode":"anchorencode","bestandspad":"filepath","filepath":"filepath","paginaid":"pageid","pageid":"pageid","int":"int","#speciaal":"special","#special":"special","#speciaale":"speciale","#speciale":"speciale","#label":"tag","#tag":"tag","#datumopmaak":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#aanroepen":"invoke","#invoke":"invoke","#related":"related","#als":"if","#if":"if","#alsgelijk":"ifeq","#ifeq":"ifeq","#schakelen":"switch","#switch":"switch","#alsbestaat":"ifexist","#ifexist":"ifexist","#alsexpressie":"ifexpr","#ifexpr":"ifexpr","#alsfout":"iferror","#iferror":"iferror","#tijd":"time","#time":"time","#tijdl":"timel","#timel":"timel","#expressie":"expr","#expr":"expr","#relatiefnaarabsoluut":"rel2abs","#rel2abs":"rel2abs","#paginanaamdelen":"titleparts","#titleparts":"titleparts","#categorieboom": +"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","geenexternetaalkoppelingen":"noexternallanglinks","geenexternetaalverwijzingen":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschap":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpad":"articlepath","articlepath":"articlepath","server":"server","servernaam":"servername","servername":"servername","scriptpad":"scriptpath","scriptpath":"scriptpath","stijlpad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"AANTALINGROEP":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","STANDAARDSORTERING":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINASINCATEGORIE":"pagesincategory","PAGINASINCAT": +"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGINAGROOTTE":"pagesize","PAGESIZE":"pagesize","BEVEILIGINGSNIVEAU":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGINANAAM":"pagename","PAGENAME":"pagename","PAGINANAAME":"pagenamee","PAGENAMEE":"pagenamee","VOLLEDIGEPAGINANAAM":"fullpagename","FULLPAGENAME":"fullpagename","VOLLEDIGEPAGINANAAME":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","DEELPAGINANAAM":"subpagename","SUBPAGENAME":"subpagename","DEELPAGINANAAME":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGINANAAM":"rootpagename","ROOTPAGENAME":"rootpagename","ROOTPAGINANAAME":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","BASISPAGINANAAM":"basepagename","BASEPAGENAME":"basepagename","BASISPAGINANAAME":"basepagenamee","BASEPAGENAMEE":"basepagenamee","OVERLEGPAGINANAAM":"talkpagename","TALKPAGENAME":"talkpagename","OVERLEGPAGINANAAME":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"ONDERWERPPAGINANAAM":"subjectpagename","ARTIKELPAGINANAAM":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ONDERWERPPAGINANAAME":"subjectpagenamee","ARTIKELPAGINANAAME":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","VERSIEID":"revisionid","REVISIONID":"revisionid","VERSIEDAG":"revisionday","REVISIONDAY":"revisionday","VERSIEDAG2":"revisionday2","REVISIONDAY2":"revisionday2","VERSIEMAAND":"revisionmonth","REVISIONMONTH":"revisionmonth","VERSIEMAAND1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","VERSIEJAAR":"revisionyear","REVISIONYEAR":"revisionyear","VERSIETIJD":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","VERSIEGEBRUIKER":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAAMRUIMTE":"namespace","NAMESPACE":"namespace","NAAMRUIMTEE":"namespacee","NAMESPACEE":"namespacee","NAAMRUIMTENUMMER":"namespacenumber","NAMESPACENUMBER": +"namespacenumber","OVERLEGRUIMTE":"talkspace","TALKSPACE":"talkspace","OVERLEGRUIMTEE":"talkspacee","TALKSPACEE":"talkspacee","ONDERWERPRUIMTE":"subjectspace","ARTIKELRUIMTE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ONDERWERPRUIMTEE":"subjectspacee","ARTIKELRUIMTEE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","AANTALARTIKELEN":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","AANTALBESTANDEN":"numberoffiles","NUMBEROFFILES":"numberoffiles","AANTALGEBRUIKERS":"numberofusers","NUMBEROFUSERS":"numberofusers","AANTALACTIEVEGEBRUIKERS":"numberofactiveusers","ACTIEVEGEBRUIKERS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","AANTALPAGINAS":"numberofpages","AANTALPAGINA'S":"numberofpages","AANTALPAGINA’S":"numberofpages","NUMBEROFPAGES":"numberofpages","AANTALBEHEERDERS":"numberofadmins","AANTALADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","AANTALBEWERKINGEN":"numberofedits", +"NUMBEROFEDITS":"numberofedits","WEERGEGEVENTITEL":"displaytitle","TOONTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","HUIDIGEMAAND":"currentmonth","HUIDIGEMAAND2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","HUIDIGEMAAND1":"currentmonth1","CURRENTMONTH1":"currentmonth1","HUIDIGEMAANDNAAM":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","HUIDIGEMAANDGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","HUIDIGEMAANDAFK":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HUIDIGEDAG":"currentday","CURRENTDAY":"currentday","HUIDIGEDAG2":"currentday2","CURRENTDAY2":"currentday2","HUIDIGEDAGNAAM":"currentdayname","CURRENTDAYNAME":"currentdayname","HUIDIGJAAR":"currentyear","CURRENTYEAR":"currentyear","HUIDIGETIJD":"currenttime","CURRENTTIME":"currenttime","HUIDIGUUR":"currenthour","CURRENTHOUR":"currenthour","PLAATSELIJKEMAAND":"localmonth","LOKALEMAAND":"localmonth","LOKALEMAAND2":"localmonth", +"LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALEMAAND1":"localmonth1","LOCALMONTH1":"localmonth1","PLAATSELIJKEMAANDNAAM":"localmonthname","LOKALEMAANDNAAM":"localmonthname","LOCALMONTHNAME":"localmonthname","PLAATSELIJKEMAANDNAAMGEN":"localmonthnamegen","LOKALEMAANDNAAMGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","PLAATSELIJKEMAANDAFK":"localmonthabbrev","LOKALEMAANDAFK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","PLAATSELIJKEDAG":"localday","LOKALEDAG":"localday","LOCALDAY":"localday","PLAATSELIJKEDAG2":"localday2","LOKALEDAG2":"localday2","LOCALDAY2":"localday2","PLAATSELIJKEDAGNAAM":"localdayname","LOKALEDAGNAAM":"localdayname","LOCALDAYNAME":"localdayname","PLAATSELIJKJAAR":"localyear","LOKAALJAAR":"localyear","LOCALYEAR":"localyear","PLAATSELIJKETIJD":"localtime","LOKALETIJD":"localtime","LOCALTIME":"localtime","PLAATSELIJKUUR":"localhour","LOKAALUUR":"localhour","LOCALHOUR":"localhour","SITENAAM":"sitename","SITENAME":"sitename", +"HUIDIGEWEEK":"currentweek","CURRENTWEEK":"currentweek","HUIDIGEDVDW":"currentdow","CURRENTDOW":"currentdow","PLAATSELIJKEWEEK":"localweek","LOKALEWEEK":"localweek","LOCALWEEK":"localweek","PLAATSELIJKEDVDW":"localdow","LOKALEDVDW":"localdow","LOCALDOW":"localdow","VERSIEGROOTTE":"revisionsize","REVISIONSIZE":"revisionsize","HUIDIGEVERSIE":"currentversion","CURRENTVERSION":"currentversion","HUIDIGETIJDSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","PLAATSELIJKETIJDSTEMPEL":"localtimestamp","LOKALETIJDSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","RICHTINGMARKERING":"directionmark","RICHTINGSMARKERING":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHOUDSTAAL":"contentlanguage","INHOUDTAAL":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zäöüïëéèà]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lij.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lij.json new file mode 100644 index 0000000..03b7a4a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lij.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag", +"#formatodata":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#alberocategorie":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath": +"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2", +"CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME" +:"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NOMESITO":"sitename","SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lld.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lld.json new file mode 100644 index 0000000..03b7a4a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lld.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag", +"#formatodata":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#alberocategorie":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath": +"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2", +"CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME" +:"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NOMESITO":"sitename","SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lmo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lmo.json new file mode 100644 index 0000000..67e3217 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lmo.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag","#formatodata":"formatdate","#formatdate":"formatdate", +"#dateformat":"formatdate","#alberocategorie":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE": +"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1", +"REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2","CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE": +"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME":"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NUMSIT":"sitename", +"NOMESITO":"sitename","SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ln.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ln.json new file mode 100644 index 0000000..620e0f2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ln.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode","initminus":"lcfirst","lcfirst":"lcfirst","initmajus": +"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int","#spécial":"special","#special":"special","#spéciale": +"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property","#property":"property","#statements":"statements", +"#commaseparatedlist":"commaSeparatedList","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","CLEFDETRI":"defaultsort", +"CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee","ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMPAGE":"pagename","PAGENAME":"pagename","NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename", +"FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid","REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday", +"REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev", +"JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL":"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL": +"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lo.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lo.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lrc.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lrc.json new file mode 100644 index 0000000..fd36a61 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lrc.json @@ -0,0 +1,16 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__بی‌فهرست__":"notoc","__notoc__":"notoc","__بی‌نگارخانه__":"nogallery","__nogallery__":"nogallery","__بافهرست__":"forcetoc","__forcetoc__":"forcetoc","__فهرست__":"toc","__toc__":"toc","__بی‌بخش__":"noeditsection","__noeditsection__":"noeditsection", +"__عنوان‌تبدیل‌نشده__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__محتواتبدیل‌نشده__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__بخش‌جدید__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__بی‌پیوندبخش__":"nonewsectionlink","__بی‌پیوند‌بخش‌جدید__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__رده‌پنهان__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__نمایه__":"index","__INDEX__":"index","__بی‌نمایه__":"noindex","__NOINDEX__":"noindex","__تغییرمسیرثابت__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"فن":"ns","ns":"ns","فنک":"nse","nse":"nse","کدنشانی": +"urlencode","urlencode":"urlencode","ابتداکوچک":"lcfirst","ابتدا_کوچک":"lcfirst","lcfirst":"lcfirst","ابتدابزرگ":"ucfirst","ابتدا_بزرگ":"ucfirst","ucfirst":"ucfirst","ک":"lc","lc":"lc","ب":"uc","uc":"uc","نشانی":"localurl","localurl":"localurl","نشانی‌کد":"localurle","نشانی_کد":"localurle","localurle":"localurle","نشانی‌کامل":"fullurl","نشانی_کامل":"fullurl","fullurl":"fullurl","نشانی‌کامل‌کد":"fullurle","نشانی_کامل_کد":"fullurle","fullurle":"fullurle","نشانی_استاندارد":"canonicalurl","نشانی‌استاندارد":"canonicalurl","canonicalurl":"canonicalurl","نشانی_استاندارد_کد":"canonicalurle","نشانی‌استانداردکد":"canonicalurle","canonicalurle":"canonicalurle","آرایش‌عدد":"formatnum","آرایش_عدد":"formatnum","formatnum":"formatnum","دستورزبان":"grammar","دستور_زبان":"grammar","grammar":"grammar", +"جنسیت":"gender","جنس":"gender","gender":"gender","جمع":"plural","plural":"plural","bidi":"bidi","#زبان":"language","#language":"language","لبه‌چپ":"padleft","لبه_چپ":"padleft","padleft":"padleft","لبه‌راست":"padright","لبه_راست":"padright","padright":"padright","کدلنگر":"anchorencode","anchorencode":"anchorencode","مسیرپرونده":"filepath","مسیر_پرونده":"filepath","filepath":"filepath","شناسه_صفحه":"pageid","pageid":"pageid","ترجمه":"int","int":"int","#ویژه":"special","#special":"special","#ویژه_ای":"speciale","#speciale":"speciale","#برچسب":"tag","#tag":"tag","#آرایش‌تاریخ":"formatdate","#آرایش_تاریخ":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#هدف":"target","#target":"target","#بابل":"babel","#babel":"babel","#coordinates":"coordinates","#درخواست":"invoke","#invoke":"invoke","#related":"related","#اگر":"if","#if":"if", +"#اگرمساوی":"ifeq","#ifeq":"ifeq","#گزینه":"switch","#switch":"switch","#اگرموجود":"ifexist","#ifexist":"ifexist","#اگرحساب":"ifexpr","#ifexpr":"ifexpr","#اگرخطا":"iferror","#iferror":"iferror","#زمان":"time","#time":"time","#زمان‌بلند":"timel","#timel":"timel","#حساب":"expr","#expr":"expr","#نسبی‌به‌مطلق":"rel2abs","#rel2abs":"rel2abs","#پاره‌عنوان":"titleparts","#titleparts":"titleparts","#درخت‌رده":"categorytree","#درخت_رده":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#ویژگی":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","مسیرمقاله":"articlepath","مسیر_مقاله":"articlepath","articlepath":"articlepath","سرور":"server","کارساز":"server","server":"server", +"نام‌کارساز":"servername","نام_کارساز":"servername","نام‌سرور":"servername","نام_سرور":"servername","servername":"servername","مسیرسند":"scriptpath","مسیر_سند":"scriptpath","scriptpath":"scriptpath","مسیرسبک":"stylepath","مسیر_سبک":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"تعداددرگروه":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ترتیب":"defaultsort","ترتیب‌پیش‌فرض":"defaultsort","ترتیب_پیش_فرض":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","صفحه‌دررده":"pagesincategory","صفحه_در_رده":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","اندازه‌صفحه":"pagesize","اندازه_صفحه":"pagesize","PAGESIZE":"pagesize","سطح‌حفاطت":"protectionlevel", +"سطح_حفاظت":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","نام‌صفحه":"pagename","نام_صفحه":"pagename","PAGENAME":"pagename","نام‌صفحه‌کد":"pagenamee","نام_صفحه_کد":"pagenamee","PAGENAMEE":"pagenamee","نام‌کامل‌صفحه":"fullpagename","نام_کامل_صفحه":"fullpagename","FULLPAGENAME":"fullpagename","نام‌کامل‌صفحه‌کد":"fullpagenamee","نام_کامل_صفحه_کد":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","نام‌زیرصفحه":"subpagename","نام_زیرصفحه":"subpagename","SUBPAGENAME":"subpagename","نام‌زیرصفحه‌کد":"subpagenamee","نام_زیرصفحه_کد":"subpagenamee","SUBPAGENAMEE":"subpagenamee","نام_صفحه_ریشه":"rootpagename","ROOTPAGENAME":"rootpagename","نام_صفحه_ریشه_ای":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","نام‌صفحه‌مبنا":"basepagename","نام_صفحه_مبنا": +"basepagename","BASEPAGENAME":"basepagename","نام‌صفحه‌مبناکد":"basepagenamee","نام_صفحه_مبنا_کد":"basepagenamee","BASEPAGENAMEE":"basepagenamee","نام‌صفحه‌بحث":"talkpagename","نام_صفحه_بحث":"talkpagename","TALKPAGENAME":"talkpagename","نام‌صفحه‌بحث‌کد":"talkpagenamee","نام_صفحه_بحث_کد":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","نام‌صفحه‌موضوع":"subjectpagename","نام‌صفحه‌مقاله":"subjectpagename","نام_صفحه_موضوع":"subjectpagename","نام_صفحه_مقاله":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","نام‌صفحه‌موضوع‌کد":"subjectpagenamee","نام‌صفحه‌مقاله‌کد":"subjectpagenamee","نام_صفحه_موضوع_کد":"subjectpagenamee","نام_صفحه_مقاله_کد":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","نسخه": +"revisionid","شماره‌نسخه":"revisionid","شماره_نسخه":"revisionid","REVISIONID":"revisionid","روزنسخه":"revisionday","روز_نسخه":"revisionday","REVISIONDAY":"revisionday","روزنسخه۲":"revisionday2","روز_نسخه۲":"revisionday2","روز_نسخه_۲":"revisionday2","REVISIONDAY2":"revisionday2","ماه‌نسخه":"revisionmonth","ماه_نسخه":"revisionmonth","REVISIONMONTH":"revisionmonth","ماه‌نسخه۱":"revisionmonth1","ماه_نسخه_۱":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","سال‌نسخه":"revisionyear","سال_نسخه":"revisionyear","REVISIONYEAR":"revisionyear","زمان‌یونیکسی‌نسخه":"revisiontimestamp","زمان‌نسخه":"revisiontimestamp","زمان_یونیکسی_نسخه":"revisiontimestamp","زمان_نسخه":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","کاربرنسخه":"revisionuser","کاربر_نسخه":"revisionuser","REVISIONUSER":"revisionuser", +"CASCADINGSOURCES":"cascadingsources","فضای‌نام":"namespace","فضای_نام":"namespace","NAMESPACE":"namespace","فضای‌نام‌کد":"namespacee","فضای_نام_کد":"namespacee","NAMESPACEE":"namespacee","شماره_فضای_نام":"namespacenumber","شماره‌فضای‌نام":"namespacenumber","NAMESPACENUMBER":"namespacenumber","فضای‌بحث":"talkspace","فضای_بحث":"talkspace","TALKSPACE":"talkspace","فضای‌بحث‌کد":"talkspacee","فضای_بحث_کد":"talkspacee","TALKSPACEE":"talkspacee","فضای‌موضوع":"subjectspace","فضای‌مقاله":"subjectspace","فضای_موضوع":"subjectspace","فضای_مقاله":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","فضای‌موضوع‌کد":"subjectspacee","فضای‌مقاله‌کد":"subjectspacee","فضای_موضوع_کد":"subjectspacee","فضای_مقاله_کد":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE": +"subjectspacee","تعدادمقاله‌ها":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","تعدادپرونده‌ها":"numberoffiles","NUMBEROFFILES":"numberoffiles","تعدادکاربران":"numberofusers","NUMBEROFUSERS":"numberofusers","کاربران‌فعال":"numberofactiveusers","کاربران_فعال":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","تعدادصفحه‌ها":"numberofpages","NUMBEROFPAGES":"numberofpages","تعدادمدیران":"numberofadmins","NUMBEROFADMINS":"numberofadmins","تعدادویرایش‌ها":"numberofedits","NUMBEROFEDITS":"numberofedits","عنوان‌ظاهری":"displaytitle","عنوان_ظاهری":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ماه":"currentmonth","ماه‌کنونی":"currentmonth","ماه_کنونی":"currentmonth","ماه‌کنونی۲":"currentmonth","ماه_کنونی۲":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth", +"ماه۱":"currentmonth1","ماه‌کنونی۱":"currentmonth1","ماه_کنونی۱":"currentmonth1","CURRENTMONTH1":"currentmonth1","نام‌ماه":"currentmonthname","نام_ماه":"currentmonthname","نام‌ماه‌کنونی":"currentmonthname","نام_ماه_کنونی":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","نام‌ماه‌اضافه":"currentmonthnamegen","نام_ماه_اضافه":"currentmonthnamegen","نام‌ماه‌کنونی‌اضافه":"currentmonthnamegen","نام_ماه_کنونی_اضافه":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","مخفف‌نام‌ماه":"currentmonthabbrev","مخفف_نام_ماه":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","روز":"currentday","روزکنونی":"currentday","روز_کنونی":"currentday","CURRENTDAY":"currentday","روز۲":"currentday2","روز_۲":"currentday2","CURRENTDAY2":"currentday2","نام‌روز":"currentdayname","نام_روز": +"currentdayname","CURRENTDAYNAME":"currentdayname","سال":"currentyear","سال‌کنونی":"currentyear","سال_کنونی":"currentyear","CURRENTYEAR":"currentyear","زمان‌کنونی":"currenttime","زمان_کنونی":"currenttime","CURRENTTIME":"currenttime","ساعت":"currenthour","ساعت‌کنونی":"currenthour","ساعت_کنونی":"currenthour","CURRENTHOUR":"currenthour","ماه‌محلی":"localmonth","ماه_محلی":"localmonth","ماه‌محلی۲":"localmonth","ماه_محلی۲":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","ماه‌محلی۱":"localmonth1","ماه_محلی۱":"localmonth1","LOCALMONTH1":"localmonth1","نام‌ماه‌محلی":"localmonthname","نام_ماه_محلی":"localmonthname","LOCALMONTHNAME":"localmonthname","نام‌ماه‌محلی‌اضافه":"localmonthnamegen","نام_ماه_محلی_اضافه":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","مخفف‌ماه‌محلی": +"localmonthabbrev","مخفف_ماه_محلی":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","روزمحلی":"localday","روز_محلی":"localday","LOCALDAY":"localday","روزمحلی۲":"localday2","روز_محلی_۲":"localday2","LOCALDAY2":"localday2","نام‌روزمحلی":"localdayname","نام_روز_محلی":"localdayname","LOCALDAYNAME":"localdayname","سال‌محلی":"localyear","سال_محلی":"localyear","LOCALYEAR":"localyear","زمان‌محلی":"localtime","زمان_محلی":"localtime","LOCALTIME":"localtime","ساعت‌محلی":"localhour","ساعت_محلی":"localhour","LOCALHOUR":"localhour","نام‌وبگاه":"sitename","نام_وبگاه":"sitename","SITENAME":"sitename","هفته":"currentweek","CURRENTWEEK":"currentweek","روزهفته":"currentdow","روز_هفته":"currentdow","CURRENTDOW":"currentdow","هفته‌محلی":"localweek","هفته_محلی":"localweek","LOCALWEEK":"localweek","روزهفته‌محلی": +"localdow","روز_هفته_محلی":"localdow","LOCALDOW":"localdow","اندازهٔ‌نسخه":"revisionsize","اندازهٔ_نسخه":"revisionsize","REVISIONSIZE":"revisionsize","نسخه‌کنونی":"currentversion","نسخه_کنونی":"currentversion","CURRENTVERSION":"currentversion","زمان‌یونیکسی":"currenttimestamp","زمان_یونیکسی":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","زمان‌یونیکسی‌محلی":"localtimestamp","زمان_یونیکسی_محلی":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","علامت‌جهت":"directionmark","علامت_جهت":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","زبان‌محتوا":"contentlanguage","زبان_محتوا":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیآأئؤة‌]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lt.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lt.json new file mode 100644 index 0000000..444eb9d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lt.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__beturin__":"notoc","__notoc__":"notoc","__begalerijos__":"nogallery","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__turinys__":"toc","__toc__":"toc","__beredagsekc__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__": +"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree": +"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"PUSLAPIŲSKAIČIUS":"numberofpages","NUMBEROFPAGES":"numberofpages","NAUDOTOJŲSKAIČIUS":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","STRAIPSNIŲSKAIČIUS":"numberofarticles","NUMBEROFARTICLES": +"numberofarticles","FAILŲSKAIČIUS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","KEITIMŲSKAIČIUS":"numberofedits","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME": +"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","DABARTINISMĖNESIS":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","DABARTINIOMĖNESIOPAVADINIMAS":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","DABARTINĖDIENA":"currentday","CURRENTDAY":"currentday","DABARTINĖDIENA2":"currentday2", +"CURRENTDAY2":"currentday2","DABARTINĖSDIENOSPAVADINIMAS":"currentdayname","CURRENTDAYNAME":"currentdayname","DABARTINIAIMETAI":"currentyear","CURRENTYEAR":"currentyear","DABARTINISLAIKAS":"currenttime","CURRENTTIME":"currenttime","DABARTINĖVALANDA":"currenthour","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG" +:"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-ząčęėįšųūž]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ltg.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ltg.json new file mode 100644 index 0000000..ae0d483 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ltg.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zA-ZĀāČčĒēĢģĪīĶķĻļŅņŠšŪūŽž]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lv.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lv.json new file mode 100644 index 0000000..ae0d483 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-lv.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zA-ZĀāČčĒēĢģĪīĶķĻļŅņŠšŪūŽž]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mad.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mad.json new file mode 100644 index 0000000..04dbf3d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mad.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa": +"language","#bhs":"language","#language":"language","isikiri":"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1":"timel","#timel":"timel","#hitung":"expr", +"#expr":"expr","#rel2abs":"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","HALAMANDIKATEGORI":"pagesincategory","HALDIKAT": +"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee", +"NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE":"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid","HARIREVISI":"revisionday","HAREV":"revisionday" +,"REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace","RUANGNAMAE":"namespacee","RUNAME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace","RUTAMA":"subjectspace","RUTIKEL":"subjectspace", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","JUMLAHARTIKEL":"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits","NUMBEROFEDITS":"numberofedits","JUDULTAMPILAN":"displaytitle" +,"JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME":"currentdayname","TAHUNKINI":"currentyear","TAKIN":"currentyear", +"CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN":"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname","LOCALDAYNAME":"localdayname","TAHUNLOKAL":"localyear","TALOK":"localyear", +"LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK":"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","BAHASAISI":"contentlanguage", +"BHSISI":"contentlanguage","BASI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mai.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mai.json new file mode 100644 index 0000000..501d412 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mai.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__बिना_अनुक्रम__":"notoc","__विषय_सूची_हीन__":"notoc","__notoc__":"notoc","__गैलरी_नहीं__":"nogallery","__nogallery__":"nogallery","__अनुक्रम_दिखाएँ__":"forcetoc","__विषय_सूची_दिखाएँ__":"forcetoc", +"__विषय_सूची_दिखायें__":"forcetoc","__forcetoc__":"forcetoc","__अनुक्रम__":"toc","__विषय_सूची__":"toc","__toc__":"toc","__अनुभाग_सम्पादन_नहीं__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__विषय_जोड़ें_कड़ी__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__विषय_जोड़े_कड़ी_रहित__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__छुपी_श्रेणी__":"hiddencat","__छिपी_श्रेणी__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__सूचीबद्ध__":"index","__INDEX__":"index","__असूचीबद्ध__":"noindex","__NOINDEX__":"noindex", +"__स्थिर_पुनर्प्रेषण__":"staticredirect","__स्थिर_अनुप्रेषण__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"नामस्थान":"ns","ns":"ns","नामस्थान_कोड":"nse","nse":"nse","यू_आर_एल_कोड":"urlencode","urlencode":"urlencode","छोटे_अक्षर_से_शुरू":"lcfirst","lcfirst":"lcfirst","बड़े_अक्षर_से_शुरू":"ucfirst","ucfirst":"ucfirst","छोटे_अक्षर":"lc","lc":"lc","बड़े_अक्षर":"uc","uc":"uc","स्थानीय_यू_आर_एल":"localurl","localurl":"localurl","स्थानीय_यू_आर_एल_कोड":"localurle","localurle":"localurle","पूर्ण_यू_आर_एल":"fullurl","fullurl":"fullurl","पूर्ण_यू_आर_एल_कोड":"fullurle","fullurle":"fullurle", +"मानक_यू_आर_एल":"canonicalurl","canonicalurl":"canonicalurl","मानक_यू_आर_एल_कोड":"canonicalurle","canonicalurle":"canonicalurle","संख्या_रूप":"formatnum","formatnum":"formatnum","व्याकरण":"grammar","grammar":"grammar","लिंग":"gender","gender":"gender","वचन":"plural","plural":"plural","bidi":"bidi","#भाषा":"language","#language":"language","बाएँ_जोड़ें":"padleft","बायें_जोड़ें":"padleft","padleft":"padleft","दाएँ_जोड़ें":"padright","दायें_जोड़ें":"padright","padright":"padright","ऐंकर_कोड":"anchorencode","anchorencode":"anchorencode","फ़ाइल_पथ":"filepath","filepath":"filepath","पृष्ठ_आइ_डी":"pageid","pageid":"pageid","विश्व":"int","int":"int","#विशेष":"special","#special":"special","#विशेष_कोड":"speciale","#speciale": +"speciale","#टैग":"tag","#tag":"tag","#तिथि_रूप":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#श्रेणी_वृक्ष":"categorytree","#categorytree":"categorytree","#लक्ष्य":"target","#target":"target","#बेबल":"babel","#babel":"babel","#coordinates":"coordinates","#बुलाएँ":"invoke","#बुलायें":"invoke","#invoke":"invoke","#related":"related","#यदि":"if","#if":"if","#यदिसम":"ifeq","#यदि_समान":"ifeq","#यदि_बराबर":"ifeq","#ifeq":"ifeq","#बदलें":"switch","#switch":"switch","#यदि_मौजूद":"ifexist","#ifexist":"ifexist","#यदि_सूत्र":"ifexpr","#ifexpr":"ifexpr","#यदि_त्रुटि":"iferror","#iferror":"iferror","#समय":"time","#time":"time","#समय_स्थानीय":"timel","#timel":"timel","#सूत्र":"expr","#expr":"expr","#सम्बन्धित_से_पूर्ण" +:"rel2abs","#संबंधित_से_पूर्ण":"rel2abs","#rel2abs":"rel2abs","#शीर्षक_भाग":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","कोई_अंतरविकी_कड़ियाँ_नहीं":"noexternallanglinks","कोई_अंतरविकि_कड़ियाँ_नहीं":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#गुण":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","लेख_पथ":"articlepath","articlepath":"articlepath","सर्वर":"server","server":"server","सर्वर_नाम":"servername","servername":"servername","स्क्रिप्ट_पथ":"scriptpath","scriptpath":"scriptpath","स्टाइल_पथ":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"पृष्ठ_संख्या":"numberofpages","NUMBEROFPAGES":"numberofpages","सदस्य_संख्या":"numberofusers","NUMBEROFUSERS":"numberofusers","सक्रिय_सदस्य_संख्या":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","लेख_संख्या":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","फ़ाइल_संख्या":"numberoffiles","फाइल_संख्या":"numberoffiles","NUMBEROFFILES":"numberoffiles","प्रबन्धक_संख्या":"numberofadmins","प्रबंधक_संख्या":"numberofadmins","NUMBEROFADMINS":"numberofadmins","समूह_संख्या":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","सम्पादन_संख्या":"numberofedits","NUMBEROFEDITS":"numberofedits","मूल_सॉर्ट":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT": +"defaultsort","श्रेणी_में_पृष्ठ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","पृष्ठ_आकार":"pagesize","PAGESIZE":"pagesize","सुरक्षा_स्तर":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","नामस्थान_कोड":"namespacee","NAMESPACEE":"namespacee","नामस्थान_संख्या":"namespacenumber","NAMESPACENUMBER":"namespacenumber","चर्चा_स्थान":"talkspace","TALKSPACE":"talkspace","चर्चा_स्थान_कोड":"talkspacee","TALKSPACEE":"talkspacee","सामग्री_स्थान":"subjectspace","लेख_स्थान":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","सामग्री_स्थान_कोड":"subjectspacee","लेख_स्थान_कोड":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE": +"subjectspacee","पृष्ठ_नाम":"pagename","PAGENAME":"pagename","पृष्ठ_नाम_कोड":"pagenamee","PAGENAMEE":"pagenamee","पूर्ण_पृष्ठ_नाम":"fullpagename","FULLPAGENAME":"fullpagename","पूर्ण_पृष्ठ_नाम_कोड":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","मूल_पृष्ठ_नाम":"rootpagename","ROOTPAGENAME":"rootpagename","मूल_पृष्ठ_नाम_कोड":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","तल_पृष्ठ_नाम":"basepagename","BASEPAGENAME":"basepagename","तल_पृष्ठ_नाम_कोड":"basepagenamee","BASEPAGENAMEE":"basepagenamee","उपपृष्ठ_नाम":"subpagename","SUBPAGENAME":"subpagename","उपपृष्ठ_नाम_कोड":"subpagenamee","SUBPAGENAMEE":"subpagenamee","चर्चा_पृष्ठ_नाम":"talkpagename","TALKPAGENAME":"talkpagename", +"चर्चा_पृष्ठ_नाम_कोड":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","सामग्री_पृष्ठ_नाम":"subjectpagename","लेख_पृष्ठ_नाम":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","सामग्री_पृष्ठ_नाम_कोड":"subjectpagenamee","लेख_पृष्ठ_नाम_कोड":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","अवतरण_संख्या":"revisionid","REVISIONID":"revisionid","अवतरण_दिन":"revisionday","REVISIONDAY":"revisionday","अवतरण_दिन2":"revisionday2","अवतरण_दिन२":"revisionday2","REVISIONDAY2":"revisionday2","अवतरण_माह":"revisionmonth","REVISIONMONTH":"revisionmonth","अवतरण_माह1":"revisionmonth1","अवतरण_माह१":"revisionmonth1","REVISIONMONTH1":"revisionmonth1", +"अवतरण_वर्ष":"revisionyear","REVISIONYEAR":"revisionyear","अवतरण_समय":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","अवतरण_सदस्य":"revisionuser","REVISIONUSER":"revisionuser","सीढ़ी_सुरक्षा_स्रोत":"cascadingsources","CASCADINGSOURCES":"cascadingsources","नामस्थान":"namespace","NAMESPACE":"namespace","दृश्य_शीर्षक":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","वर्तमान_माह":"currentmonth","वर्तमान_माह2":"currentmonth","वर्तमान_माह२":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","वर्तमान_माह1":"currentmonth1","वर्तमान_माह१":"currentmonth1","CURRENTMONTH1":"currentmonth1","वर्तमान_माह_नाम":"currentmonthname","CURRENTMONTHNAME":"currentmonthname", +"वर्तमान_माह_सम्बन्ध":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","वर्तमान_माह_संक्षेप":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","वर्तमान_दिन":"currentday","CURRENTDAY":"currentday","वर्तमान_दिन2":"currentday2","वर्तमान_दिन२":"currentday2","CURRENTDAY2":"currentday2","वर्तमान_दिन_नाम":"currentdayname","CURRENTDAYNAME":"currentdayname","वर्तमान_वर्ष":"currentyear","CURRENTYEAR":"currentyear","वर्तमान_समय":"currenttime","CURRENTTIME":"currenttime","वर्तमान_घंटा":"currenthour","CURRENTHOUR":"currenthour","स्थानीय_माह":"localmonth","स्थानीय_माह2":"localmonth","स्थानीय_माह२":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth", +"स्थानीय_माह1":"localmonth1","स्थानीय_माह१":"localmonth1","LOCALMONTH1":"localmonth1","स्थानीय_माह_नाम":"localmonthname","LOCALMONTHNAME":"localmonthname","स्थानीय_माह_सम्बन्ध":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","स्थानीय_माह_संक्षेप":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","स्थानीय_दिन":"localday","LOCALDAY":"localday","स्थानीय_दिन2":"localday2","स्थानीय_दिन२":"localday2","LOCALDAY2":"localday2","स्थानीय_दिन_नाम":"localdayname","LOCALDAYNAME":"localdayname","स्थानीय_वर्ष":"localyear","LOCALYEAR":"localyear","स्थानीय_समय":"localtime","LOCALTIME":"localtime","स्थानीय_घंटा":"localhour","LOCALHOUR":"localhour","साइट_नाम":"sitename","SITENAME": +"sitename","वर्तमान_सप्ताह":"currentweek","CURRENTWEEK":"currentweek","वर्तमान_सप्ताह_का_दिन":"currentdow","CURRENTDOW":"currentdow","स्थानीय_सप्ताह":"localweek","LOCALWEEK":"localweek","स्थानीय_सप्ताह_का_दिन":"localdow","LOCALDOW":"localdow","अवतरण_आकार":"revisionsize","REVISIONSIZE":"revisionsize","वर्तमान_अवतरण":"currentversion","CURRENTVERSION":"currentversion","वर्तमान_समय_मुहर":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","स्थानीय_समय_मुहर":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","दिशा_चिन्ह":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","सामग्री_भाषा":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}], +"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z\\x{0900}-\\x{0963}\\x{0966}-\\x{A8E0}-\\x{A8FF}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-map-bms.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-map-bms.json new file mode 100644 index 0000000..04dbf3d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-map-bms.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa": +"language","#bhs":"language","#language":"language","isikiri":"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1":"timel","#timel":"timel","#hitung":"expr", +"#expr":"expr","#rel2abs":"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","HALAMANDIKATEGORI":"pagesincategory","HALDIKAT": +"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee", +"NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE":"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid","HARIREVISI":"revisionday","HAREV":"revisionday" +,"REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace","RUANGNAMAE":"namespacee","RUNAME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace","RUTAMA":"subjectspace","RUTIKEL":"subjectspace", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","JUMLAHARTIKEL":"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits","NUMBEROFEDITS":"numberofedits","JUDULTAMPILAN":"displaytitle" +,"JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME":"currentdayname","TAHUNKINI":"currentyear","TAKIN":"currentyear", +"CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN":"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname","LOCALDAYNAME":"localdayname","TAHUNLOKAL":"localyear","TALOK":"localyear", +"LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK":"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","BAHASAISI":"contentlanguage", +"BHSISI":"contentlanguage","BASI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mdf.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mdf.json new file mode 100644 index 0000000..0f7ad2e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mdf.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","васенце_тештькесь_вишкине":"lcfirst","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","васенце_тештькесь_покш":"ucfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","вишка_тештькесэ":"lc","маленькими_буквами":"lc","lc":"lc","пошк_тештькесэ":"uc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle": +"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","сыме":"gender","пол":"gender","gender":"gender","ламоньчисла":"plural","множественное_число":"plural","plural":"plural","bidi":"bidi","#келесь":"language","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","файланьки":"filepath","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#башка_тевень":"special","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate", +"#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath" +:"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ЛОПАКУВАЛМО":"pagesize","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ЛОПАЛЕМ": +"pagename","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ЛОПАЛЕМКУВАКАСТО":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","КОРТАМОЛОПАЛЕМ":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename", +"НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ЛИЯКСТОМТОМАID":"revisionid","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ЛИЯКСТОМТОМАЧЫ":"revisionday","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ЛИЯКСТОМТОМАЧЫ2":"revisionday2","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","ЛИЯКСТОМТОМАКОВ":"revisionmonth","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ЛИЯКСТОМТОМАИЕ":"revisionyear","ГОД_ВЕРСИИ": +"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ЛЕМПОТМО":"namespace","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","КОРТАМОПОТМО":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee", +"ЗЯРОСЁРМАДОВКСТ":"numberofarticles","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ЗЯРОФАЙЛАТ":"numberoffiles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","ЗЯРОТЕИЦЯТ":"numberofusers","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ЗЯРОЛОПАТ":"numberofpages","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","ЗЯРОАДМИНТНЭДЕ":"numberofadmins","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ЗЯРОВИТНЕМАТПЕТНЕМАТ":"numberofedits","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","НЕВТЕМС_КОНЯКС":"displaytitle", +"ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","МОЛИЦЯКОВ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","МОЛИЦЯКОВЛЕМ":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","МОЛИЦЯКОВЛЕМГЕН":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","МОЛИЦЯКОВКИРЬТЯНЬХВОРМА":"currentmonthabbrev","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","МОЛИЦЯЧЫ":"currentday","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","МОЛИЦЯЧЫ2": +"currentday2","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","МОЛИЦЯЧЫЛЕМ":"currentdayname","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","МОЛИЦЯИЕ":"currentyear","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","МОЛИЦЯШКА":"currenttime","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","МОЛИЦЯЦЯС":"currenthour","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","ТЕСКЭНЬКОВ":"localmonth","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","ТЕСКЭНЬКОВЛЕМ":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","ТЕСКЭНЬКОВЛЕМГЕН":"localmonthnamegen", +"НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ТЕСКЭНЬКОВКИРЬТЯНЬХВОРМА":"localmonthabbrev","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ТЕСКЭНЬЧЫ":"localday","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","ТЕСКЭНЬЧЫ2":"localday2","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","ТЕСКЭНЬЧЫЛЕМ":"localdayname","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","ТЕСКЭНЬИЕ":"localyear","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","ТЕСКЭНЬШКА":"localtime","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","ТЕСКЭНЬЦЯС":"localhour","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","САЙТЛЕМ":"sitename","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename", +"МОЛИЦЯ_ТАРГО":"currentweek","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","МОЛИЦЯ_ЧИ":"currentdow","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","ТЕСКЭНЬТАРГО":"localweek","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕВАТЕВЕРСИЯ":"currentversion","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage", +"PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mg.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mg.json new file mode 100644 index 0000000..37d869a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mg.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__tsyasianalahatra__":"notoc","__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__tsyasianagallery__":"nogallery","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__tereonylahatra__":"forcetoc","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc", +"__lahatra__":"toc","__lahat__":"toc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__tsyazoovaina__":"noeditsection","__sectionnoneditable__":"noeditsection","__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation", +"__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode","initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlrehetra":"fullurl","urlcomplete":"fullurl","fullurl":"fullurl","urlrehetrax":"fullurle","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft" +,"bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int","#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#raha":"if","#if":"if","#si":"if","#rahamitovy":"ifeq","#ifeq":"ifeq","#si=":"ifeq","#selon":"switch","#switch":"switch","#rahamisy":"ifexist","#ifexist":"ifexist","#siexiste":"ifexist","#rahamarina":"ifexpr","#ifexpr":"ifexpr","#siexpr":"ifexpr","#rahadiso":"iferror","#iferror":"iferror","#sierreur":"iferror","#lera":"time","#time":"time","#heure":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre": +"titleparts","#titleparts":"titleparts","#karazantsokajy":"categorytree","#categorytree":"categorytree","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT": +"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ANARAMPEJY":"pagename","ANARANAPEJY":"pagename","NOMPAGE":"pagename","PAGENAME":"pagename","ANARAMPEJYX":"pagenamee","ANARANAPEJYX":"pagenamee","NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","ANARAMPEJYFENO":"fullpagename","ANARANAPEJYFENO":"fullpagename","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","ANARAMPEJYFENOX":"fullpagenamee","ANARANAPEJYFENOX":"fullpagenamee","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ANARANAZANAPEJY":"subpagename","ANARANJANAPEJY":"subpagename","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","ANARANJANAPEJYX":"subpagenamee","ANARANAZANAPEJYX":"subpagenamee","NOMSOUSPAGEX": +"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","ANARANAFOTOPEJY":"basepagename","ANARAMPOTOPEJY":"basepagename","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","ANARANAFOTOPEJYE":"basepagenamee","ANARAMPOTOPEJYE":"basepagenamee","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ANARAMPEJINDRESAKA":"talkpagename","ANARANAPEJINDRESAKA":"talkpagename","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid","REVISIONID":"revisionid","JOURVERSION": +"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ANARANTSEHATRA":"namespace","ANARANASEHATRA":"namespace","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ANARANTSEHATRAX":"namespacee","ANARANASEHATRAX":"namespacee","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","PEJINDRESAKA":"talkspace","PEJYRESAKA":"talkspace","DINIKA":"talkspace","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","PEJINDRESAKAX":"talkspacee","PEJYRESAKAX":"talkspacee","DINIKAX": +"talkspacee","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","TOERANALAHATSORATRA":"subjectspace","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","TOERANNYLAHATSORATRA":"subjectspacee","ESPACESUJETX":"subjectspacee","ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ISALAHATSORATRA":"numberofarticles","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ISARAKITRA":"numberoffiles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","ISAMPIKAMBANA":"numberofusers","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","ISAMPIKAMBANAMANOVA":"numberofactiveusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ISAPEJY":"numberofpages","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins", +"ISAFANOVANA":"numberofedits","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","ASEHOLOHATENY":"displaytitle","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","VOLANA":"currentmonth","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","VOLANA1":"currentmonth1","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","ANARAMBOLANA":"currentmonthname","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthnamegen","ANARAMBOLANAGEN":"currentmonthnamegen","ANARANAVOLANA":"currentmonthnamegen","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ANARAMBOLANAFOHY":"currentmonthabbrev","ANARANAVOLANAFOHY":"currentmonthabbrev","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ANDRO":"currentday","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","ANDRO2":"currentday2", +"JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","ANARANANDRO":"currentdayname","ANARANAANDRO":"currentdayname","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","TAONA":"currentyear","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","LERA":"currenttime","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","ORA":"currenthour","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","VOLANAANTOERANA":"localmonth","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","VOLANAANTOERANA1":"localmonth1","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","ANARAMBOLANAANTOERANA":"localmonthname","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","ANARAMBOLANAANTOERANAGEN":"localmonthnamegen","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ANARAMBOLANAANTOERANAFOHY":"localmonthabbrev","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev", +"ANDROANTOERANA":"localday","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","ANDROANTOERANA2":"localday2","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","ANARANANDROANTOERANA":"localdayname","NOMJOURLOCAL":"localdayname","LOCALDAYNAME":"localdayname","TAONAANTOERANA":"localyear","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","LERAANTOERANA":"localtime","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","ORAANTOERANA":"localhour","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","HERINANDRO":"currentweek","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","ALAHADY":"currentdow","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","HERINANDROANTOERANA":"localweek","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","ALAHADYANTOERANA":"localdow","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL": +"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mh.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mh.json new file mode 100644 index 0000000..a906994 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mh.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"charinsert":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mhr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mhr.json new file mode 100644 index 0000000..add2b51 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mhr.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender", +"множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist": +"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ": +"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY": +"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour", +"МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","САЙТЛӰМ":"sitename","НАЗВАНИЕ_САЙТА": +"sitename","SITENAME":"sitename","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mi.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mi.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mi.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-min.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-min.json new file mode 100644 index 0000000..f2ba3b0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-min.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa":"language","#bhs":"babel","#language":"language","isikiri": +"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1":"timel","#timel":"timel","#hitung":"expr","#expr":"expr","#rel2abs": +"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","tanpainterwiki":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#properti":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHARTIKEL": +"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits","NUMBEROFEDITS":"numberofedits","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","HALAMANDIKATEGORI":"pagesincategory","HALDIKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel", +"PROTECTIONEXPIRY":"protectionexpiry","RUANGNAMAE":"namespacee","RUNAME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace","RUTAMA":"subjectspace","RUTIKEL":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE": +"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME": +"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE":"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid","HARIREVISI":"revisionday","HAREV":"revisionday","REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace", +"JUDULTAMPILAN":"displaytitle","JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME":"currentdayname","TAHUNKINI":"currentyear", +"TAKIN":"currentyear","CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN":"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname","LOCALDAYNAME":"localdayname","TAHUNLOKAL": +"localyear","TALOK":"localyear","LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK":"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark", +"BAHASAISI":"contentlanguage","BHSISI":"contentlanguage","BASI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mk.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mk.json new file mode 100644 index 0000000..6c01a86 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mk.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__безсодржина__":"notoc","__notoc__":"notoc","__безгалерија__":"nogallery","__nogallery__":"nogallery","__сосодржина__":"forcetoc","__forcetoc__":"forcetoc","__содржина__":"toc","__toc__":"toc","__без_уредување_на_поднаслови__":"noeditsection", +"__noeditsection__":"noeditsection","__безпретворањенаслов__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__безпретворањесодржина__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ВРСКАНОВПОДНАСЛОВ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗВРСКАНОВПОДНАСЛОВ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРИЕНАКАТ__":"hiddencat","__СКРИЕНАКАТЕГОРИЈА__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗИНДЕКС__":"noindex","__NOINDEX__":"noindex","__СТАТИЧНОПРЕНАСОЧУВАЊЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__": +"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","шифрирајurl":"urlencode","urlencode":"urlencode","првомб":"lcfirst","lcfirst":"lcfirst","првогб":"ucfirst","ucfirst":"ucfirst","мб":"lc","lc":"lc","гб":"uc","uc":"uc","локалнаадреса":"localurl","localurl":"localurl","локалнаадресаи":"localurle","localurle":"localurle","полнаurl":"fullurl","fullurl":"fullurl","полнаurle":"fullurle","fullurle":"fullurle","канонскаurl":"canonicalurl","canonicalurl":"canonicalurl","канонскаurle":"canonicalurle","canonicalurle":"canonicalurle","форматброј":"formatnum","formatnum":"formatnum","граматика":"grammar","grammar":"grammar","пол":"gender","gender":"gender","множина":"plural","plural":"plural","bidi":"bidi","#јазик":"language","#language":"language","поставилево":"padleft","padleft":"padleft","поставидесно":"padright","padright":"padright", +"шифрирајкотва":"anchorencode","anchorencode":"anchorencode","податотечнапатека":"filepath","filepath":"filepath","назнаканастраница":"pageid","pageid":"pageid","int":"int","#службена":"special","#службени":"special","#special":"special","#speciale":"speciale","#ознака":"tag","#tag":"tag","#форматдатум":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#цел":"target","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#повикај":"invoke","#invoke":"invoke","#related":"related","#ако":"if","#if":"if","#акоисто":"ifeq","#ifeq":"ifeq","#префрли":"switch","#switch":"switch","#акопостои":"ifexist","#ifexist":"ifexist","#акоизраз":"ifexpr","#ifexpr":"ifexpr","#акогрешка":"iferror","#iferror":"iferror","#време":"time","#time":"time","#времел":"timel","#timel":"timel","#израз":"expr","#expr" +:"expr","#релдоапс":"rel2abs","#rel2abs":"rel2abs","#насловделови":"titleparts","#titleparts":"titleparts","#стеблонакатегории":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","безнадворјазврски":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#својство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","патеканастатија":"articlepath","articlepath":"articlepath","опслужувач":"server","server":"server","именаопслужувач":"servername","servername":"servername","патеканаскрипта":"scriptpath","scriptpath":"scriptpath","стилскапатека":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","вбскладиштеиме":"wbreponame","wbreponame":"wbreponame"},{ +"БРОЈВОГРУПА":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ОСНОВНОПОДРЕДУВАЊЕ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦИВОКАТЕГОРИЈА":"pagesincategory","СТРАНИЦИВОКАТ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ГОЛЕМИНА_НА_СТРАНИЦА":"pagesize","PAGESIZE":"pagesize","НИВОНАЗАШТИТА":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","СТРАНИЦА":"pagename","PAGENAME":"pagename","СТРАНИЦАИ":"pagenamee","PAGENAMEE":"pagenamee","ЦЕЛОСНОИМЕНАСТРАНИЦА":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ПОТСТРАНИЦА":"subpagename","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE": +"rootpagenamee","ОСНОВНАСТРАНИЦА":"basepagename","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","СТРАНИЦАЗАРАЗГОВОР":"talkpagename","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","ИМЕНАСТАТИЈА":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","НАЗНАКАНАПРЕРАБОТКА":"revisionid","REVISIONID":"revisionid","ДЕННАПРЕРАБОТКА":"revisionday","REVISIONDAY":"revisionday","ДЕННАПРЕРАБОТКА2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЕЦНАПРЕРАБОТКА":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЕЦНАПРЕРАБОТКА1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОДИНАНАПРЕРАБОТКА":"revisionyear","REVISIONYEAR":"revisionyear","ВРЕМЕНАПРЕРАБОТКА":"revisiontimestamp", +"REVISIONTIMESTAMP":"revisiontimestamp","КОРИСНИКНАНАПРЕРАБОТКА":"revisionuser","REVISIONUSER":"revisionuser","КАСКАДНИИЗВОРИ":"cascadingsources","CASCADINGSOURCES":"cascadingsources","ИМЕПРОСТОР":"namespace","ИМЕНСКИПРОСТОР":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","РАЗГОВОРПРОСТОР":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","БРОЈСТАТИИ":"numberofarticles","БРОЈНАСТАТИИ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","БРОЈНАПОДАТОТЕКИ":"numberoffiles","NUMBEROFFILES":"numberoffiles","БРОЈНАКОРИСНИЦИ":"numberofusers","NUMBEROFUSERS":"numberofusers","БРОЈНААКТИВНИКОРИСНИЦИ":"numberofactiveusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","БРОЈНАСТРАНИЦИ":"numberofpages","NUMBEROFPAGES":"numberofpages","БРОЈНААДМИНИСТРАТОРИ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","БРОЈНАУРЕДУВАЊА":"numberofedits","NUMBEROFEDITS":"numberofedits","ПРИКАЖИНАСЛОВ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКОВЕНМЕСЕЦ":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКОВЕНМЕСЕЦ1":"currentmonth1","CURRENTMONTH1":"currentmonth1","ТЕКОВЕНМЕСЕЦИМЕ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","ТЕКОВЕНМЕСЕЦИМЕРОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ТЕКОВЕНМЕСЕЦСКР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКОВЕНДЕН":"currentday","CURRENTDAY":"currentday","ТЕКОВЕНДЕН2":"currentday2","CURRENTDAY2":"currentday2","ТЕКОВЕНДЕНИМЕ": +"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКОВНАГОДИНА":"currentyear","CURRENTYEAR":"currentyear","ТЕКОВНОВРЕМЕ":"currenttime","CURRENTTIME":"currenttime","ТЕКОВЕНЧАС":"currenthour","CURRENTHOUR":"currenthour","МЕСЕЦ_ЛОКАЛНО":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСЕЦ_ЛОКАЛНО1":"localmonth1","LOCALMONTH1":"localmonth1","МЕСЕЦИМЕ_ЛОКАЛНО":"localmonthname","LOCALMONTHNAME":"localmonthname","МЕСЕЦИМЕ_ЛОКАЛНО_ГЕНИТИВ":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","МЕСЕЦИМЕ_ЛОКАЛНО_КРАТЕНКА":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ДЕН_ЛОКАЛНО":"localday","LOCALDAY":"localday","ДЕН2_ЛОКАЛНО":"localday2","LOCALDAY2":"localday2","ИМЕНАДЕН_ЛОКАЛНО":"localdayname","LOCALDAYNAME":"localdayname","ГОДИНА_ЛОКАЛНО":"localyear","LOCALYEAR":"localyear", +"ВРЕМЕ_ЛОКАЛНО":"localtime","LOCALTIME":"localtime","ЧАС_ЛОКАЛНО":"localhour","LOCALHOUR":"localhour","ИМЕНАМРЕЖНОМЕСТО":"sitename","SITENAME":"sitename","ТЕКОВНАСЕДМИЦА":"currentweek","CURRENTWEEK":"currentweek","ТЕКОВЕНДЕНВОСЕДМИЦАТА":"currentdow","CURRENTDOW":"currentdow","СЕДМИЦА_ЛОКАЛНО":"localweek","LOCALWEEK":"localweek","ЛОКАЛЕНДЕНВОСЕДМИЦАТА":"localdow","LOCALDOW":"localdow","ГОЛЕМИНАНАПРЕРАБОТКА":"revisionsize","REVISIONSIZE":"revisionsize","ТЕКОВНАВЕРЗИЈА":"currentversion","CURRENTVERSION":"currentversion","ОЗНАЧЕНОТЕКОВНОВРЕМЕ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОЗНАЧЕНОЛОКАЛНОВРЕМЕ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","ОЗНАКАЗАНАСОКА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЈАЗИКНАСОДРЖИНАТА" +:"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдѓежзѕијклљмнњопрстќуфхцчџш]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ml.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ml.json new file mode 100644 index 0000000..b4a60f8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ml.json @@ -0,0 +1,16 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__ഉള്ളടക്കംവേണ്ട__":"notoc","__notoc__":"notoc","__ചിത്രസഞ്ചയംവേണ്ട__":"nogallery","__nogallery__":"nogallery","__ഉള്ളടക്കംഇടുക__":"forcetoc","__forcetoc__":"forcetoc","__ഉള്ളടക്കം__":"toc","__toc__":"toc", +"__സംശോധിക്കേണ്ട__":"noeditsection","__noeditsection__":"noeditsection","__തലക്കെട്ട്മാറ്റേണ്ട__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__ഉള്ളടക്കംമാറ്റേണ്ട__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__പുതിയവിഭാഗംകണ്ണി__":"newsectionlink","__പുതിയഖണ്ഡിക്കണ്ണി__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__പുതിയവിഭാഗംകണ്ണിവേണ്ട__":"nonewsectionlink","__പുതിയഖണ്ഡിക്കണ്ണിവേണ്ട__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","‌‌__മറഞ്ഞിരിക്കുംവർഗ്ഗം__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","‌‌__സൂചിക__": +"index","__INDEX__":"index","__സൂചികവേണ്ട__":"noindex","__NOINDEX__":"noindex","_സ്ഥിരസ്ഥിതതിരിച്ചുവിടൽ_":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"നാമേ":"ns","ns":"ns","നാമേസ":"nse","nse":"nse","വിലാസഗൂഢീകരണം":"urlencode","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","ലോക്കൽയുആർഎൽ":"localurl","localurl":"localurl","ലോക്കൽയുആർഎൽഇ":"localurle","localurle":"localurle","പൂർണ്ണവിലാസം":"fullurl","fullurl":"fullurl","പൂർണ്ണവിലാസംസമഗ്രം":"fullurle","fullurle":"fullurle","കാനോനിക്കൽവിലാസം":"canonicalurl","canonicalurl":"canonicalurl", +"കാനോനിക്കൽവിലാസംസമഗ്രം":"canonicalurle","canonicalurle":"canonicalurle","ദശാംശഘടന":"formatnum","സംഖ്യാഘടന":"formatnum","formatnum":"formatnum","വ്യാകരണം":"grammar","grammar":"grammar","ലിംഗം":"gender","gender":"gender","ബഹുവചനം":"plural","plural":"plural","bidi":"bidi","#ഭാഷ":"language","#language":"language","ഇടത്ത്നിറക്കുക":"padleft","padleft":"padleft","വലത്ത്നിറക്കുക":"padright","padright":"padright","anchorencode":"anchorencode","പ്രമാണപഥം":"filepath","filepath":"filepath","താൾഐ‌ഡി":"pageid","pageid":"pageid","സമ്പർക്കം":"int","int":"int","#പ്രത്യേകം":"special","#special":"special","#സവിശേഷം":"speciale","#speciale":"speciale","#റ്റാഗ്":"tag","#ടാഗ്":"tag","#tag":"tag", +"#ദിനരേഖീകരണരീതി":"formatdate","#ദിവസരേഖീകരണരീതി":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ലക്ഷ്യം":"target","#target":"target","#ബാബേൽ":"babel","#babel":"babel","#coordinates":"coordinates","#ഉണർത്തുക":"invoke","#invoke":"invoke","#related":"related","#എങ്കിൽ":"if","#if":"if","#സമമെങ്കിൽ":"ifeq","#ifeq":"ifeq","#മാറ്റുക":"switch","#switch":"switch","#ഉണ്ടെങ്കിൽ":"ifexist","#ifexist":"ifexist","#എക്സ്പ്രെഷനെങ്കിൽ":"ifexpr","#ifexpr":"ifexpr","#പിഴവെങ്കിൽ":"iferror","#iferror":"iferror","#സമയം":"time","#time":"time","#സമയം|":"timel","#timel":"timel","#ദ്യോതകം":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#വർഗ്ഗവൃക്ഷം":"categorytree","#categorytree":"categorytree","#lst": +"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","ബാഹ്യഭാഷാകണ്ണികൾവേണ്ട":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","ലേഖനപഥം":"articlepath","articlepath":"articlepath","സെർവർ":"server","server":"server","സെർവറിന്റെപേര്":"servername","servername":"servername","സ്ക്രിപ്റ്റ്പഥം":"scriptpath","scriptpath":"scriptpath","സ്റ്റൈൽപഥം":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ഗണത്തിലെയെണ്ണം":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","സ്വതേയുള്ളക്രമപ്പെടുത്തൽ":"defaultsort", +"സ്വതേയുള്ളക്രമപ്പെടുത്തൽചാവി":"defaultsort","സ്വതേയുള്ളവർഗ്ഗക്രമപ്പെടുത്തൽ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","വർഗ്ഗത്തിലുള്ളതാളുകൾ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","താൾവലിപ്പം":"pagesize","PAGESIZE":"pagesize","സംരക്ഷണതലം":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","താളിന്റെപേര്‌":"pagename","PAGENAME":"pagename","താളിന്റെപേര്‌സമഗ്രം":"pagenamee","PAGENAMEE":"pagenamee","താളിന്റെമുഴുവൻപേര്‌":"fullpagename","FULLPAGENAME":"fullpagename","താളിന്റെമുഴുവൻപേര്സമഗ്രം": +"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","അനുബന്ധതാളിന്റെപേര്‌":"subpagename","SUBPAGENAME":"subpagename","അനുബന്ധതാളിന്റെപേര്സമഗ്രം":"subpagenamee","SUBPAGENAMEE":"subpagenamee","മൂലതാളിന്റെപേര്":"rootpagename","ROOTPAGENAME":"rootpagename","മൂലതാളിന്റെപേര്‌സമഗ്രം":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","അടിസ്ഥാനതാളിന്റെപേര്‌":"basepagename","BASEPAGENAME":"basepagename","അടിസ്ഥാനതാളിന്റെപേര്‌സമഗ്രം":"basepagenamee","BASEPAGENAMEE":"basepagenamee","സംവാദതാളിന്റെപേര്‌":"talkpagename","TALKPAGENAME":"talkpagename","സംവാദതാളിന്റെപേര്‌സമഗ്രം":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"ലേഖനതാളിന്റെപേര്‌":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ലേഖനതാളിന്റെപേര്‌സമഗ്രം":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","തിരുത്തൽഅടയാളം":"revisionid","REVISIONID":"revisionid","തിരുത്തിയദിവസം":"revisionday","തിരുത്തിയദിനം":"revisionday","REVISIONDAY":"revisionday","തിരുത്തിയദിവസം2":"revisionday2","തിരുത്തിയദിനം2":"revisionday2","REVISIONDAY2":"revisionday2","തിരുത്തിയമാസം":"revisionmonth","REVISIONMONTH":"revisionmonth","തിരുത്തിയമാസം1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","തിരുത്തിയവർഷം":"revisionyear","REVISIONYEAR":"revisionyear", +"തിരുത്തിയസമയമുദ്ര":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","അവസാനംതിരുത്തിയയാൾ":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","നാമമേഖല":"namespace","NAMESPACE":"namespace","നാമമേഖലസമഗ്രം":"namespacee","NAMESPACEE":"namespacee","നാമമേഖലാസംഖ്യ":"namespacenumber","NAMESPACENUMBER":"namespacenumber","സംവാദമേഖല":"talkspace","TALKSPACE":"talkspace","സംവാദമേഖലസമഗ്രം":"talkspacee","TALKSPACEE":"talkspacee","വിഷയമേഖല":"subjectspace","ലേഖനമേഖല":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","വിഷയമേഖലസമഗ്രം":"subjectspacee","ലേഖനമേഖലസമഗ്രം":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee", +"ലേഖനങ്ങളുടെയെണ്ണം":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","പ്രമാണങ്ങളുടെയെണ്ണം":"numberoffiles","NUMBEROFFILES":"numberoffiles","ഉപയോക്താക്കളുടെയെണ്ണം":"numberofusers","അംഗങ്ങളുടെയെണ്ണം":"numberofusers","NUMBEROFUSERS":"numberofusers","സജീവോപയാക്താക്കളുടെയെണ്ണം":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","താളുകളുടെയെണ്ണം":"numberofpages","NUMBEROFPAGES":"numberofpages","കാര്യനിർവ്വാഹകരുടെഎണ്ണം":"numberofadmins","NUMBEROFADMINS":"numberofadmins","തിരുത്തലുകളുടെണ്ണം":"numberofedits","NUMBEROFEDITS":"numberofedits","ശീർഷകംപ്രദർശിപ്പിക്കുക":"displaytitle", +"തലക്കെട്ട്പ്രദർശിപ്പിക്കുക":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ഈമാസം":"currentmonth","ഈമാസം2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ഈമാസം1":"currentmonth1","CURRENTMONTH1":"currentmonth1","ഈമാസത്തിന്റെപേര്‌":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","ഈമാസത്തിന്റെപേരുസൃഷ്ടിക്കുക":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ഈമാസത്തിന്റെപേര്‌സംഗ്രഹം":"currentmonthabbrev","ഈമാസത്തിന്റെപേര്‌ചുരുക്കം":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ഈദിവസം":"currentday","CURRENTDAY":"currentday","ഈദിവസം2":"currentday2","CURRENTDAY2":"currentday2", +"ഈദിവസത്തിന്റെപേര്‌":"currentdayname","CURRENTDAYNAME":"currentdayname","ഈവർഷം":"currentyear","CURRENTYEAR":"currentyear","ഈസമയം":"currenttime","CURRENTTIME":"currenttime","ഈമണിക്കൂർ":"currenthour","CURRENTHOUR":"currenthour","പ്രാദേശികമാസം":"localmonth","പ്രാദേശികമാസം2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","പ്രാദേശികമാസം1":"localmonth1","LOCALMONTH1":"localmonth1","പ്രാദേശികമാസത്തിന്റെപേര്‌":"localmonthname","LOCALMONTHNAME":"localmonthname","പ്രാദേശികമാസത്തിന്റെപേരുസൃഷ്ടിക്കുക":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","പ്രാദേശികമാസത്തിന്റെപേര്‌സംഗ്രഹം":"localmonthabbrev", +"പ്രാദേശികമാസത്തിന്റെപേര്‌ചുരുക്കം":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","പ്രാദേശികദിവസം":"localday","LOCALDAY":"localday","പ്രാദേശികദിവസം2":"localday2","LOCALDAY2":"localday2","പ്രാദേശികദിവസത്തിന്റെപേര്‌":"localdayname","LOCALDAYNAME":"localdayname","പ്രാദേശികവർഷം":"localyear","LOCALYEAR":"localyear","പ്രാദേശികസമയം":"localtime","LOCALTIME":"localtime","പ്രാദേശികമണിക്കൂർ":"localhour","LOCALHOUR":"localhour","സൈറ്റിന്റെപേര്":"sitename","SITENAME":"sitename","ആഴ്ച":"currentweek","ആഴ്‌ച":"currentweek","CURRENTWEEK":"currentweek","ദിവസത്തിന്റെപേര്‌അക്കത്തിൽ":"currentdow","CURRENTDOW":"currentdow", +"പ്രാദേശികആഴ്ച":"localweek","പ്രാദേശികആഴ്‌ച":"localweek","LOCALWEEK":"localweek","ആഴ്ചയുടെപേര്‌അക്കത്തിൽ":"localdow","ആഴ്‌ചയുടെപേര്‌അക്കത്തിൽ":"localdow","LOCALDOW":"localdow","നാൾപ്പതിപ്പിന്റെവലിപ്പം":"revisionsize","REVISIONSIZE":"revisionsize","ഈപതിപ്പ്":"currentversion","CURRENTVERSION":"currentversion","സമയമുദ്ര":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","പ്രാദേശികസമയമുദ്ര":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","ദിശാസൂചിക":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ഉള്ളടക്കഭാഷ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z\\x{0D02}-\\x{0D7F}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mn.json new file mode 100644 index 0000000..35018d1 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mn.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя“»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mni.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mni.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mni.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mnw.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mnw.json new file mode 100644 index 0000000..ecb1f4f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mnw.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates", +"#စၞောန်ထ္ၜး":"invoke","#ဖော်ပြ":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT": +"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE": +"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen", +"LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mr.json new file mode 100644 index 0000000..149cc73 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mr.json @@ -0,0 +1,14 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__अनुक्रमणिकानको__":"notoc","__notoc__":"notoc","__प्रदर्शननको__":"nogallery","__nogallery__":"nogallery","__अनुक्रमणिकाहवीच__":"forcetoc","__forcetoc__":"forcetoc","__अनुक्रमणिका__":"toc","__toc__":"toc", +"__विभागअसंपादनक्षम__":"noeditsection","__noeditsection__":"noeditsection","__विनाशीर्षकबदल__":"notitleconvert","__विनाशीब__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__विनामजकुरबदल__":"nocontentconvert","__विनामब__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__नवविभागदुवा__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__विनानवविभागदुवा__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__वर्गलपवा__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__क्रमीत__":"index","__अनुक्रमीत__":"index","__INDEX__":"index","__विनाक्रमीत__":"noindex","__विनाअनुक्रमीत__":"noindex", +"__NOINDEX__":"noindex","__अविचलपुर्ननिर्देश__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"नावि":"ns","ns":"ns","नाविअरिक्त":"nse","नाव्यारिक्त":"nse","नाव्याख":"nse","nse":"nse","संकेतस्थलीआंग्ल्संकेत":"urlencode","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","स्थानिकस्थळ":"localurl","स्थानिकसंकेतस्थळ":"localurl","localurl":"localurl","स्थानिकस्थली":"localurle","localurle":"localurle","संपूर्णसंस्थळ":"fullurl","fullurl":"fullurl","संपूर्णसंस्थली":"fullurle","संपूर्णसंस्थळी":"fullurle","fullurle": +"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","क्रमपद्धती":"formatnum","formatnum":"formatnum","व्याकरण":"grammar","grammar":"grammar","लिंग":"gender","gender":"gender","बहुवचन":"plural","plural":"plural","bidi":"bidi","#भाषा":"language","#language":"language","डावाभरीव":"padleft","भरीवडावा":"padleft","padleft":"padleft","उजवाभरीव":"padright","भरीवउजवा":"padright","padright":"padright","नांगरआंग्लसंकेत":"anchorencode","anchorencode":"anchorencode","संचिकामार्ग":"filepath","filepath":"filepath","pageid":"pageid","इन्ट":"int","int":"int","#विशेष":"special","#special":"special","#speciale":"speciale","#खूण":"tag","#खूणगाठ":"tag","#tag":"tag","#दिनांकनपद्धती":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate", +"#target":"target","#बॅबेल":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#जर":"if","#इफ":"if","#if":"if","#जरसम":"ifeq","#ifeq":"ifeq","#कळ":"switch","#सांगकळ":"switch","#असेलतरसांग":"switch","#असलेतरसांग":"switch","#स्वीच":"switch","#switch":"switch","#जरअसेल":"ifexist","#जरआहे":"ifexist","#ifexist":"ifexist","#जरकरण":"ifexpr","#ifexpr":"ifexpr","#जरत्रुटी":"iferror","#iferror":"iferror","#वेळ":"time","#time":"time","#वेळस्था":"timel","#timel":"timel","#करण":"expr","#expr":"expr","#rel2abs":"rel2abs","#शीर्षकखंड":"titleparts","#टाइटलपार्ट्स":"titleparts","#titleparts":"titleparts","#वर्गवृक्ष":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth": +"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","विदादाता":"server","server":"server","विदादातानाव":"servername","servername":"servername","संहीतामार्ग":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"गटक्रमांक":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","अविचलवर्ग":"defaultsort","अविचलवर्गकळ":"defaultsort","अविचलवर्गवर्गीकरण":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","वर्गातीलपाने":"pagesincategory","वर्गीतपाने":"pagesincategory", +"श्रेणीतपाने":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","पानक्षमता":"pagesize","PAGESIZE":"pagesize","सुरक्षास्तर":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","लेखनाव":"pagename","PAGENAME":"pagename","लेखानावव":"pagenamee","PAGENAMEE":"pagenamee","पूर्णलेखनाव":"fullpagename","FULLPAGENAME":"fullpagename","पूर्णलेखनावे":"fullpagenamee","अंशदुवा":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","उपपाननाव":"subpagename","SUBPAGENAME":"subpagename","उपपाननावे":"subpagenamee","उपपाननावाचे":"subpagenamee","उपौंशदुवा":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","मूळपाननाव":"basepagename","BASEPAGENAME": +"basepagename","मूळपाननावे":"basepagenamee","BASEPAGENAMEE":"basepagenamee","चर्चापाननाव":"talkpagename","TALKPAGENAME":"talkpagename","चर्चापाननावे":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","विषयपाननाव":"subjectpagename","लेखपाननाव":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","विषयपाननावे":"subjectpagenamee","लेखपाननावे":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","आवृत्तीक्र्मांक":"revisionid","REVISIONID":"revisionid","आवृत्तीदिन":"revisionday","REVISIONDAY":"revisionday","आवृत्तीदिन२":"revisionday2","REVISIONDAY2":"revisionday2","आवृत्तीमास":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1", +"आवृत्तीवर्ष":"revisionyear","REVISIONYEAR":"revisionyear","आवृत्तीमुद्रा":"revisiontimestamp","आवृत्तीठसा":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","आवृत्तीसदस्य":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","नामविश्व":"namespace","NAMESPACE":"namespace","नामविश्वा":"namespacee","नामविश्वाचे":"namespacee","NAMESPACEE":"namespacee","नामविश्वक्रमांक":"namespacenumber","NAMESPACENUMBER":"namespacenumber","चर्चाविश्व":"talkspace","TALKSPACE":"talkspace","चर्चाविश्वा":"talkspacee","चर्चाविश्वाचे":"talkspacee","TALKSPACEE":"talkspacee","विषयविश्व":"subjectspace","लेखविश्व":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace", +"विषयविश्वा":"subjectspacee","लेखविश्वा":"subjectspacee","विषयविश्वाचे":"subjectspacee","लेखविश्वाचे":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","लेखसंख्या":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","संचिकासंख्या":"numberoffiles","NUMBEROFFILES":"numberoffiles","सदस्यसंख्या":"numberofusers","NUMBEROFUSERS":"numberofusers","सक्रीयसदस्यसंख्या":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","पानसंख्या":"numberofpages","NUMBEROFPAGES":"numberofpages","प्रचालकसंख्या":"numberofadmins","NUMBEROFADMINS":"numberofadmins","संपादनसंख्या":"numberofedits","NUMBEROFEDITS":"numberofedits","शीर्षकदाखवा":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=", +"सद्यमहिना":"currentmonth","सद्यमहिना२":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","सद्यमहिना१":"currentmonth1","CURRENTMONTH1":"currentmonth1","सद्यमहिनानाव":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","सद्यमहिनासाधारण":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","सद्यमहिनासंक्षीप्त":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","सद्यदिवस":"currentday","CURRENTDAY":"currentday","सद्यदिवस२":"currentday2","CURRENTDAY2":"currentday2","सद्यदिवसनाव":"currentdayname","CURRENTDAYNAME":"currentdayname","सद्यवर्ष":"currentyear","CURRENTYEAR":"currentyear","सद्यवेळ":"currenttime","CURRENTTIME":"currenttime","सद्यतास":"currenthour","CURRENTHOUR":"currenthour", +"स्थानिकमहिना":"localmonth","स्थानिकमहिना२":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","स्थानिकमहिना१":"localmonth1","LOCALMONTH1":"localmonth1","स्थानिकमहिनानाव":"localmonthname","LOCALMONTHNAME":"localmonthname","स्थानिकमहिनासाधारण":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","स्थानिकमहिनासंक्षीप्त":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","स्थानिकदिवस":"localday","LOCALDAY":"localday","स्थानिकदिवस२":"localday2","LOCALDAY2":"localday2","स्थानिकदिवसनाव":"localdayname","LOCALDAYNAME":"localdayname","स्थानिकवर्ष":"localyear","LOCALYEAR":"localyear","स्थानिकवेळ":"localtime","LOCALTIME":"localtime","स्थानिकतास": +"localhour","LOCALHOUR":"localhour","संकेतस्थळनाव":"sitename","SITENAME":"sitename","सद्यआठवडा":"currentweek","CURRENTWEEK":"currentweek","सद्यउतरण":"currentdow","सद्यउतार":"currentdow","CURRENTDOW":"currentdow","स्थानिकआठवडा":"localweek","LOCALWEEK":"localweek","स्थानिकउतरण":"localdow","स्थानिकउतार":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","सद्यआवृत्ती":"currentversion","CURRENTVERSION":"currentversion","सद्यकालमुद्रा":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","स्थानिककालमुद्रा":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","दिशाचिन्ह":"directionmark","दिशादर्शक":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","मसुदाभाषा":"contentlanguage", +"मजकुरभाषा":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([ऀ-ॣॱ-ॿ‍]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mrj.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mrj.json new file mode 100644 index 0000000..add2b51 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mrj.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender", +"множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist": +"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ": +"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY": +"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour", +"МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","САЙТЛӰМ":"sitename","НАЗВАНИЕ_САЙТА": +"sitename","SITENAME":"sitename","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ms.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ms.json new file mode 100644 index 0000000..6b98149 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ms.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"rn":"ns","ns":"ns","rne":"nse","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","jantina":"gender","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel": +"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize", +"PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMALAMAN":"pagename","PAGENAME":"pagename","NAMALAMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMALAMANPENUH":"fullpagename","FULLPAGENAME":"fullpagename","NAMALAMANPENUHE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace", +"NAMESPACE":"namespace","RUANGNAMAE":"namespacee","NAMESPACEE":"namespacee","NOMBORRUANGNAMA":"namespacenumber","NAMESPACENUMBER":"namespacenumber","RUANGBINCANG":"talkspace","TALKSPACE":"talkspace","RUANGBINCANGE":"talkspacee","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","BULANSEMASA":"currentmonth","BULANSEMASA2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANSEMASA1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANSEMASA":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMABULANSEMASAGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","SINGBULANSEMASA":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARISEMASA":"currentday","CURRENTDAY":"currentday","HARISEMASA2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARISEMASA":"currentdayname","CURRENTDAYNAME":"currentdayname","TAHUNSEMASA":"currentyear","CURRENTYEAR":"currentyear","WAKTUSEMASA":"currenttime","CURRENTTIME":"currenttime","JAMSEMASA":"currenthour","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","NAMATAPAK":"sitename","SITENAME":"sitename","MINGGUSEMASA":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize", +"CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mt.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mt.json new file mode 100644 index 0000000..8666bc2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mt.json @@ -0,0 +1,10 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__ebdawerrej__":"notoc","__notoc__":"notoc","__ebdagallerija__":"nogallery","__nogallery__":"nogallery","__sforzawerrej__":"forcetoc","__forcetoc__":"forcetoc","__werrej__":"toc","__toc__":"toc","__ebdasezzjonimodifika__":"noeditsection","__noeditsection__":"noeditsection","__ebdakonverturtitlu__":"notitleconvert", +"__ebdakt__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__ebdakonverturkontenut__":"nocontentconvert","__ebdakk__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ĦOLQASEZZJONIĠDIDA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__EBDAĦOLQASEZZJONIĠDIDA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATMOĦBIJA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDIĊI__":"index","__INDEX__":"index","__EBDAINDIĊI__":"noindex","__NOINDEX__":"noindex","__RIINDIRIZZSTATIKU__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"is":"ns","ns":"ns","nse":"nse","urlkodiċi":"urlencode","urlencode":"urlencode","ibdakż":"lcfirst","lcfirst":"lcfirst","ibdakk":"ucfirst","ucfirst": +"ucfirst","kż":"lc","lc":"lc","kk":"uc","uc":"uc","urllokali":"localurl","localurl":"localurl","urllokalie":"localurle","localurle":"localurle","urlsħiħa":"fullurl","fullurl":"fullurl","urlsħiħae":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammatika":"grammar","grammar":"grammar","sess":"gender","gender":"gender","plural":"plural","bidi":"bidi","#lingwa":"language","#language":"language","padxellug":"padleft","padleft":"padleft","padlemin":"padright","padright":"padright","ankrakodiċi":"anchorencode","anchorencode":"anchorencode","destinazzjonital-fajl":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#speċjali":"special","#special":"special","#speciale":"speciale","#tabella":"tag","#tag":"tag","#formatdata":"formatdate","#dataformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke", +"#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","isemtas-server":"servername","servername":"servername","destinazzjonita'skritt":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMRUFIL-GRUPP":"numberingroup","NUMFIL-GRUPP":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORTJA":"defaultsort","DEFAULTSORTJAĊAVETTA":"defaultsort","DEFAULTKATEGORIJISORTJA":"defaultsort", +"DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAĠNIFIL-KATEGORIJA":"pagesincategory","PAĠNIFILK":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DAQSTAL-PAĠNI":"pagesize","PAGESIZE":"pagesize","LIVELLITA'PROTEZZJONI":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ISEMTAL-PAĠNA":"pagename","PAGENAME":"pagename","ISEMTAL-PAĠNAE":"pagenamee","PAGENAMEE":"pagenamee","ISEMSĦIĦTAL-PAĠNA":"fullpagename","FULLPAGENAME":"fullpagename","ISEMTAL-PAĠNASĦIĦAE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ISEMTAS-SOTTOPAĠNA":"subpagename","SUBPAGENAME":"subpagename","ISEMTAS-SUBPAĠNAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ISEMBAŻIKUTAL-PAĠNA":"basepagename","BASEPAGENAME":"basepagename","ISEMTAL-PAĠNATAL-BAŻIE":"basepagenamee","BASEPAGENAMEE":"basepagenamee", +"ISEMPAĠNATA'DISKUSSJONI":"talkpagename","TALKPAGENAME":"talkpagename","ISEMTAL-PAĠNATAD-DISKUSSJONIE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","ISEMTAS-SUĠĠETTTAL-PAĠNA":"subjectpagename","ISEMTAL-ARTIKLUTAL-PAĠNA":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ISEMTAS-SUĠĠETTTAL-PAĠNAE":"subjectpagenamee","ISEMTAL-ARTIKLUTAL-PAĠNAE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDTAR-REVIŻJONI":"revisionid","REVISIONID":"revisionid","ĠURNATATAR-REVIŻJONI":"revisionday","REVISIONDAY":"revisionday","ĠURNATATAR-REVIŻJONI2":"revisionday2","REVISIONDAY2":"revisionday2","XAHARTAR-REVIŻJONI":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","SENATAR-REVIŻJONI":"revisionyear","REVISIONYEAR":"revisionyear","TIMBRUTAR-REVIŻJONI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES": +"cascadingsources","SPAZJUTAL-ISEM":"namespace","NAMESPACE":"namespace","SPAZJUTAL-ISEME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","SPAZJUTA'DISKUSSJONI":"talkspace","TALKSPACE":"talkspace","SPAZJUTA'DISKUSSJONIE":"talkspacee","TALKSPACEE":"talkspacee","SPAZJUTAS-SUĠĠETT":"subjectspace","SPAZJUTAL-ARTIKLU":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMRUTA'ARTIKLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMRUTA'FAJLS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMRUTA'UTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMRUTA'UTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMRUTA'PAĠNI":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMRUTA'AMMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBRUTA'MODIFIKI":"numberofedits","NUMBEROFEDITS":"numberofedits","URITITLU": +"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","XAHARKURRENTI":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","ISEMXAHARKURRENTI":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","ĠENISEMXAHARKURRENTI":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ABBREVXAHARKURRENTI":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ĠURNATAKURRENTI":"currentday","CURRENTDAY":"currentday","ĠURNATAKURRENTI2":"currentday2","CURRENTDAY2":"currentday2","ISEMĠURNATAKURRENTI":"currentdayname","CURRENTDAYNAME":"currentdayname","SENAKURRENTI":"currentyear","CURRENTYEAR":"currentyear","ĦINKURRENTI":"currenttime","CURRENTTIME":"currenttime","SIEGĦAKURRENTI":"currenthour","CURRENTHOUR":"currenthour","XAHARLOKALI":"localmonth","XAHARLOKALI2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","XAHARLOKALI1":"localmonth1","LOCALMONTH1":"localmonth1","ISEMXAHARLOKALI": +"localmonthname","LOCALMONTHNAME":"localmonthname","ĠENISEMXAHARLOKALI":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABBREVXAHARLOKALI":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ĠURNATALOKALI":"localday","LOCALDAY":"localday","ĠURNATALOKALI2":"localday2","LOCALDAY2":"localday2","ISEMTAL-ĠURNATALOKALI":"localdayname","LOCALDAYNAME":"localdayname","SENALOKALI":"localyear","LOCALYEAR":"localyear","ĦINLOKALI":"localtime","LOCALTIME":"localtime","SIEGĦALOKALI":"localhour","LOCALHOUR":"localhour","ISEMTAS-SIT":"sitename","SITENAME":"sitename","ĠIMGĦAKURRENTI":"currentweek","CURRENTWEEK":"currentweek","ĠTĠKURRENTI":"currentdow","CURRENTDOW":"currentdow","ĠIMGĦALOKALI":"localweek","LOCALWEEK":"localweek","ĠTĠLOKALI":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERŻJONIKURRENTI":"currentversion","CURRENTVERSION":"currentversion","TIMBRUTAL-ĦINKURRENTI":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp", +"TIMBRUTAL-ĦINLOKALI":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKATAD-DIREZZJONI":"directionmark","MARKADIRE":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LINGWATAL-KONTENUT":"contentlanguage","LINGKONTENUT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mus.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mus.json new file mode 100644 index 0000000..a906994 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mus.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"charinsert":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mwl.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mwl.json new file mode 100644 index 0000000..64d88e8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mwl.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__semtdc__":"notoc","__semsumário__":"notoc","__notoc__":"notoc","__semgaleria__":"nogallery","__nogallery__":"nogallery","__forcartdc__":"forcetoc","__forcarsumario__":"forcetoc","__forçartdc__":"forcetoc","__forçarsumário__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc","__sumário__":"toc","__sumario__":"toc", +"__toc__":"toc","__nãoeditarseção__":"noeditsection","__semeditarseção__":"noeditsection","__naoeditarsecao__":"noeditsection","__semeditarsecao__":"noeditsection","__noeditsection__":"noeditsection","__semconvertertitulo__":"notitleconvert","__semconvertertítulo__":"notitleconvert","__semct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__semconverterconteudo__":"nocontentconvert","__semconverterconteúdo__":"nocontentconvert","__semcc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LINKDENOVASECAO__":"newsectionlink","__LINKDENOVASEÇÃO__":"newsectionlink","__LIGACAODENOVASECAO__":"newsectionlink","__LIGAÇÃODENOVASEÇÃO__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__SEMLINKDENOVASECAO__":"nonewsectionlink","__SEMLINKDENOVASEÇÃO__":"nonewsectionlink","__SEMLIGACAODENOVASECAO__":"nonewsectionlink","__SEMLIGAÇÃODENOVASEÇÃO__":"nonewsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__CATEGORIAOCULTA__":"hiddencat","__CATOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NAOINDEXAR__":"noindex","__NÃOINDEXAR__":"noindex","__NOINDEX__":"noindex","_ANCAMINARSTATICO_":"staticredirect","__REDIRECIONAMENTOESTATICO__":"staticredirect","__REDIRECIONAMENTOESTÁTICO__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","codificaurl":"urlencode","urlencode":"urlencode","primeiraminuscula":"lcfirst","primeiraminúscula":"lcfirst","lcfirst":"lcfirst","primeiramaiuscula":"ucfirst","primeiramaiúscula":"ucfirst","ucfirst":"ucfirst","minuscula":"lc","minúscula":"lc","minusculas":"lc","minúsculas":"lc","lc":"lc","maiuscula":"uc","maiúscula":"uc","maiusculas":"uc","maiúsculas":"uc","uc":"uc","localurl" +:"localurl","localurle":"localurle","urlcompleto":"fullurl","fullurl":"fullurl","urlcompletoc":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genero":"gender","gênero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#lhengua":"language","#idioma":"language","#language":"language","padleft":"padleft","padright":"padright","codificaancora":"anchorencode","codificaâncora":"anchorencode","anchorencode":"anchorencode","caminofexeiro":"filepath","caminhodoarquivo":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#eitiqueta":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seigual":"ifeq","#ifeq":"ifeq","#switch":"switch","#seexiste":"ifexist","#ifexist":"ifexist","#seexpr": +"ifexpr","#ifexpr":"ifexpr","#seerro":"iferror","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#partesdotítulo":"titleparts","#partesdotitulo":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#trecho":"lst","#lstx":"lstx","#section-x":"lstx","#trecho-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#propriedade":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","servidor":"server","server":"server","nomedoservidor":"servername","servername":"servername","caminhodoscript":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMERONOGRUPO":"numberingroup","NÚMERONOGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENACAOPADRAO":"defaultsort", +"ORDENAÇÃOPADRÃO":"defaultsort","ORDEMPADRAO":"defaultsort","ORDEMPADRÃO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINASNACATEGORIA":"pagesincategory","PÁGINASNACATEGORIA":"pagesincategory","PAGINASNACAT":"pagesincategory","PÁGINASNACAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMANHOFEXEIRO":"pagesize","TAMANHODAPAGINA":"pagesize","TAMANHODAPÁGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECAO":"protectionlevel","NÍVELDEPROTEÇÃO":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMEDAPAGINA":"pagename","NOMEDAPÁGINA":"pagename","PAGENAME":"pagename","NOMEDAPAGINAC":"pagenamee","NOMEDAPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMECOMPLETODAPAGINA":"fullpagename","NOMECOMPLETODAPÁGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMECOMPLETODAPAGINAC":"fullpagenamee","NOMECOMPLETODAPÁGINAC": +"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMEDASUBPAGINA":"subpagename","NOMEDASUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMEDASUBPAGINAC":"subpagenamee","NOMEDASUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","NOMEDAPAGINABASE":"basepagename","NOMEDAPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMEDAPAGINABASEC":"basepagenamee","NOMEDAPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMEDAPAGINADEDISCUSSAO":"talkpagename","NOMEDAPÁGINADEDISCUSSÃO":"talkpagename","TALKPAGENAME":"talkpagename","NOMEDAPAGINADEDISCUSSAOC":"talkpagenamee","NOMEDAPÁGINADEDISCUSSÃOC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMEDAPAGINADECONTEUDO":"subjectpagename","NOMEDAPÁGINADECONTEÚDO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMEDAPAGINADECONTEUDOC":"subjectpagenamee","NOMEDAPÁGINADECONTEÚDOC":"subjectpagenamee", +"SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDAREVISAO":"revisionid","IDDAREVISÃO":"revisionid","REVISIONID":"revisionid","DIADAREVISAO":"revisionday","DIADAREVISÃO":"revisionday","REVISIONDAY":"revisionday","DIADAREVISAO2":"revisionday2","DIADAREVISÃO2":"revisionday2","REVISIONDAY2":"revisionday2","MESDAREVISAO":"revisionmonth","MÊSDAREVISÃO":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ANODAREVISAO":"revisionyear","ANODAREVISÃO":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODAREVISAO":"revisionuser","USUÁRIODAREVISÃO":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","DOMINIO":"namespace","DOMÍNIO":"namespace","ESPACONOMINAL":"namespace","ESPAÇONOMINAL":"namespace","NAMESPACE":"namespace","DOMINIOC":"namespacee","DOMÍNIOC":"namespacee","ESPACONOMINALC":"namespacee","ESPAÇONOMINALC":"namespacee","NAMESPACEE":"namespacee", +"NAMESPACENUMBER":"namespacenumber","PAGINADEDISCUSSAO":"talkspace","PÁGINADEDISCUSSÃO":"talkspace","TALKSPACE":"talkspace","PAGINADEDISCUSSAOC":"talkspacee","PÁGINADEDISCUSSÃOC":"talkspacee","TALKSPACEE":"talkspacee","PAGINADECONTEUDO":"subjectspace","PAGINADECONTEÚDO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","PAGINADECONTEUDOC":"subjectspacee","PAGINADECONTEÚDOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMERODEARTIGOS":"numberofarticles","NÚMERODEARTIGOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMERODEARQUIVOS":"numberoffiles","NÚMERODEARQUIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMERODEUSUARIOS":"numberofusers","NÚMERODEUSUÁRIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMERODEUSUARIOSATIVOS":"numberofactiveusers","NÚMERODEUSUÁRIOSATIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMERODEPAGINAS":"numberofpages", +"NÚMERODEPÁGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMERODEEDICOES":"numberofedits","NÚMERODEEDIÇÕES":"numberofedits","NUMBEROFEDITS":"numberofedits","EXIBETITULO":"displaytitle","EXIBETÍTULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESATUAL":"currentmonth","MESATUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESATUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMEDOMESATUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESATUALABREV":"currentmonthabbrev","MESATUALABREVIADO":"currentmonthabbrev","ABREVIATURADOMESATUAL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DIAATUAL":"currentday","CURRENTDAY":"currentday","DIAATUAL2":"currentday2","CURRENTDAY2":"currentday2","NOMEDODIAATUAL":"currentdayname", +"CURRENTDAYNAME":"currentdayname","ANOATUAL":"currentyear","CURRENTYEAR":"currentyear","HORARIOATUAL":"currenttime","CURRENTTIME":"currenttime","HORAATUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEDOMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREV":"localmonthabbrev","MESLOCALABREVIADO":"localmonthabbrev","ABREVIATURADOMESLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","LOCALDAY2":"localday2","NOMEDODIALOCAL":"localdayname","LOCALDAYNAME":"localdayname","ANOLOCAL":"localyear","LOCALYEAR":"localyear","HORARIOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMEDOSITE":"sitename","NOMEDOSÍTIO":"sitename","NOMEDOSITIO":"sitename","SITENAME":"sitename", +"SEMANAATUAL":"currentweek","CURRENTWEEK":"currentweek","DIADASEMANAATUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DIADASEMANALOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","REVISAOATUAL":"currentversion","REVISÃOATUAL":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","IDIOMADOCONTEUDO":"contentlanguage","IDIOMADOCONTEÚDO":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/", +"linkTrailCharacters":"/^([áâãàéêẽçíòóôõq̃úüűũa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-my.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-my.json new file mode 100644 index 0000000..36d44fb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-my.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates", +"#ဖော်ပြ":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize", +"PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev", +"LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-myv.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-myv.json new file mode 100644 index 0000000..90f4d7e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-myv.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","васенце_тештькесь_вишкине":"lcfirst","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","васенце_тештькесь_покш":"ucfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","вишка_тештькесэ":"lc","маленькими_буквами":"lc","lc":"lc","пошк_тештькесэ":"uc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число": +"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","сыме":"gender","пол":"gender","gender":"gender","ламоньчисла":"plural","множественное_число":"plural","plural":"plural","bidi":"bidi","#келесь":"language","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","файланьки":"filepath","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#башка_тевень":"special","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate", +"#деревокатегорий":"categorytree","#categorytree":"categorytree","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername", +"servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ЗЯРОЛОПАТ":"numberofpages","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","ЗЯРОТЕИЦЯТ":"numberofusers","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ЗЯРОСЁРМАДОВКСТ":"numberofarticles","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ЗЯРОФАЙЛАТ":"numberoffiles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","ЗЯРОАДМИНТНЭДЕ":"numberofadmins","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS": +"numberofadmins","ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ЗЯРОВИТНЕМАТПЕТНЕМАТ":"numberofedits","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ЛОПАКУВАЛМО":"pagesize","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber", +"КОРТАМОПОТМО":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ЛОПАЛЕМ":"pagename","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ЛОПАЛЕМКУВАКАСТО":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename", +"BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","КОРТАМОЛОПАЛЕМ":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ЛИЯКСТОМТОМАID":"revisionid","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ЛИЯКСТОМТОМАЧЫ":"revisionday", +"ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ЛИЯКСТОМТОМАЧЫ2":"revisionday2","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","ЛИЯКСТОМТОМАКОВ":"revisionmonth","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ЛИЯКСТОМТОМАИЕ":"revisionyear","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ЛЕМПОТМО":"namespace","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","НЕВТЕМС_КОНЯКС":"displaytitle","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","МОЛИЦЯКОВ":"currentmonth", +"ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","МОЛИЦЯКОВЛЕМ":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","МОЛИЦЯКОВЛЕМГЕН":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","МОЛИЦЯКОВКИРЬТЯНЬХВОРМА":"currentmonthabbrev","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","МОЛИЦЯЧЫ":"currentday","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","МОЛИЦЯЧЫ2":"currentday2","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","МОЛИЦЯЧЫЛЕМ":"currentdayname", +"НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","МОЛИЦЯИЕ":"currentyear","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","МОЛИЦЯШКА":"currenttime","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","МОЛИЦЯЦЯС":"currenthour","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","ТЕСКЭНЬКОВ":"localmonth","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","ТЕСКЭНЬКОВЛЕМ":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","ТЕСКЭНЬКОВЛЕМГЕН":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen", +"ТЕСКЭНЬКОВКИРЬТЯНЬХВОРМА":"localmonthabbrev","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ТЕСКЭНЬЧЫ":"localday","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","ТЕСКЭНЬЧЫ2":"localday2","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","ТЕСКЭНЬЧЫЛЕМ":"localdayname","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","ТЕСКЭНЬИЕ":"localyear","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","ТЕСКЭНЬШКА":"localtime","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","ТЕСКЭНЬЦЯС":"localhour","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","САЙТЛЕМ":"sitename","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename","МОЛИЦЯ_ТАРГО":"currentweek","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek", +"МОЛИЦЯ_ЧИ":"currentdow","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","ТЕСКЭНЬТАРГО":"localweek","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕВАТЕВЕРСИЯ":"currentversion","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mzn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mzn.json new file mode 100644 index 0000000..040a5de --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-mzn.json @@ -0,0 +1,16 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__بی‌فهرست__":"notoc","__notoc__":"notoc","__بی‌نگارخنه__":"nogallery","__بی‌نگارخانه__":"nogallery","__nogallery__":"nogallery","__بافهرست__":"forcetoc","__forcetoc__":"forcetoc","__فهرست__":"toc","__toc__":"toc","__بی‌بخش__":"noeditsection","__noeditsection__": +"noeditsection","__عنوان‌تبدیل‌نشده__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__محتواتبدیل‌نشده__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__بخش‌جدید__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__بی‌پیوندبخش__":"nonewsectionlink","__بی‌پیوند‌بخش‌جدید__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__رده‌پنهان__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__نمایه__":"index","__INDEX__":"index","__بی‌نمایه__":"noindex","__NOINDEX__":"noindex","__تغییرمسیرثابت__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"فن":"ns","ns":"ns","فنک":"nse","nse":"nse", +"کدنشانی":"urlencode","urlencode":"urlencode","ابتداکوچک":"lcfirst","ابتدا_کوچک":"lcfirst","lcfirst":"lcfirst","ابتدابزرگ":"ucfirst","ابتدا_بزرگ":"ucfirst","ucfirst":"ucfirst","ک":"lc","lc":"lc","ب":"uc","uc":"uc","نشونی":"localurl","نشانی":"localurl","localurl":"localurl","نشانی‌کد":"localurle","نشانی_کد":"localurle","localurle":"localurle","نشانی‌کامل":"fullurl","نشانی_کامل":"fullurl","fullurl":"fullurl","نشانی‌کامل‌کد":"fullurle","نشانی_کامل_کد":"fullurle","fullurle":"fullurle","نشانی_استاندارد":"canonicalurl","نشانی‌استاندارد":"canonicalurl","canonicalurl":"canonicalurl","نشانی_استاندارد_کد":"canonicalurle","نشانی‌استانداردکد":"canonicalurle","canonicalurle":"canonicalurle","آرایش‌عدد":"formatnum","آرایش_عدد":"formatnum","formatnum":"formatnum","دستور_زبون":"grammar", +"دستور_زوون":"grammar","دستورزبان":"grammar","دستور_زبان":"grammar","grammar":"grammar","جنسیت":"gender","جنس":"gender","gender":"gender","جمع":"plural","plural":"plural","bidi":"bidi","#زبان":"language","#language":"language","لبه‌چپ":"padleft","لبه_چپ":"padleft","padleft":"padleft","لبه‌راست":"padright","لبه_راست":"padright","padright":"padright","کدلنگر":"anchorencode","anchorencode":"anchorencode","مسیرپرونده":"filepath","مسیر_پرونده":"filepath","filepath":"filepath","شناسه_صفحه":"pageid","pageid":"pageid","ترجمه":"int","int":"int","#ویژه":"special","#special":"special","#ویژه_ای":"speciale","#speciale":"speciale","#برچسب":"tag","#tag":"tag","#آرایش‌تاریخ":"formatdate","#آرایش_تاریخ":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#هدف":"target","#target":"target","#بابل":"babel","#babel":"babel","#coordinates" +:"coordinates","#درخواست":"invoke","#invoke":"invoke","#related":"related","#اگر":"if","#if":"if","#اگرمساوی":"ifeq","#ifeq":"ifeq","#گزینه":"switch","#switch":"switch","#اگرموجود":"ifexist","#ifexist":"ifexist","#اگرحساب":"ifexpr","#ifexpr":"ifexpr","#اگرخطا":"iferror","#iferror":"iferror","#زمان":"time","#time":"time","#زمان‌بلند":"timel","#timel":"timel","#حساب":"expr","#expr":"expr","#نسبی‌به‌مطلق":"rel2abs","#rel2abs":"rel2abs","#پاره‌عنوان":"titleparts","#titleparts":"titleparts","#درخت‌رده":"categorytree","#درخت_رده":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#ویژگی":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","مسیرمقاله":"articlepath", +"مسیر_مقاله":"articlepath","articlepath":"articlepath","سرور":"server","کارساز":"server","server":"server","نام‌کارساز":"servername","نام_کارساز":"servername","نام‌سرور":"servername","نام_سرور":"servername","servername":"servername","مسیرسند":"scriptpath","مسیر_سند":"scriptpath","scriptpath":"scriptpath","مسیرسبک":"stylepath","مسیر_سبک":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"تعداددرگروه":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ترتیب":"defaultsort","ترتیب‌پیش‌فرض":"defaultsort","ترتیب_پیش_فرض":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","صفحه‌دررده":"pagesincategory","صفحه_در_رده":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory", +"اندازه‌صفحه":"pagesize","اندازه_صفحه":"pagesize","PAGESIZE":"pagesize","سطح‌حفاطت":"protectionlevel","سطح_حفاظت":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","نام‌صفحه":"pagename","نام_صفحه":"pagename","PAGENAME":"pagename","نام‌صفحه‌کد":"pagenamee","نام_صفحه_کد":"pagenamee","PAGENAMEE":"pagenamee","نام‌کامل‌صفحه":"fullpagename","نام_کامل_صفحه":"fullpagename","FULLPAGENAME":"fullpagename","نام‌کامل‌صفحه‌کد":"fullpagenamee","نام_کامل_صفحه_کد":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","نام‌زیرصفحه":"subpagename","نام_زیرصفحه":"subpagename","SUBPAGENAME":"subpagename","نام‌زیرصفحه‌کد":"subpagenamee","نام_زیرصفحه_کد":"subpagenamee","SUBPAGENAMEE":"subpagenamee","نام_صفحه_ریشه":"rootpagename","ROOTPAGENAME":"rootpagename", +"نام_صفحه_ریشه_ای":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","نام‌صفحه‌مبنا":"basepagename","نام_صفحه_مبنا":"basepagename","BASEPAGENAME":"basepagename","نام‌صفحه‌مبناکد":"basepagenamee","نام_صفحه_مبنا_کد":"basepagenamee","BASEPAGENAMEE":"basepagenamee","نام‌صفحه‌بحث":"talkpagename","نام_صفحه_بحث":"talkpagename","TALKPAGENAME":"talkpagename","نام‌صفحه‌بحث‌کد":"talkpagenamee","نام_صفحه_بحث_کد":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","نام‌صفحه‌موضوع":"subjectpagename","نام‌صفحه‌مقاله":"subjectpagename","نام_صفحه_موضوع":"subjectpagename","نام_صفحه_مقاله":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","نام‌صفحه‌موضوع‌کد":"subjectpagenamee","نام‌صفحه‌مقاله‌کد":"subjectpagenamee","نام_صفحه_موضوع_کد": +"subjectpagenamee","نام_صفحه_مقاله_کد":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","نسخه":"revisionid","شماره‌نسخه":"revisionid","شماره_نسخه":"revisionid","REVISIONID":"revisionid","روزنسخه":"revisionday","روز_نسخه":"revisionday","REVISIONDAY":"revisionday","روزنسخه۲":"revisionday2","روز_نسخه۲":"revisionday2","روز_نسخه_۲":"revisionday2","REVISIONDAY2":"revisionday2","ماه‌نسخه":"revisionmonth","ماه_نسخه":"revisionmonth","REVISIONMONTH":"revisionmonth","ماه‌نسخه۱":"revisionmonth1","ماه_نسخه_۱":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","سال‌نسخه":"revisionyear","سال_نسخه":"revisionyear","REVISIONYEAR":"revisionyear","زمان‌یونیکسی‌نسخه":"revisiontimestamp","زمان‌نسخه":"revisiontimestamp","زمان_یونیکسی_نسخه":"revisiontimestamp","زمان_نسخه": +"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","کاربرنسخه":"revisionuser","کاربر_نسخه":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","فضای‌نام":"namespace","فضای_نام":"namespace","NAMESPACE":"namespace","فضای‌نام‌کد":"namespacee","فضای_نام_کد":"namespacee","NAMESPACEE":"namespacee","شماره_فضای_نام":"namespacenumber","شماره‌فضای‌نام":"namespacenumber","NAMESPACENUMBER":"namespacenumber","فضای‌گپ":"talkspace","فضای_گپ":"talkspace","فضای‌بحث":"talkspace","فضای_بحث":"talkspace","TALKSPACE":"talkspace","فضای‌گپ_کد":"talkspacee","فضای_گپ_کد":"talkspacee","فضای‌بحث‌کد":"talkspacee","فضای_بحث_کد":"talkspacee","TALKSPACEE":"talkspacee","فضای‌موضوع":"subjectspace","فضای‌مقاله":"subjectspace","فضای_موضوع":"subjectspace","فضای_مقاله":"subjectspace", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","فضای‌موضوع‌کد":"subjectspacee","فضای‌مقاله‌کد":"subjectspacee","فضای_موضوع_کد":"subjectspacee","فضای_مقاله_کد":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","تعدادمقاله‌ها":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","تعدادپرونده‌ها":"numberoffiles","NUMBEROFFILES":"numberoffiles","تعدادکارورون":"numberofusers","تعدادکاربران":"numberofusers","NUMBEROFUSERS":"numberofusers","کارورون_فعال":"numberofactiveusers","کاربران_فعال":"numberofactiveusers","کاربران‌فعال":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","تعدادصفحه‌ها":"numberofpages","تعداد_صفحه‌ها":"numberofpages","ولگ‌ئون_نمره":"numberofpages","وألگ‌ئون_نومره":"numberofpages","NUMBEROFPAGES":"numberofpages", +"تعدادمدیران":"numberofadmins","NUMBEROFADMINS":"numberofadmins","تعداددچی‌یه‌ئون":"numberofedits","تعدادویرایش‌ها":"numberofedits","NUMBEROFEDITS":"numberofedits","عنوان‌ظاهری":"displaytitle","عنوان_ظاهری":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ماه":"currentmonth","ماه‌کنونی":"currentmonth","ماه_کنونی":"currentmonth","ماه‌کنونی۲":"currentmonth","ماه_اسایی۲":"currentmonth","ماه_کنونی۲":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ماه۱":"currentmonth1","ماه‌کنونی۱":"currentmonth1","ماه_کنونی۱":"currentmonth1","CURRENTMONTH1":"currentmonth1","نام‌ماه":"currentmonthname","نام_ماه":"currentmonthname","نام‌ماه‌کنونی":"currentmonthname","نام_ماه_کنونی":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","نام‌ماه‌اضافه":"currentmonthnamegen", +"نام_ماه_اضافه":"currentmonthnamegen","نام‌ماه‌کنونی‌اضافه":"currentmonthnamegen","نام_ماه_کنونی_اضافه":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","مخفف‌نام‌ماه":"currentmonthabbrev","مخفف_نام_ماه":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","روز":"currentday","روزکنونی":"currentday","روز_کنونی":"currentday","CURRENTDAY":"currentday","روز۲":"currentday2","روز_۲":"currentday2","CURRENTDAY2":"currentday2","نام‌روز":"currentdayname","نام_روز":"currentdayname","CURRENTDAYNAME":"currentdayname","سال":"currentyear","سال‌کنونی":"currentyear","سال_کنونی":"currentyear","CURRENTYEAR":"currentyear","زمان‌کنونی":"currenttime","زمان_کنونی":"currenttime","CURRENTTIME":"currenttime","ساعت":"currenthour","ساعت‌کنونی":"currenthour","ساعت_کنونی":"currenthour","CURRENTHOUR":"currenthour" +,"ماه‌محلی":"localmonth","ماه_محلی":"localmonth","ماه‌محلی۲":"localmonth","ماه_محلی۲":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","ماه‌محلی۱":"localmonth1","ماه_محلی۱":"localmonth1","LOCALMONTH1":"localmonth1","نام‌ماه‌محلی":"localmonthname","نام_ماه_محلی":"localmonthname","LOCALMONTHNAME":"localmonthname","نام‌ماه‌محلی‌اضافه":"localmonthnamegen","نام_ماه_محلی_اضافه":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","مخفف‌ماه‌محلی":"localmonthabbrev","مخفف_ماه_محلی":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","روزمحلی":"localday","روز_محلی":"localday","LOCALDAY":"localday","روزمحلی۲":"localday2","روز_محلی_۲":"localday2","LOCALDAY2":"localday2","نام‌روزمحلی":"localdayname","نام_روز_محلی":"localdayname","LOCALDAYNAME":"localdayname","سال‌محلی": +"localyear","سال_محلی":"localyear","LOCALYEAR":"localyear","زمون_محلی":"localtime","زمان_محلی":"localtime","زمان‌محلی":"localtime","LOCALTIME":"localtime","ساعت‌محلی":"localhour","ساعت_محلی":"localhour","LOCALHOUR":"localhour","نام‌وبگاه":"sitename","نام_وبگاه":"sitename","SITENAME":"sitename","هفته":"currentweek","CURRENTWEEK":"currentweek","روزهفته":"currentdow","روز_هفته":"currentdow","CURRENTDOW":"currentdow","هفته‌محلی":"localweek","هفته_محلی":"localweek","LOCALWEEK":"localweek","روزهفته‌محلی":"localdow","روز_هفته_محلی":"localdow","LOCALDOW":"localdow","اندازهٔ‌نسخه":"revisionsize","اندازهٔ_نسخه":"revisionsize","REVISIONSIZE":"revisionsize","نسخه‌کنونی":"currentversion","نسخه_کنونی":"currentversion","CURRENTVERSION":"currentversion","زمان‌یونیکسی":"currenttimestamp","زمان_یونیکسی": +"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","زمان‌یونیکسی‌محلی":"localtimestamp","زمان_یونیکسی_محلی":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","علامت‌جهت":"directionmark","علامت_جهت":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","زبان‌محتوا":"contentlanguage","زبان_محتوا":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیآأئؤة‌]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-na.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-na.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-na.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nah.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nah.json new file mode 100644 index 0000000..968d03e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nah.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__sin_tdc__":"notoc","__notdc__":"notoc","__notoc__":"notoc","__sin_galería__":"nogallery","__nogalería__":"nogallery","__nogaleria__":"nogallery","__nogallery__":"nogallery","__forzar_tdc__":"forcetoc","__forzartdc__":"forcetoc","__forzartoc__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc","__toc__":"toc", +"__no_editar_sección__":"noeditsection","__noeditarsección__":"noeditsection","__noeditarseccion__":"noeditsection","__noeditsection__":"noeditsection","__noconvertirtitulo__":"notitleconvert","__noconvertirtítulo__":"notitleconvert","__noct___":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__noconvertircontenido__":"nocontentconvert","__nocc___":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__VINCULARANUEVASECCION__":"newsectionlink","__ENLACECREARSECCIÓN__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NOVINCULARANUEVASECCION__":"nonewsectionlink","__SINENLACECREARSECCIÓN__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATEGORÍAOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECCIÓNESTÁTICA__":"staticredirect", +"__REDIRECCIONESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIGUACION__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"en":"ns","ns":"ns","nse":"nse","codificarurl":"urlencode","urlencode":"urlencode","primerominus":"lcfirst","primerominús":"lcfirst","lcfirst":"lcfirst","primeromayus":"ucfirst","primeromayús":"ucfirst","ucfirst":"ucfirst","minus":"lc","minús":"lc","lc":"lc","mayus":"uc","mayús":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","urllocalc":"localurle","localurle":"localurle","urlcompleta":"fullurl","fullurl":"fullurl","urlcompletac":"fullurle","fullurle":"fullurle","urlcanonica":"canonicalurl","canonicalurl":"canonicalurl","urlcanonicac":"canonicalurle","canonicalurle":"canonicalurle","formatonúmero":"formatnum","formatonumero":"formatnum","formatnum":"formatnum","gramatica":"grammar","gramática":"grammar","grammar": +"grammar","género":"gender","genero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#idioma":"language","#language":"language","rellenarizquierda":"padleft","rellenarizq":"padleft","padleft":"padleft","rellenarderecha":"padright","rellenarder":"padright","padright":"padright","anchorencode":"anchorencode","rutaarchivo":"filepath","rutarchivo":"filepath","rutadearchivo":"filepath","filepath":"filepath","iddepágina":"pageid","idpágina":"pageid","iddepagina":"pageid","idpagina":"pageid","pageid":"pageid","int":"int","#especial":"special","#special":"special","#especialc":"speciale","#speciale":"speciale","#etiqueta":"tag","#tag":"tag","#formatodefecha":"formatdate","#formatearfecha":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#destino":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#invocar":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#siigual":"ifeq","#ifeq":"ifeq","#según":"switch","#switch" +:"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierror":"iferror","#iferror":"iferror","#tiempo":"time","#time":"time","#tiempol":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#árboldecategorías":"categorytree","#arboldecategorias":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","nointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propiedad":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","rutaartículo":"articlepath","rutaarticulo":"articlepath","articlepath":"articlepath","servidor":"server","server":"server","nombreservidor":"servername","servername":"servername","rutascript":"scriptpath","rutadescript":"scriptpath","scriptpath":"scriptpath","rutaestilo":"stylepath","rutadeestilo":"stylepath", +"stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NÚMEROENGRUPO":"numberingroup","NUMEROENGRUPO":"numberingroup","NUMENGRUPO":"numberingroup","NÚMENGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENAR":"defaultsort","CLAVEDEORDENPREDETERMINADO":"defaultsort","ORDENDECATEGORIAPREDETERMINADO":"defaultsort","ORDENDECATEGORÍAPREDETERMINADO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PÁGINASENCATEGORÍA":"pagesincategory","PÁGINASENCAT":"pagesincategory","PAGSENCAT":"pagesincategory","PAGINASENCATEGORIA":"pagesincategory","PAGINASENCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMAÑOPÁGINA":"pagesize","TAMAÑODEPÁGINA":"pagesize","TAMAÑOPAGINA":"pagesize","TAMAÑODEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECCIÓN":"protectionlevel","NIVELDEPROTECCION":"protectionlevel","PROTECTIONLEVEL" +:"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMBREDEPAGINA":"pagename","NOMBREDEPÁGINA":"pagename","PAGENAME":"pagename","NOMBREDEPAGINAC":"pagenamee","NOMBREDEPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMBRECOMPLETODEPÁGINA":"fullpagename","NOMBRECOMPLETODEPAGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMBRECOMPLETODEPAGINAC":"fullpagenamee","NOMBRECOMPLETODEPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMBREDESUBPAGINA":"subpagename","NOMBREDESUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMBREDESUBPAGINAC":"subpagenamee","NOMBREDESUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMBREDEPAGINARAIZ":"rootpagename","NOMBREDEPÁGINARAÍZ":"rootpagename","ROOTPAGENAME":"rootpagename","NOMBREDEPAGINARAIZC":"rootpagenamee","NOMBREDEPÁGINARAÍZC":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBREDEPAGINABASE":"basepagename","NOMBREDEPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMBREDEPAGINABASEC": +"basepagenamee","NOMBREDEPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMBREDEPÁGINADEDISCUSIÓN":"talkpagename","NOMBREDEPAGINADEDISCUSION":"talkpagename","NOMBREDEPAGINADISCUSION":"talkpagename","NOMBREDEPÁGINADISCUSIÓN":"talkpagename","TALKPAGENAME":"talkpagename","NOMBREDEPÁGINADEDISCUSIÓNC":"talkpagenamee","NOMBREDEPAGINADEDISCUSIONC":"talkpagenamee","NOMBREDEPAGINADISCUSIONC":"talkpagenamee","NOMBREDEPÁGINADISCUSIÓNC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMBREDEPAGINADETEMA":"subjectpagename","NOMBREDEPÁGINADETEMA":"subjectpagename","NOMBREDEPÁGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEARTICULO":"subjectpagename","NOMBREDEPÁGINADEARTÍCULO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMBREDEPAGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEASUNTOC": +"subjectpagenamee","NOMBREDEPAGINADEARTICULOC":"subjectpagenamee","NOMBREDEPÁGINADEARTÍCULOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDEREVISION":"revisionid","IDREVISION":"revisionid","IDDEREVISIÓN":"revisionid","IDREVISIÓN":"revisionid","REVISIONID":"revisionid","DIADEREVISION":"revisionday","DIAREVISION":"revisionday","DÍADEREVISIÓN":"revisionday","DÍAREVISIÓN":"revisionday","REVISIONDAY":"revisionday","DIADEREVISION2":"revisionday2","DIAREVISION2":"revisionday2","DÍADEREVISIÓN2":"revisionday2","DÍAREVISIÓN2":"revisionday2","REVISIONDAY2":"revisionday2","MESDEREVISION":"revisionmonth","MESDEREVISIÓN":"revisionmonth","MESREVISION":"revisionmonth","MESREVISIÓN":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDEREVISION1":"revisionmonth1","MESDEREVISIÓN1":"revisionmonth1","MESREVISION1":"revisionmonth1","MESREVISIÓN1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","AÑODEREVISION":"revisionyear", +"AÑODEREVISIÓN":"revisionyear","AÑOREVISION":"revisionyear","AÑOREVISIÓN":"revisionyear","REVISIONYEAR":"revisionyear","MARCADEHORADEREVISION":"revisiontimestamp","MARCADEHORADEREVISIÓN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODEREVISION":"revisionuser","USUARIODEREVISIÓN":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACIODENOMBRE":"namespace","NAMESPACE":"namespace","ESPACIODENOMBREC":"namespacee","NAMESPACEE":"namespacee","NÚMERODELESPACIO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACIODEDISCUSION":"talkspace","ESPACIODEDISCUSIÓN":"talkspace","TALKSPACE":"talkspace","ESPACIODEDISCUSIONC":"talkspacee","TALKSPACEE":"talkspacee","ESPACIODEASUNTO":"subjectspace","ESPACIODETEMA":"subjectspace","ESPACIODEARTÍCULO":"subjectspace","ESPACIODEARTICULO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACIODETEMAC":"subjectspacee","ESPACIODEASUNTOC":"subjectspacee", +"ESPACIODEARTICULOC":"subjectspacee","ESPACIODEARTÍCULOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NÚMERODEARTÍCULOS":"numberofarticles","NUMERODEARTICULOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NÚMERODEARCHIVOS":"numberoffiles","NUMERODEARCHIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NÚMERODEPÁGINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NÚMEROADMINIISTRADORES":"numberofadmins","NÚMEROADMINS":"numberofadmins","NUMEROADMINS":"numberofadmins","NUMEROADMINISTRADORES":"numberofadmins","NUMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINS":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINS": +"numberofadmins","NUMBEROFADMINS":"numberofadmins","NÚMERODEEDICIONES":"numberofedits","NUMERODEEDICIONES":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRARTÍTULO":"displaytitle","MOSTRARTITULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESACTUAL":"currentmonth","MESACTUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MESACTUALCOMPLETO":"currentmonthname","NOMBREMESACTUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MESACTUALGENITIVO":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESACTUALABREVIADO":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DÍAACTUAL":"currentday","DIAACTUAL":"currentday","DÍA_ACTUAL":"currentday","DIA_ACTUAL":"currentday","CURRENTDAY":"currentday","DÍAACTUAL2":"currentday2","DIAACTUAL2":"currentday2","DÍA_ACTUAL2":"currentday2","DIA_ACTUAL2":"currentday2","CURRENTDAY2":"currentday2", +"NOMBREDÍAACTUAL":"currentdayname","NOMBREDIAACTUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","AÑOACTUAL":"currentyear","AÑO_ACTUAL":"currentyear","CURRENTYEAR":"currentyear","HORA_MINUTOS_ACTUAL":"currenttime","HORAMINUTOSACTUAL":"currenttime","TIEMPOACTUAL":"currenttime","CURRENTTIME":"currenttime","HORAACTUAL":"currenthour","HORA_ACTUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","MESLOCALCOMPLETO":"localmonthname","NOMBREMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","MESLOCALGENITIVO":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREVIADO":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DÍALOCAL":"localday","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","DÍALOCAL2":"localday2","LOCALDAY2":"localday2","NOMBREDIALOCAL":"localdayname", +"NOMBREDÍALOCAL":"localdayname","LOCALDAYNAME":"localdayname","AÑOLOCAL":"localyear","LOCALYEAR":"localyear","HORAMINUTOSLOCAL":"localtime","TIEMPOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMBREDELSITIO":"sitename","SITENAME":"sitename","SEMANAACTUAL":"currentweek","CURRENTWEEK":"currentweek","DDSACTUAL":"currentdow","DIADESEMANAACTUAL":"currentdow","DÍADESEMANAACTUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DDSLOCAL":"localdow","DIADESEMANALOCAL":"localdow","DÍADESEMANALOCAL":"localdow","LOCALDOW":"localdow","TAMAÑODEREVISIÓN":"revisionsize","TAMAÑODEREVISION":"revisionsize","REVISIONSIZE":"revisionsize","VERSIONACTUAL":"currentversion","VERSIÓNACTUAL":"currentversion","CURRENTVERSION":"currentversion","MARCADEHORAACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MARCADEHORALOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark" +,"DIRMARK":"directionmark","IDIOMADELCONTENIDO":"contentlanguage","IDIOMADELCONT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záéíóúñ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nap.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nap.json new file mode 100644 index 0000000..03b7a4a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nap.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag", +"#formatodata":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#alberocategorie":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath": +"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2", +"CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME" +:"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NOMESITO":"sitename","SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nds-nl.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nds-nl.json new file mode 100644 index 0000000..fc5f927 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nds-nl.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__gienonderwarpen__":"notoc","__geeninhoud__":"notoc","__notoc__":"notoc","__giengallerieje__":"nogallery","__giengalderieje__":"nogallery","__geen_galerij__":"nogallery","__nogallery__":"nogallery","__forseeronderwarpen_":"forcetoc","__inhoud_dwingen__":"forcetoc","__forceerinhoud__":"forcetoc","__forcetoc__":"forcetoc", +"__onderwarpen__":"toc","__inhoud__":"toc","__toc__":"toc","__gienbewarkseksie__":"noeditsection","__nietbewerkbaresectie__":"noeditsection","__noeditsection__":"noeditsection","__gientitelkonversie__":"notitleconvert","__gientk__":"notitleconvert","__geenpaginanaamconversie__":"notitleconvert","__geentitelconversie__":"notitleconvert","__geentc__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__gieninhoudkonversie__":"nocontentconvert","__gienik__":"nocontentconvert","__geeninhoudconversie__":"nocontentconvert","__geenic__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NIEJESEKSIEVERWIEZING__":"newsectionlink","__NIEUWESECTIELINK__":"newsectionlink","__NIEUWESECTIEKOPPELING__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__GIENNIEJKOPJENVERWIEZING__":"nonewsectionlink","__GEENNIEUWKOPJEKOPPELING__":"nonewsectionlink","__GEENNIEUWESECTIELINK__":"nonewsectionlink", +"__GEENNIEUWKOPJEVERWIJZING__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERBÖRGENKAT__":"hiddencat","__VERBORGENCAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__GIENINDEX__":"noindex","__GEENINDEX__":"noindex","__NOINDEX__":"noindex","__STAOTIESEDEURVERWIEZING__":"staticredirect","__STATISCHEDOORVERWIJZING__":"staticredirect","__STATISCHEREDIRECT__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nre":"nse","nse":"nse","urlkoderen":"urlencode","urlcoderen":"urlencode","codeerurl":"urlencode","urlencode":"urlencode","kleerste":"lcfirst","lcfirst":"lcfirst","gleerste":"ucfirst","hleerste":"ucfirst","ucfirst":"ucfirst","kl":"lc","lc":"lc","hl":"uc","uc":"uc","lokaleurl":"localurl","localurl":"localurl","lokaleurle":"localurle", +"localurle":"localurle","heleurl":"fullurl","volledigeurl":"fullurl","fullurl":"fullurl","heleurle":"fullurle","volledigeurle":"fullurle","fullurle":"fullurle","canoiekeurl":"canonicalurl","canonicalurl":"canonicalurl","canoniekeurle":"canonicalurle","canonicalurle":"canonicalurle","formatteernum":"formatnum","numformatteren":"formatnum","formatnum":"formatnum","grammatika":"grammar","grammatica":"grammar","grammar":"grammar","geslachte":"gender","geslacht":"gender","gender":"gender","meervoud":"plural","plural":"plural","bidi":"bidi","#taal":"language","#language":"language","linksopvullen":"padleft","padleft":"padleft","rechtsopvullen":"padright","padright":"padright","ankerkoderen":"anchorencode","ankercoderen":"anchorencode","codeeranker":"anchorencode","anchorencode":"anchorencode","bestaandspad":"filepath","bestandspad":"filepath","filepath":"filepath","paginaid":"pageid","pageid":"pageid","int":"int","#spesiaal":"special","#speciaal":"special","#special":"special","#speciaale": +"speciale","#speciale":"speciale","#etiket":"tag","#label":"tag","#tag":"tag","#daotumopmaak":"formatdate","#datumopmaak":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#aanroepen":"invoke","#invoke":"invoke","#related":"related","#as":"if","#als":"if","#if":"if","#asgelieke":"ifeq","#alsgelijk":"ifeq","#ifeq":"ifeq","#schakelen":"switch","#switch":"switch","#asbesteet":"ifexist","#alsbestaat":"ifexist","#ifexist":"ifexist","#asexpressie":"ifexpr","#alsexpressie":"ifexpr","#ifexpr":"ifexpr","#asfout":"iferror","#alsfout":"iferror","#iferror":"iferror","#tied":"time","#tijd":"time","#time":"time","#tiedl":"timel","#tijdl":"timel","#timel":"timel","#expressie":"expr","#expr":"expr","#relatiefnaorabseluut":"rel2abs","#relatiefnaarabsoluut":"rel2abs","#rel2abs":"rel2abs","#paginanaamdelen":"titleparts","#titleparts":"titleparts","#kategorieboom":"categorytree","#categorieboom":"categorytree", +"#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","geenexternetaalkoppelingen":"noexternallanglinks","geenexternetaalverwijzingen":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschap":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpad":"articlepath","articlepath":"articlepath","server":"server","servernaam":"servername","servername":"servername","skriptpad":"scriptpath","scriptpad":"scriptpath","scriptpath":"scriptpath","stielpad":"stylepath","stijlpad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ANTALINGROEP":"numberingroup","AANTALINGROEP":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","STANDARDSORTERING":"defaultsort","STANDAARDSORTERING":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","ZIEJENINKATEGORIE":"pagesincategory","PAGINASINCATEGORIE":"pagesincategory","PAGINASINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ZIEDGROOTTE":"pagesize","PAGINAGROOTTE":"pagesize","PAGESIZE":"pagesize","BEVEILIGINGSNIVO":"protectionlevel","BEVEILIGINGSNIVEAU":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ZIEDNAAM":"pagename","PAGINANAAM":"pagename","PAGENAME":"pagename","ZIEDNAAME":"pagenamee","PAGINANAAME":"pagenamee","PAGENAMEE":"pagenamee","HELEZIEDNAAM":"fullpagename","VOLLEDIGEPAGINANAAM":"fullpagename","FULLPAGENAME":"fullpagename","HELEZIEDNAAME":"fullpagenamee","VOLLEDIGEPAGINANAAME":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","DEELZIEDNAAM":"subpagename","DEELPAGINANAAM":"subpagename","SUBPAGENAME":"subpagename","DEELZIEDNAAME":"subpagenamee","DEELPAGINANAAME":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTZIEDNAAM":"rootpagename", +"ROOTPAGINANAAM":"rootpagename","ROOTPAGENAME":"rootpagename","ROOTZIEDNAAME":"rootpagenamee","ROOTPAGINANAAME":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","BAOSISPAGINANAAM":"basepagename","BASISPAGINANAAM":"basepagename","BASEPAGENAME":"basepagename","BAOSISPAGINANAAME":"basepagenamee","BASISPAGINANAAME":"basepagenamee","BASEPAGENAMEE":"basepagenamee","OVERLEGPAGINANAAM":"talkpagename","TALKPAGENAME":"talkpagename","OVERLEGPAGINANAAME":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","ONDERWARPZIEDNAAM":"subjectpagename","ARTIKELZIEDNAAM":"subjectpagename","ONDERWERPPAGINANAAM":"subjectpagename","ARTIKELPAGINANAAM":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ONDERWARZIEDNAAME":"subjectpagenamee","ARTIKELZIEDNAAME":"subjectpagenamee","ONDERWERPPAGINANAAME":"subjectpagenamee","ARTIKELPAGINANAAME":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","VERSIEID":"revisionid","REVISIONID": +"revisionid","VERSIEDAG":"revisionday","REVISIONDAY":"revisionday","VERSIEDAG2":"revisionday2","REVISIONDAY2":"revisionday2","VERSIEMAOND":"revisionmonth","VERSIEMAAND":"revisionmonth","REVISIONMONTH":"revisionmonth","VERSIEMAOND1":"revisionmonth1","VERSIEMAAND1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","VERSIEJAOR":"revisionyear","VERSIEJAAR":"revisionyear","REVISIONYEAR":"revisionyear","VERSIETIEDSTEMPEL":"revisiontimestamp","VERSIETIJD":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","VERSIEGEBRUKER":"revisionuser","VERSIEGEBRUIKER":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAAMRUUMTE":"namespace","NAAMRUIMTE":"namespace","NAMESPACE":"namespace","NAAMRUUMTEE":"namespacee","NAAMRUIMTEE":"namespacee","NAMESPACEE":"namespacee","NAAMRUUMTENUMMER":"namespacenumber","NAAMRUIMTENUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","OVERLEGRUUMTE":"talkspace","OVERLEGRUIMTE":"talkspace","TALKSPACE":"talkspace", +"OVERLEGRUUMTEE":"talkspacee","OVERLEGRUIMTEE":"talkspacee","TALKSPACEE":"talkspacee","ONDERWARPRUUMTE":"subjectspace","ARTIKELRUUMTE":"subjectspace","ONDERWERPRUIMTE":"subjectspace","ARTIKELRUIMTE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ONDERWARPRUUMTEE":"subjectspacee","ARTIKELRUUMTEE":"subjectspacee","ONDERWERPRUIMTEE":"subjectspacee","ARTIKELRUIMTEE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ANTALARTIKELS":"numberofarticles","AANTALARTIKELEN":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ANTALBESTANDEN":"numberoffiles","AANTALBESTANDEN":"numberoffiles","NUMBEROFFILES":"numberoffiles","ANTALGEBRUKERS":"numberofusers","AANTALGEBRUIKERS":"numberofusers","NUMBEROFUSERS":"numberofusers","ANTALAKTIEVEGEBRUKERS":"numberofactiveusers","AANTALACTIEVEGEBRUIKERS":"numberofactiveusers","ACTIEVEGEBRUIKERS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ANTALZIEJEN":"numberofpages", +"AANTALPAGINAS":"numberofpages","AANTALPAGINA'S":"numberofpages","AANTALPAGINA’S":"numberofpages","NUMBEROFPAGES":"numberofpages","ANTALBEHEERDERS":"numberofadmins","AANTALBEHEERDERS":"numberofadmins","AANTALADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ANTALBEWARKINGEN":"numberofedits","AANTALBEWERKINGEN":"numberofedits","NUMBEROFEDITS":"numberofedits","TEUNTITEL":"displaytitle","WEERGEGEVENTITEL":"displaytitle","TOONTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","DISSEMAOND":"currentmonth","DISSEMAOND2":"currentmonth","HUIDIGEMAAND":"currentmonth","HUIDIGEMAAND2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","DISSEMAOND1":"currentmonth1","HUIDIGEMAAND1":"currentmonth1","CURRENTMONTH1":"currentmonth1","DISSEMAONDNAAM":"currentmonthname","HUIDIGEMAANDNAAM":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","DISSEMAONDGEN":"currentmonthnamegen","HUIDIGEMAANDGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","DISSEMAONDAOFK":"currentmonthabbrev","HUIDIGEMAANDAFK":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DISSEDAG":"currentday","HUIDIGEDAG":"currentday","CURRENTDAY":"currentday","DISSEDAG2":"currentday2","HUIDIGEDAG2":"currentday2","CURRENTDAY2":"currentday2","DISSEDAGNAAM":"currentdayname","HUIDIGEDAGNAAM":"currentdayname","CURRENTDAYNAME":"currentdayname","DITJAOR":"currentyear","HUIDIGJAAR":"currentyear","CURRENTYEAR":"currentyear","DISSETIED":"currenttime","HUIDIGETIJD":"currenttime","CURRENTTIME":"currenttime","DITURE":"currenthour","HUIDIGUUR":"currenthour","CURRENTHOUR":"currenthour","LOKALEMAOND":"localmonth","PLAATSELIJKEMAAND":"localmonth","LOKALEMAAND":"localmonth","LOKALEMAAND2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALEMAOND1":"localmonth1","LOKALEMAAND1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALEMAONDNAAM":"localmonthname","PLAATSELIJKEMAANDNAAM":"localmonthname","LOKALEMAANDNAAM": +"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALEMAONDNAAMGEN":"localmonthnamegen","PLAATSELIJKEMAANDNAAMGEN":"localmonthnamegen","LOKALEMAANDNAAMGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALEMAONDAOFK":"localmonthabbrev","PLAATSELIJKEMAANDAFK":"localmonthabbrev","LOKALEMAANDAFK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALEDAG":"localday","PLAATSELIJKEDAG":"localday","LOCALDAY":"localday","LOKALEDAG2":"localday2","PLAATSELIJKEDAG2":"localday2","LOCALDAY2":"localday2","LOKALEDAGNAAM":"localdayname","PLAATSELIJKEDAGNAAM":"localdayname","LOCALDAYNAME":"localdayname","LOKAALJAOR":"localyear","PLAATSELIJKJAAR":"localyear","LOKAALJAAR":"localyear","LOCALYEAR":"localyear","LOKALETIED":"localtime","PLAATSELIJKETIJD":"localtime","LOKALETIJD":"localtime","LOCALTIME":"localtime","LOKAALURE":"localhour","PLAATSELIJKUUR":"localhour","LOKAALUUR":"localhour","LOCALHOUR":"localhour","WEBSTEENAAM":"sitename","SITENAAM":"sitename","SITENAME": +"sitename","DISSEWEKE":"currentweek","HUIDIGEWEEK":"currentweek","CURRENTWEEK":"currentweek","DISSEDVDW":"currentdow","HUIDIGEDVDW":"currentdow","CURRENTDOW":"currentdow","LOKALEWEKE":"localweek","PLAATSELIJKEWEEK":"localweek","LOKALEWEEK":"localweek","LOCALWEEK":"localweek","LOKALEDVDW":"localdow","PLAATSELIJKEDVDW":"localdow","LOCALDOW":"localdow","VERSIEGROOTTE":"revisionsize","REVISIONSIZE":"revisionsize","DISSEVERSIE":"currentversion","HUIDIGEVERSIE":"currentversion","CURRENTVERSION":"currentversion","DISSETIEDSTEMPEL":"currenttimestamp","HUIDIGETIJDSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALETIEDSTEMPEL":"localtimestamp","PLAATSELIJKETIJDSTEMPEL":"localtimestamp","LOKALETIJDSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","RICHTINGMARKERING":"directionmark","RICHTINGSMARKERING":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHOUDSTAAL":"contentlanguage","INHOUDTAAL":"contentlanguage","CONTENTLANGUAGE": +"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zäöüïëéèà]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nds.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nds.json new file mode 100644 index 0000000..0dfcfc5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nds.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__keeninholtverteken__":"notoc","__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__wiesinholtverteken__":"forcetoc","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__":"forcetoc", +"__inholtverteken__":"toc","__inhaltsverzeichnis__":"toc","__toc__":"toc","__keenännernlink__":"noeditsection","__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__": +"noindex","__NOINDEX__":"noindex","__PERMANENTE_WEITERLEITUNG__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","steedurl":"localurl","lokale_url":"localurl","localurl":"localurl","steedurle":"localurle","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zahlenformat":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#sprache":"language" +,"#language":"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright":"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#kategorienbaum":"categorytree","#kategoriebaum":"categorytree","#categorytree":"categorytree","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs", +"#titleparts":"titleparts","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx","#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTIVE_BENUTZER":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ARTIKELTALL":"numberofarticles", +"ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DATEIANZAHL":"numberoffiles","NUMBEROFFILES":"numberoffiles","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE":"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR": +"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","SIETNAAM":"pagename","SEITENNAME":"pagename","PAGENAME":"pagename","SIETNAAME":"pagenamee","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","DISKUSSIONSSEITE": +"talkpagename","DISK":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","HAUPTSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR": +"revisionyear","VERSIONSJAHR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","NAAMRUUM":"namespace","NAMENSRAUM":"namespace","NAMESPACE":"namespace","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","AKTMAAND":"currentmonth","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","AKTMAANDNAAM":"currentmonthname","JETZIGER_MONATSNAME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","AKTMAANDNAAMGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen", +"CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","AKTDAG":"currentday","JETZIGER_KALENDERTAG":"currentday","JETZIGER_TAG":"currentday","CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2","JETZIGER_TAG_2":"currentday2","CURRENTDAY2":"currentday2","AKTDAGNAAM":"currentdayname","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","AKTJOHR":"currentyear","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","AKTTIED":"currenttime","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV":"localmonthnamegen","LOKALER_MONATSNAME_GEN": +"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2","LOKALER_TAG_2":"localday2","LOCALDAY2":"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME":"localdayname","LOKALES_JAHR":"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","STEEDNAAM":"sitename","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize","JETZIGE_VERSION": +"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHALTSSPRACHE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöüßa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ne.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ne.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ne.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-new.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-new.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-new.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ng.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ng.json new file mode 100644 index 0000000..195d9f8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ng.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry" +,"PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE" +:"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname", +"LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nia.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nia.json new file mode 100644 index 0000000..04dbf3d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nia.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa": +"language","#bhs":"language","#language":"language","isikiri":"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1":"timel","#timel":"timel","#hitung":"expr", +"#expr":"expr","#rel2abs":"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","HALAMANDIKATEGORI":"pagesincategory","HALDIKAT": +"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee", +"NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE":"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid","HARIREVISI":"revisionday","HAREV":"revisionday" +,"REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace","RUANGNAMAE":"namespacee","RUNAME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace","RUTAMA":"subjectspace","RUTIKEL":"subjectspace", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","JUMLAHARTIKEL":"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits","NUMBEROFEDITS":"numberofedits","JUDULTAMPILAN":"displaytitle" +,"JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME":"currentdayname","TAHUNKINI":"currentyear","TAKIN":"currentyear", +"CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN":"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname","LOCALDAYNAME":"localdayname","TAHUNLOKAL":"localyear","TALOK":"localyear", +"LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK":"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","BAHASAISI":"contentlanguage", +"BHSISI":"contentlanguage","BASI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nl.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nl.json new file mode 100644 index 0000000..1f11fdf --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nl.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__geeninhoud__":"notoc","__notoc__":"notoc","__geen_galerij__":"nogallery","__nogallery__":"nogallery","__inhoud_dwingen__":"forcetoc","__forceerinhoud__":"forcetoc","__forcetoc__":"forcetoc","__inhoud__":"toc","__toc__":"toc","__nietbewerkbaresectie__":"noeditsection","__noeditsection__":"noeditsection", +"__geenpaginanaamconversie__":"notitleconvert","__geentitelconversie__":"notitleconvert","__geentc__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__geeninhoudconversie__":"nocontentconvert","__geenic__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NIEUWESECTIELINK__":"newsectionlink","__NIEUWESECTIEKOPPELING__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__GEENNIEUWKOPJEKOPPELING__":"nonewsectionlink","__GEENNIEUWESECTIELINK__":"nonewsectionlink","__GEENNIEUWKOPJEVERWIJZING__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERBORGENCAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__GEENINDEX__":"noindex","__NOINDEX__":"noindex","__STATISCHEDOORVERWIJZING__":"staticredirect","__STATISCHEREDIRECT__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__": +"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nre":"nse","nse":"nse","urlcoderen":"urlencode","codeerurl":"urlencode","urlencode":"urlencode","kleerste":"lcfirst","lcfirst":"lcfirst","gleerste":"ucfirst","hleerste":"ucfirst","ucfirst":"ucfirst","kl":"lc","lc":"lc","hl":"uc","uc":"uc","lokaleurl":"localurl","localurl":"localurl","lokaleurle":"localurle","localurle":"localurle","volledigeurl":"fullurl","fullurl":"fullurl","volledigeurle":"fullurle","fullurle":"fullurle","canoiekeurl":"canonicalurl","canonicalurl":"canonicalurl","canoniekeurle":"canonicalurle","canonicalurle":"canonicalurle","formatteernum":"formatnum","numformatteren":"formatnum","formatnum":"formatnum","grammatica":"grammar","grammar":"grammar","geslacht":"gender","gender":"gender","meervoud":"plural","plural":"plural","bidi":"bidi","#taal":"language","#language":"language","linksopvullen":"padleft","padleft":"padleft","rechtsopvullen":"padright", +"padright":"padright","ankercoderen":"anchorencode","codeeranker":"anchorencode","anchorencode":"anchorencode","bestandspad":"filepath","filepath":"filepath","paginaid":"pageid","pageid":"pageid","int":"int","#speciaal":"special","#special":"special","#speciaale":"speciale","#speciale":"speciale","#label":"tag","#tag":"tag","#datumopmaak":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#aanroepen":"invoke","#invoke":"invoke","#related":"related","#als":"if","#if":"if","#alsgelijk":"ifeq","#ifeq":"ifeq","#schakelen":"switch","#switch":"switch","#alsbestaat":"ifexist","#ifexist":"ifexist","#alsexpressie":"ifexpr","#ifexpr":"ifexpr","#alsfout":"iferror","#iferror":"iferror","#tijd":"time","#time":"time","#tijdl":"timel","#timel":"timel","#expressie":"expr","#expr":"expr","#relatiefnaarabsoluut":"rel2abs","#rel2abs":"rel2abs","#paginanaamdelen":"titleparts","#titleparts":"titleparts","#categorieboom": +"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","geenexternetaalkoppelingen":"noexternallanglinks","geenexternetaalverwijzingen":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschap":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpad":"articlepath","articlepath":"articlepath","server":"server","servernaam":"servername","servername":"servername","scriptpad":"scriptpath","scriptpath":"scriptpath","stijlpad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"AANTALINGROEP":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","STANDAARDSORTERING":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINASINCATEGORIE":"pagesincategory","PAGINASINCAT": +"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGINAGROOTTE":"pagesize","PAGESIZE":"pagesize","BEVEILIGINGSNIVEAU":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGINANAAM":"pagename","PAGENAME":"pagename","PAGINANAAME":"pagenamee","PAGENAMEE":"pagenamee","VOLLEDIGEPAGINANAAM":"fullpagename","FULLPAGENAME":"fullpagename","VOLLEDIGEPAGINANAAME":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","DEELPAGINANAAM":"subpagename","SUBPAGENAME":"subpagename","DEELPAGINANAAME":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGINANAAM":"rootpagename","ROOTPAGENAME":"rootpagename","ROOTPAGINANAAME":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","BASISPAGINANAAM":"basepagename","BASEPAGENAME":"basepagename","BASISPAGINANAAME":"basepagenamee","BASEPAGENAMEE":"basepagenamee","OVERLEGPAGINANAAM":"talkpagename","TALKPAGENAME":"talkpagename","OVERLEGPAGINANAAME":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"ONDERWERPPAGINANAAM":"subjectpagename","ARTIKELPAGINANAAM":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ONDERWERPPAGINANAAME":"subjectpagenamee","ARTIKELPAGINANAAME":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","VERSIEID":"revisionid","REVISIONID":"revisionid","VERSIEDAG":"revisionday","REVISIONDAY":"revisionday","VERSIEDAG2":"revisionday2","REVISIONDAY2":"revisionday2","VERSIEMAAND":"revisionmonth","REVISIONMONTH":"revisionmonth","VERSIEMAAND1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","VERSIEJAAR":"revisionyear","REVISIONYEAR":"revisionyear","VERSIETIJD":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","VERSIEGEBRUIKER":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAAMRUIMTE":"namespace","NAMESPACE":"namespace","NAAMRUIMTEE":"namespacee","NAMESPACEE":"namespacee","NAAMRUIMTENUMMER":"namespacenumber","NAMESPACENUMBER": +"namespacenumber","OVERLEGRUIMTE":"talkspace","TALKSPACE":"talkspace","OVERLEGRUIMTEE":"talkspacee","TALKSPACEE":"talkspacee","ONDERWERPRUIMTE":"subjectspace","ARTIKELRUIMTE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ONDERWERPRUIMTEE":"subjectspacee","ARTIKELRUIMTEE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","AANTALARTIKELEN":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","AANTALBESTANDEN":"numberoffiles","NUMBEROFFILES":"numberoffiles","AANTALGEBRUIKERS":"numberofusers","NUMBEROFUSERS":"numberofusers","AANTALACTIEVEGEBRUIKERS":"numberofactiveusers","ACTIEVEGEBRUIKERS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","AANTALPAGINAS":"numberofpages","AANTALPAGINA'S":"numberofpages","AANTALPAGINA’S":"numberofpages","NUMBEROFPAGES":"numberofpages","AANTALBEHEERDERS":"numberofadmins","AANTALADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","AANTALBEWERKINGEN":"numberofedits", +"NUMBEROFEDITS":"numberofedits","WEERGEGEVENTITEL":"displaytitle","TOONTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","HUIDIGEMAAND":"currentmonth","HUIDIGEMAAND2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","HUIDIGEMAAND1":"currentmonth1","CURRENTMONTH1":"currentmonth1","HUIDIGEMAANDNAAM":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","HUIDIGEMAANDGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","HUIDIGEMAANDAFK":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HUIDIGEDAG":"currentday","CURRENTDAY":"currentday","HUIDIGEDAG2":"currentday2","CURRENTDAY2":"currentday2","HUIDIGEDAGNAAM":"currentdayname","CURRENTDAYNAME":"currentdayname","HUIDIGJAAR":"currentyear","CURRENTYEAR":"currentyear","HUIDIGETIJD":"currenttime","CURRENTTIME":"currenttime","HUIDIGUUR":"currenthour","CURRENTHOUR":"currenthour","PLAATSELIJKEMAAND":"localmonth","LOKALEMAAND":"localmonth","LOKALEMAAND2":"localmonth", +"LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALEMAAND1":"localmonth1","LOCALMONTH1":"localmonth1","PLAATSELIJKEMAANDNAAM":"localmonthname","LOKALEMAANDNAAM":"localmonthname","LOCALMONTHNAME":"localmonthname","PLAATSELIJKEMAANDNAAMGEN":"localmonthnamegen","LOKALEMAANDNAAMGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","PLAATSELIJKEMAANDAFK":"localmonthabbrev","LOKALEMAANDAFK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","PLAATSELIJKEDAG":"localday","LOKALEDAG":"localday","LOCALDAY":"localday","PLAATSELIJKEDAG2":"localday2","LOKALEDAG2":"localday2","LOCALDAY2":"localday2","PLAATSELIJKEDAGNAAM":"localdayname","LOKALEDAGNAAM":"localdayname","LOCALDAYNAME":"localdayname","PLAATSELIJKJAAR":"localyear","LOKAALJAAR":"localyear","LOCALYEAR":"localyear","PLAATSELIJKETIJD":"localtime","LOKALETIJD":"localtime","LOCALTIME":"localtime","PLAATSELIJKUUR":"localhour","LOKAALUUR":"localhour","LOCALHOUR":"localhour","SITENAAM":"sitename","SITENAME":"sitename", +"HUIDIGEWEEK":"currentweek","CURRENTWEEK":"currentweek","HUIDIGEDVDW":"currentdow","CURRENTDOW":"currentdow","PLAATSELIJKEWEEK":"localweek","LOKALEWEEK":"localweek","LOCALWEEK":"localweek","PLAATSELIJKEDVDW":"localdow","LOKALEDVDW":"localdow","LOCALDOW":"localdow","VERSIEGROOTTE":"revisionsize","REVISIONSIZE":"revisionsize","HUIDIGEVERSIE":"currentversion","CURRENTVERSION":"currentversion","HUIDIGETIJDSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","PLAATSELIJKETIJDSTEMPEL":"localtimestamp","LOKALETIJDSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","RICHTINGMARKERING":"directionmark","RICHTINGSMARKERING":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHOUDSTAAL":"contentlanguage","INHOUDTAAL":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zäöüïëéèà]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nn.json new file mode 100644 index 0000000..1ce6474 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nn.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__ingainnhaldsliste__":"notoc","__ingeninnholdsliste__":"notoc","__notoc__":"notoc","__ingeninnholdsfortegnelse__":"notoc","__inkjegalleri__":"nogallery","__nogallery__":"nogallery","__intetgalleri__":"nogallery","__alltidinnhaldsliste__":"forcetoc","__alltidinnholdsliste__":"forcetoc","__forcetoc__":"forcetoc", +"__tvinginnholdsfortegnelse__":"forcetoc","__innhaldsliste__":"toc","__innholdsliste__":"toc","__toc__":"toc","__innholdsfortegnelse__":"toc","__ingabolkendring__":"noeditsection","__ingabolkredigering__":"noeditsection","__ingendelendring__":"noeditsection","__noeditsection__":"noeditsection","__ingenseksjonsredigering__":"noeditsection","__ingentittelkonvertering__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__ingeninnholdskonvertering__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NYSEKSJONSLENKE__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__INGENNYSEKSJONSLENKE__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__GØYMDKAT__":"hiddencat","__LØYNDKAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__SKJULTKATEGORI__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKSER__":"index","__INDEX__":"index","__INGENINDEKSERING__":"noindex","__NOINDEX__" +:"noindex","__STATISTOMDIRIGERING__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__PEKER__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","lfyrst":"lcfirst","lførst":"lcfirst","lcfirst":"lcfirst","sfyrst":"ucfirst","sførst":"ucfirst","ucfirst":"ucfirst","små":"lc","lc":"lc","store":"uc","uc":"uc","lokallenkje":"localurl","lokallenke":"localurl","localurl":"localurl","lokalurl":"localurl","lokallenkjee":"localurle","lokallenkee":"localurle","localurle":"localurle","lokalurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formattal":"formatnum","formatnum":"formatnum","grammatikk":"grammar","grammar":"grammar","kjønn":"gender","gender":"gender","fleirtal":"plural","plural":"plural","flertall":"plural","bidi":"bidi","#språk":"language", +"#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filstig":"filepath","filepath":"filepath","filsti":"filepath","sideid":"pageid","pageid":"pageid","int":"int","#spesial":"special","#special":"special","#speciale":"speciale","#merke":"tag","#tag":"tag","#datoformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#mål":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#køyr":"invoke","#kjør":"invoke","#invoke":"invoke","#related":"related","#om":"if","#if":"if","#omlik":"ifeq","#ifeq":"ifeq","#byt":"switch","#switch":"switch","#omfinst":"ifexist","#ifexist":"ifexist","#omuttrykk":"ifexpr","#omuttr":"ifexpr","#ifexpr":"ifexpr","#omfeil":"iferror","#iferror":"iferror","#tid":"time","#time":"time","#tidl":"timel","#timel":"timel","#uttrykk":"expr","#uttr":"expr","#expr":"expr","#reltilabs":"rel2abs","#rel2abs":"rel2abs","#titteldelar":"titleparts","#titleparts":"titleparts","#kategoritre": +"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","ingenspråklenkjerutanfrå":"noexternallanglinks","ingenspråklenkerutenfra":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenskap":"property","#egenskap":"property","#property":"property","#påstandar":"statements","#påstander":"statements","#statements":"statements","#kommaadskiltliste":"commaSeparatedList","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikkelstig":"articlepath","articlepath":"articlepath","artikkelsti":"articlepath","tenar":"server","tjener":"server","server":"server","tenarnamn":"servername","tjenernavn":"servername","servername":"servername","skriptstig":"scriptpath","skriptsti":"scriptpath","scriptpath":"scriptpath","stilsti":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMMERIGRUPPE":"numberingroup","NUMBERINGROUP": +"numberingroup","NUMINGROUP":"numberingroup","STANDARDSORTERING":"defaultsort","SORTERINGSNYKEL":"defaultsort","SORTERINGSNØKKEL":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SIDERIKAT":"pagesincategory","SIDERIKATEGORI":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SIDESTORLEIK":"pagesize","PAGESIZE":"pagesize","SIDESTØRRELSE":"pagesize","VERNENIVÅ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","BESKYTTELSESNIVÅ":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SIDENAMN":"pagename","SIDENAVN":"pagename","PAGENAME":"pagename","SIDENAMNE":"pagenamee","SIDENAVNE":"pagenamee","PAGENAMEE":"pagenamee","FULLTSIDENAMN":"fullpagename","FULLPAGENAME":"fullpagename","FULLTSIDENAVN":"fullpagename","FULLTSIDENAVNE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNDERSIDENAMN":"subpagename","SUBPAGENAME":"subpagename","UNDERSIDENAVN":"subpagename","UNDERSIDENAVNE": +"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","HOVUDSIDENAMN":"basepagename","BASEPAGENAME":"basepagename","GRUNNSIDENAVN":"basepagename","GRUNNSIDENAVNE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSJONSSIDENAMN":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSJONSSIDENAVN":"talkpagename","DISKUSJONSSIDENAVNE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJEKTSIDENAVN":"subjectpagename","ARTIKKELSIDENAVN":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJEKTSIDENAVNE":"subjectpagenamee","ARTIKKELSIDENAVNE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","VERSJONSID":"revisionid","REVISIONID":"revisionid","REVISJONSID":"revisionid","VERSJONSDAG":"revisionday","REVISIONDAY":"revisionday","REVISJONSDAG":"revisionday","VERSJONSDAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISJONSDAG2":"revisionday2", +"VERSJONSMÅNAD":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISJONSMÅNED":"revisionmonth","VERSJONSMÅNAD1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISJONSMÅNED1":"revisionmonth1","VERSJONSÅR":"revisionyear","REVISIONYEAR":"revisionyear","REVISJONSÅR":"revisionyear","VERSJONSTIDSTEMPEL":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISJONSTIDSSTEMPEL":"revisiontimestamp","VERSJONSBRUKAR":"revisionuser","REVISIONUSER":"revisionuser","REVISJONSBRUKER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMNEROM":"namespace","NAVNEROM":"namespace","NAMESPACE":"namespace","NAVNEROME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","DISKUSJONSROM":"talkspace","TALKSPACE":"talkspace","DISKUSJONSROME":"talkspacee","TALKSPACEE":"talkspacee","SUBJEKTROM":"subjectspace","ARTIKKELROM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJEKTROME":"subjectspacee","ARTIKKELROME":"subjectspacee", +"SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","INNHALDSSIDETAL":"numberofarticles","INNHOLDSSIDETALL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ANTALLARTIKLER":"numberofarticles","FILTAL":"numberoffiles","NUMBEROFFILES":"numberoffiles","ANTALLFILER":"numberoffiles","BRUKARTAL":"numberofusers","NUMBEROFUSERS":"numberofusers","ANTALLBRUKERE":"numberofusers","AKTIVEBRUKARAR":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ANTALLAKTIVEBRUKERE":"numberofactiveusers","SIDETAL":"numberofpages","NUMBEROFPAGES":"numberofpages","ANTALLSIDER":"numberofpages","ADMINTAL":"numberofadmins","ADMINISTRATORTAL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ANTALLADMINISTRATORER":"numberofadmins","ENDRINGSTAL":"numberofedits","NUMBEROFEDITS":"numberofedits","ANTALLREDIGERINGER":"numberofedits","VISTITTEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MÅNADNO":"currentmonth","MÅNEDNÅ":"currentmonth","CURRENTMONTH":"currentmonth" +,"CURRENTMONTH2":"currentmonth","NÅVÆRENDEMÅNED":"currentmonth","NÅVÆRENDEMÅNED2":"currentmonth","NÅVÆRENDEMÅNED1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MÅNADNONAMN":"currentmonthname","MÅNEDNÅNAVN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NÅVÆRENDEMÅNEDSNAVN":"currentmonthname","NÅVÆRENDEMÅNEDSNAVNGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MÅNADNOKORT":"currentmonthabbrev","MÅNEDNÅKORT":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","NÅVÆRENDEMÅNEDSNAVNKORT":"currentmonthabbrev","DAGNO":"currentday","DAGNÅ":"currentday","CURRENTDAY":"currentday","NÅVÆRENDEDAG":"currentday","DAGNO2":"currentday2","DAGNÅ2":"currentday2","CURRENTDAY2":"currentday2","NÅVÆRENDEDAG2":"currentday2","DAGNONAMN":"currentdayname","DAGNÅNAVN":"currentdayname","CURRENTDAYNAME":"currentdayname","NÅVÆRENDEDAGSNAVN":"currentdayname","ÅRNO":"currentyear","ÅRNÅ":"currentyear","CURRENTYEAR":"currentyear", +"NÅVÆRENDEÅR":"currentyear","TIDNO":"currenttime","TIDNÅ":"currenttime","CURRENTTIME":"currenttime","NÅVÆRENDETID":"currenttime","TIMENO":"currenthour","CURRENTHOUR":"currenthour","NÅVÆRENDETIME":"currenthour","LOKALMÅNED":"localmonth","LOKALMÅNED2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALMÅNED1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALMÅNEDSNAVN":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALMÅNEDSNAVNGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALMÅNEDSNAVNKORT":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALDAG":"localday","LOCALDAY":"localday","LOKALDAG2":"localday2","LOCALDAY2":"localday2","LOKALDAGSNAVN":"localdayname","LOCALDAYNAME":"localdayname","LOKALTÅR":"localyear","LOCALYEAR":"localyear","LOKALTID":"localtime","LOCALTIME":"localtime","LOKALTIME":"localhour","LOCALHOUR":"localhour","NETTSTADNAMN":"sitename","SITENAME":"sitename","VEKENRNO":"currentweek","UKENRNÅ": +"currentweek","CURRENTWEEK":"currentweek","NÅVÆRENDEUKE":"currentweek","VEKEDAGNRNO":"currentdow","UKEDAGNRNÅ":"currentdow","CURRENTDOW":"currentdow","NÅVÆRENDEUKEDAG":"currentdow","LOKALVEKE":"localweek","LOCALWEEK":"localweek","LOKALUKE":"localweek","LOKALUKEDAG":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSJONNO":"currentversion","CURRENTVERSION":"currentversion","NÅVÆRENDEVERSJON":"currentversion","NÅVÆRENDETIDSSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALTTIDSSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INNHALDSSPRÅK":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","INNHOLDSSPRÅK":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([æøåa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-no.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-no.json new file mode 100644 index 0000000..cff83aa --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-no.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__ingeninnholdsfortegnelse__":"notoc","__notoc__":"notoc","__ingainnhaldsliste__":"notoc","__ingeninnholdsliste__":"notoc","__intetgalleri__":"nogallery","__nogallery__":"nogallery","__inkjegalleri__":"nogallery","__tvinginnholdsfortegnelse__":"forcetoc","__forcetoc__":"forcetoc","__alltidinnhaldsliste__":"forcetoc", +"__alltidinnholdsliste__":"forcetoc","__innholdsfortegnelse__":"toc","__toc__":"toc","__innhaldsliste__":"toc","__innholdsliste__":"toc","__ingenseksjonsredigering__":"noeditsection","__noeditsection__":"noeditsection","__ingabolkendring__":"noeditsection","__ingabolkredigering__":"noeditsection","__ingendelendring__":"noeditsection","__ingentittelkonvertering__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__ingeninnholdskonvertering__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NYSEKSJONSLENKE__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__INGENNYSEKSJONSLENKE__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__SKJULTKATEGORI__":"hiddencat","__HIDDENCAT__":"hiddencat","__GØYMDKAT__":"hiddencat","__LØYNDKAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKSER__":"index","__INDEX__":"index","__INGENINDEKSERING__":"noindex","__NOINDEX__": +"noindex","__STATISTOMDIRIGERING__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__PEKER__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","lfyrst":"lcfirst","lførst":"lcfirst","lcfirst":"lcfirst","sfyrst":"ucfirst","sførst":"ucfirst","ucfirst":"ucfirst","små":"lc","lc":"lc","store":"uc","uc":"uc","lokalurl":"localurl","localurl":"localurl","lokallenkje":"localurl","lokallenke":"localurl","lokalurle":"localurle","localurle":"localurle","lokallenkjee":"localurle","lokallenkee":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formattal":"formatnum","formatnum":"formatnum","grammatikk":"grammar","grammar":"grammar","kjønn":"gender","gender":"gender","flertall":"plural","plural":"plural","fleirtal":"plural","bidi":"bidi","#språk":"language", +"#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filsti":"filepath","filepath":"filepath","filstig":"filepath","sideid":"pageid","pageid":"pageid","int":"int","#spesial":"special","#special":"special","#speciale":"speciale","#merke":"tag","#tag":"tag","#datoformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#mål":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#kjør":"invoke","#køyr":"invoke","#invoke":"invoke","#related":"related","#om":"if","#if":"if","#omlik":"ifeq","#ifeq":"ifeq","#byt":"switch","#switch":"switch","#omfinst":"ifexist","#ifexist":"ifexist","#omuttrykk":"ifexpr","#omuttr":"ifexpr","#ifexpr":"ifexpr","#omfeil":"iferror","#iferror":"iferror","#tid":"time","#time":"time","#tidl":"timel","#timel":"timel","#uttrykk":"expr","#uttr":"expr","#expr":"expr","#reltilabs":"rel2abs","#rel2abs":"rel2abs","#titteldelar":"titleparts","#titleparts":"titleparts","#kategoritre": +"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","ingenspråklenkerutenfra":"noexternallanglinks","ingenspråklenkjerutanfrå":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#egenskap":"property","#eigenskap":"property","#property":"property","#påstander":"statements","#påstandar":"statements","#statements":"statements","#kommaadskiltliste":"commaSeparatedList","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikkelsti":"articlepath","articlepath":"articlepath","artikkelstig":"articlepath","tjener":"server","server":"server","tenar":"server","tjenernavn":"servername","servername":"servername","tenarnamn":"servername","skriptsti":"scriptpath","scriptpath":"scriptpath","skriptstig":"scriptpath","stilsti":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMMERIGRUPPE":"numberingroup","NUMBERINGROUP": +"numberingroup","NUMINGROUP":"numberingroup","STANDARDSORTERING":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SORTERINGSNYKEL":"defaultsort","SORTERINGSNØKKEL":"defaultsort","SIDERIKATEGORI":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SIDERIKAT":"pagesincategory","SIDESTØRRELSE":"pagesize","PAGESIZE":"pagesize","SIDESTORLEIK":"pagesize","BESKYTTELSESNIVÅ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","VERNENIVÅ":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SIDENAVN":"pagename","PAGENAME":"pagename","SIDENAMN":"pagename","SIDENAVNE":"pagenamee","PAGENAMEE":"pagenamee","SIDENAMNE":"pagenamee","FULLTSIDENAVN":"fullpagename","FULLPAGENAME":"fullpagename","FULLTSIDENAMN":"fullpagename","FULLTSIDENAVNE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNDERSIDENAVN":"subpagename","SUBPAGENAME":"subpagename","UNDERSIDENAMN":"subpagename","UNDERSIDENAVNE": +"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","GRUNNSIDENAVN":"basepagename","BASEPAGENAME":"basepagename","HOVUDSIDENAMN":"basepagename","GRUNNSIDENAVNE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSJONSSIDENAVN":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSJONSSIDENAMN":"talkpagename","DISKUSJONSSIDENAVNE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJEKTSIDENAVN":"subjectpagename","ARTIKKELSIDENAVN":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJEKTSIDENAVNE":"subjectpagenamee","ARTIKKELSIDENAVNE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISJONSID":"revisionid","REVISIONID":"revisionid","VERSJONSID":"revisionid","REVISJONSDAG":"revisionday","REVISIONDAY":"revisionday","VERSJONSDAG":"revisionday","REVISJONSDAG2":"revisionday2","REVISIONDAY2":"revisionday2","VERSJONSDAG2":"revisionday2", +"REVISJONSMÅNED":"revisionmonth","REVISIONMONTH":"revisionmonth","VERSJONSMÅNAD":"revisionmonth","REVISJONSMÅNED1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","VERSJONSMÅNAD1":"revisionmonth1","REVISJONSÅR":"revisionyear","REVISIONYEAR":"revisionyear","VERSJONSÅR":"revisionyear","REVISJONSTIDSSTEMPEL":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","VERSJONSTIDSTEMPEL":"revisiontimestamp","REVISJONSBRUKER":"revisionuser","REVISIONUSER":"revisionuser","VERSJONSBRUKAR":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAVNEROM":"namespace","NAMESPACE":"namespace","NAMNEROM":"namespace","NAVNEROME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","DISKUSJONSROM":"talkspace","TALKSPACE":"talkspace","DISKUSJONSROME":"talkspacee","TALKSPACEE":"talkspacee","SUBJEKTROM":"subjectspace","ARTIKKELROM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJEKTROME":"subjectspacee","ARTIKKELROME":"subjectspacee", +"SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ANTALLARTIKLER":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","INNHALDSSIDETAL":"numberofarticles","INNHOLDSSIDETALL":"numberofarticles","ANTALLFILER":"numberoffiles","NUMBEROFFILES":"numberoffiles","FILTAL":"numberoffiles","ANTALLBRUKERE":"numberofusers","NUMBEROFUSERS":"numberofusers","BRUKARTAL":"numberofusers","ANTALLAKTIVEBRUKERE":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","AKTIVEBRUKARAR":"numberofactiveusers","ANTALLSIDER":"numberofpages","NUMBEROFPAGES":"numberofpages","SIDETAL":"numberofpages","ANTALLADMINISTRATORER":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ADMINTAL":"numberofadmins","ADMINISTRATORTAL":"numberofadmins","ANTALLREDIGERINGER":"numberofedits","NUMBEROFEDITS":"numberofedits","ENDRINGSTAL":"numberofedits","VISTITTEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","NÅVÆRENDEMÅNED":"currentmonth","NÅVÆRENDEMÅNED2":"currentmonth", +"CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MÅNADNO":"currentmonth","MÅNEDNÅ":"currentmonth","NÅVÆRENDEMÅNED1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NÅVÆRENDEMÅNEDSNAVN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MÅNADNONAMN":"currentmonthname","MÅNEDNÅNAVN":"currentmonthname","NÅVÆRENDEMÅNEDSNAVNGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NÅVÆRENDEMÅNEDSNAVNKORT":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","MÅNADNOKORT":"currentmonthabbrev","MÅNEDNÅKORT":"currentmonthabbrev","NÅVÆRENDEDAG":"currentday","CURRENTDAY":"currentday","DAGNO":"currentday","DAGNÅ":"currentday","NÅVÆRENDEDAG2":"currentday2","CURRENTDAY2":"currentday2","DAGNO2":"currentday2","DAGNÅ2":"currentday2","NÅVÆRENDEDAGSNAVN":"currentdayname","CURRENTDAYNAME":"currentdayname","DAGNONAMN":"currentdayname","DAGNÅNAVN":"currentdayname","NÅVÆRENDEÅR":"currentyear","CURRENTYEAR":"currentyear","ÅRNO": +"currentyear","ÅRNÅ":"currentyear","NÅVÆRENDETID":"currenttime","CURRENTTIME":"currenttime","TIDNO":"currenttime","TIDNÅ":"currenttime","NÅVÆRENDETIME":"currenthour","CURRENTHOUR":"currenthour","TIMENO":"currenthour","LOKALMÅNED":"localmonth","LOKALMÅNED2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALMÅNED1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALMÅNEDSNAVN":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALMÅNEDSNAVNGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALMÅNEDSNAVNKORT":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALDAG":"localday","LOCALDAY":"localday","LOKALDAG2":"localday2","LOCALDAY2":"localday2","LOKALDAGSNAVN":"localdayname","LOCALDAYNAME":"localdayname","LOKALTÅR":"localyear","LOCALYEAR":"localyear","LOKALTID":"localtime","LOCALTIME":"localtime","LOKALTIME":"localhour","LOCALHOUR":"localhour","NETTSTADNAMN":"sitename","SITENAME":"sitename","NÅVÆRENDEUKE":"currentweek", +"CURRENTWEEK":"currentweek","VEKENRNO":"currentweek","UKENRNÅ":"currentweek","NÅVÆRENDEUKEDAG":"currentdow","CURRENTDOW":"currentdow","VEKEDAGNRNO":"currentdow","UKEDAGNRNÅ":"currentdow","LOKALUKE":"localweek","LOCALWEEK":"localweek","LOKALVEKE":"localweek","LOKALUKEDAG":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","NÅVÆRENDEVERSJON":"currentversion","CURRENTVERSION":"currentversion","VERSJONNO":"currentversion","NÅVÆRENDETIDSSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALTTIDSSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INNHOLDSSPRÅK":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","INNHALDSSPRÅK":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([æøåa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nov.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nov.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nov.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nqo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nqo.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nqo.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nrm.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nrm.json new file mode 100644 index 0000000..30830cb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nrm.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode", +"initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int", +"#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property", +"#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGE":"pagename","PAGENAME":"pagename", +"NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid", +"REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee", +"ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL": +"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nso.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nso.json new file mode 100644 index 0000000..d07b2c7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nso.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","KGWEDI_BJALE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","LEINA_KGWEDI_BJALE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","LEHONO_LETSATSI":"currentday","CURRENTDAY":"currentday","LEHONO_LETSATSI2":"currentday2","CURRENTDAY2":"currentday2","LEHONO_LETSATSILEINA":"currentdayname","CURRENTDAYNAME":"currentdayname","NGWAGA_BJALE":"currentyear","CURRENTYEAR":"currentyear","NAKO_BJALE":"currenttime","CURRENTTIME":"currenttime","IRI_BJALE":"currenthour","CURRENTHOUR": +"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([A-Za-zŠÔÊšôê]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nv.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nv.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-nv.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ny.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ny.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ny.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-oc.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-oc.json new file mode 100644 index 0000000..29e1e34 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-oc.json @@ -0,0 +1,16 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__capdetaula__":"notoc","__pascapdesomari__":"notoc","__pascapdetdm__":"notoc","__notoc__":"notoc","__captaula__":"notoc","__notaula__":"notoc","__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__capdegalariá__":"nogallery","__capdegalaria__":"nogallery","__pascapdedegalariá__":"nogallery","__nogallery__":"nogallery", +"__capgaleria__":"nogallery","__nogaleria__":"nogallery","__aucunegalerie__":"nogallery","__forçartaula__":"forcetoc","__forçarsomari__":"forcetoc","__forçartdm__":"forcetoc","__forcetoc__":"forcetoc","__forçataula__":"forcetoc","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__taula__":"toc","__somari__":"toc","__tdm__":"toc","__toc__":"toc","__resum__":"toc","__sommaire__":"toc","__seccionnoneditabla__":"noeditsection","__noeditsection__":"noeditsection","__secciónoeditable__":"noeditsection","__seccionoeditable__":"noeditsection","__sectionnoneditable__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIGAMSECCIONNOVÈLA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__LIENNOUVELLESECTION__": +"newsectionlink","__PASCAPDELIGAMSECCIONNOVÈLA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__CATAMAGADA__":"hiddencat","__HIDDENCAT__":"hiddencat","__CATCACHEE__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__PASCAPDINDÈX__":"noindex","__NOINDEX__":"noindex","__CAPINDEX__":"noindex","__AUCUNINDEX__":"noindex","__REDIRECCIONESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__REDIRECCIÓESTATICA__":"staticredirect","__REDIRECCIOESTATICA__":"staticredirect","__REDIRECTIONSTATIQUE__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encòdaurl":"urlencode","urlencode":"urlencode","encodeurl":"urlencode","initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","ucfirst":"ucfirst","initcapit": +"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocala":"localurl","localurl":"localurl","urllocale":"localurl","urllocalax":"localurle","localurle":"localurle","urllocalex":"localurle","urlcompleta":"fullurl","fullurl":"fullurl","urlcomplete":"fullurl","urlcompletax":"fullurle","fullurle":"fullurle","urlcompletex":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","gramatica":"grammar","grammar":"grammar","grammaire":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#lenga":"language","#language":"language","#idioma":"language","#llengua":"language","#langue":"language","borratgeesquèrra":"padleft","padleft":"padleft","separacióesquerra":"padleft","separacioesquerra":"padleft","bourragegauche":"padleft","bourregauche":"padleft","borratgedrecha":"padright","padright":"padright", +"separaciódreta":"padright","separaciodreta":"padright","bourragedroite":"padright","bourredroite":"padright","encòdaancòra":"anchorencode","anchorencode":"anchorencode","encodeancre":"anchorencode","camin":"filepath","filepath":"filepath","camí":"filepath","cami":"filepath","chemin":"filepath","idpage":"pageid","pageid":"pageid","int":"int","#especial":"special","#special":"special","#spécial":"special","#spéciale":"speciale","#speciale":"speciale","#balisa":"tag","#tag":"tag","#etiqueta":"tag","#marcador":"tag","#balise":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel", +"#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","servidor":"server","server":"server","serveur":"server","nomservidor":"servername","servername":"servername","nomserveur":"servername","caminescript":"scriptpath","scriptpath":"scriptpath","cheminscript":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP": +"numberingroup","NUMINGROUP":"numberingroup","ORDENA":"defaultsort","CLAUDETRIADA":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","PAGINASDINSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PÀGINESENCATEGORIA":"pagesincategory","PAGINESENCATEGORIA":"pagesincategory","PAGINESENCAT":"pagesincategory","PAGESDANSCAT":"pagesincategory","TALHAPAGINA":"pagesize","PAGESIZE":"pagesize","MIDAPÀGINA":"pagesize","MIDAPAGINA":"pagesize","MIDADELAPLANA":"pagesize","TAILLEPAGE":"pagesize","NIVÈLDEPROTECCION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","NIVELLPROTECCIÓ":"protectionlevel","NIVELLPROTECCIO":"protectionlevel","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGINA":"pagename","PAGENAME":"pagename","NOMPÀGINA":"pagename","NOMDELAPLANA":"pagename","NOMPAGE":"pagename","NOMPAGINAX": +"pagenamee","PAGENAMEE":"pagenamee","NOMPAGEX":"pagenamee","NOMPAGINACOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGECOMPLET":"fullpagename","NOMPAGINACOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMPAGECOMPLETX":"fullpagenamee","NOMSOSPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGE":"subpagename","NOMSOSPAGINAX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMSOUSPAGEX":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASADEPAGINA":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGE":"basepagename","NOMBASADEPAGINAX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMBASEDEPAGEX":"basepagenamee","NOMPAGINADISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSION":"talkpagename","NOMPAGINADISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGEDISCUSSIONX":"talkpagenamee","NOMPAGINASUBJECTE":"subjectpagename" +,"NOMPAGINASUBJÈCTE":"subjectpagename","NOMPAGINAARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","NOMPAGINASUBJECTEX":"subjectpagenamee","NOMPAGINASUBJÈCTEX":"subjectpagenamee","NOMPAGINAARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","NUMÈROVERSION":"revisionid","REVISIONID":"revisionid","IDVERSION":"revisionid","DATAVERSION":"revisionday","REVISIONDAY":"revisionday","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","DATAVERSION2":"revisionday2","REVISIONDAY2":"revisionday2","JOUR2VERSION":"revisionday2","MESREVISION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNADAREVISION":"revisionyear","ANREVISION":"revisionyear", +"REVISIONYEAR":"revisionyear","ANNEEVERSION":"revisionyear","ORAREVISION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","INSTANTVERSION":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACINOMENATGE":"namespace","NAMESPACE":"namespace","ESPACENOMMAGE":"namespace","ESPACINOMENATGEX":"namespacee","NAMESPACEE":"namespacee","ESPACENOMMAGEX":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACIDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSION":"talkspace","ESPACIDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACEDISCUSSIONX":"talkspacee","ESPACISUBJECTE":"subjectspace","ESPACISUBJÈCTE":"subjectspace","ESPACIARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","ESPACISUBJECTEX":"subjectspacee","ESPACISUBJÈCTEX":"subjectspacee","ESPACIARTICLEX" +:"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ESPACESUJETX":"subjectspacee","ESPACEARTICLEX":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBRED'ARTICLES":"numberofarticles","NOMBREFICHIÈRS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREFITXERS":"numberoffiles","NOMBRED'ARXIUS":"numberoffiles","NOMBREFICHIERS":"numberoffiles","NOMBREUTILIZAIRES":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUSUARIS":"numberofusers","NOMBRED'USUARIS":"numberofusers","NOMBREUTILISATEURS":"numberofusers","NOMBREUTILIZAIRESACTIUS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NOMBREPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREEDICIONS":"numberofedits","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits", +"NOMBRED'EDICIONS":"numberofedits","AFICHARTÍTOL":"displaytitle","DISPLAYTITLE":"displaytitle","TÍTOL":"displaytitle","TITOL":"displaytitle","AFFICHERTITRE":"displaytitle","!":"!","=":"=","MESCORRENT":"currentmonth","MESACTUAL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMESCORRENT":"currentmonthname","NOMMESACTUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMMOISACTUEL":"currentmonthname","NOMGENMESCORRENT":"currentmonthnamegen","NOMGENMESACTUAL":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NOMGENMOISACTUEL":"currentmonthnamegen","ABREVMESCORRENT":"currentmonthabbrev","ABREVMESACTUAL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ABREVMOISACTUEL":"currentmonthabbrev","JORNCORRENT":"currentday","JORNACTUAL":"currentday","CURRENTDAY":"currentday","DIAACTUAL":"currentday", +"JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","JORNCORRENT2":"currentday2","JORNACTUAL2":"currentday2","CURRENTDAY2":"currentday2","DIAACTUAL2":"currentday2","JOUR2ACTUEL":"currentday2","NOMJORNCORRENT":"currentdayname","NOMJORNACTUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","NOMDIAACTUAL":"currentdayname","NOMJOURACTUEL":"currentdayname","ANNADACORRENTA":"currentyear","ANNADAACTUALA":"currentyear","CURRENTYEAR":"currentyear","ANYACTUAL":"currentyear","ANNEEACTUELLE":"currentyear","DATACORRENTA":"currenttime","DATAACTUALA":"currenttime","CURRENTTIME":"currenttime","HORARICTUAL":"currenttime","HORAIREACTUEL":"currenttime","ORACORRENTA":"currenthour","ORAACTUALA":"currenthour","CURRENTHOUR":"currenthour","HORAACTUAL":"currenthour","HEUREACTUELLE":"currenthour","MESLOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMESLOCAL":"localmonthname" +,"LOCALMONTHNAME":"localmonthname","NOMMOISLOCAL":"localmonthname","NOMGENMESLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NOMGENMOISLOCAL":"localmonthnamegen","ABREVMESLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ABREVMOISLOCAL":"localmonthabbrev","JORNLOCAL":"localday","LOCALDAY":"localday","DIALOCAL":"localday","JOURLOCAL":"localday","JOUR1LOCAL":"localday","JORNLOCAL2":"localday2","LOCALDAY2":"localday2","DIALOCAL2":"localday2","JOUR2LOCAL":"localday2","NOMJORNLOCAL":"localdayname","LOCALDAYNAME":"localdayname","NOMDIALOCAL":"localdayname","NOMJOURLOCAL":"localdayname","ANNADALOCALA":"localyear","LOCALYEAR":"localyear","ANYLOCAL":"localyear","ANNEELOCALE":"localyear","ORARILOCAL":"localtime","LOCALTIME":"localtime","HORARILOCAL":"localtime","HORAIRELOCAL":"localtime","ORALOCALA":"localhour","LOCALHOUR":"localhour","HORALOCAL":"localhour","HEURELOCALE":"localhour","NOMSIT":"sitename","NOMSITE_NOMSITI":"sitename","SITENAME":"sitename", +"NOMSITE":"sitename","SETMANACORRENTA":"currentweek","CURRENTWEEK":"currentweek","SEMAINEACTUELLE":"currentweek","JDSCORRENT":"currentdow","CURRENTDOW":"currentdow","JDSACTUEL":"currentdow","SETMANALOCALA":"localweek","LOCALWEEK":"localweek","SEMAINELOCALE":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUALA":"currentversion","CURRENTVERSION":"currentversion","VERSIÓACTUAL":"currentversion","VERSIOACTUAL":"currentversion","VERSIONACTUELLE":"currentversion","INSTANTACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTACTUEL":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARCADIRECCION":"directionmark","MARCADIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","LENGACONTENGUT":"contentlanguage","LENGCONTENGUT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG": +"contentlanguage","IDIOMACONTINGUT":"contentlanguage","LLENGUACONTINGUT":"contentlanguage","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôû]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-olo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-olo.json new file mode 100644 index 0000000..4e6895b --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-olo.json @@ -0,0 +1,9 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__eisisluett__":"notoc","__notoc__":"notoc","__nogallery__":"nogallery","__sisluettpakotus__":"forcetoc","__forcetoc__":"forcetoc","__sisällysluettelo__":"toc","__toc__":"toc","__eiosiomuokkausta__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert", +"__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__PIILOLUOKKA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__HAKUKONEKIELTO__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"na":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","paikallinenosoite":"localurl","localurl":"localurl","paikallinenosoitee":"localurle","localurle":"localurle","täysiosoite":"fullurl","fullurl":"fullurl","täysiosoitee":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","muotoileluku":"formatnum","formatnum":"formatnum","taivutus":"grammar","grammar":"grammar","sukupuoli": +"gender","gender":"gender","monikko":"plural","plural":"plural","bidi":"bidi","#kieli":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","tiedostopolku":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#ominaisuus":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor" +,"articlepath":"articlepath","palvelin":"server","server":"server","palvelinnimi":"servername","servername":"servername","skriptipolku":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","AAKKOSTUS":"defaultsort","OLETUSAAKKOSTUS":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SIVUKOKO":"pagesize","PAGESIZE":"pagesize","SUOJAUSTASO":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SIVUNIMI":"pagename","PAGENAME":"pagename","SIVUNIMIE":"pagenamee","PAGENAMEE":"pagenamee","KOKOSIVUNIMI":"fullpagename","FULLPAGENAME":"fullpagename","KOKOSIVUNIMIE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ALASIVUNIMI":"subpagename","SUBPAGENAME":"subpagename","ALASIVUNIMIE":"subpagenamee", +"SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","KANTASIVUNIMI":"basepagename","BASEPAGENAME":"basepagename","KANTASIVUNIMIE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","KESKUSTELUSIVUNIMI":"talkpagename","TALKPAGENAME":"talkpagename","KESKUSTELUSIVUNIMIE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","AIHESIVUNIMI":"subjectpagename","ARTIKKELISIVUNIMI":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","AIHESIVUNIMIE":"subjectpagenamee","ARTIKKELISIVUNIMIE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","VERSIOID":"revisionid","REVISIONID":"revisionid","VERSIOPÄIVÄ":"revisionday","REVISIONDAY":"revisionday","VERSIOPÄIVÄ2":"revisionday2","REVISIONDAY2":"revisionday2","VERSIOKUUKAUSI":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","VERSIOVUOSI":"revisionyear","REVISIONYEAR":"revisionyear","VERSIOAIKALEIMA": +"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NIMIAVARUUS":"namespace","NAMESPACE":"namespace","NIMIAVARUUSE":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","KESKUSTELUAVARUUS":"talkspace","TALKSPACE":"talkspace","KESKUSTELUAVARUUSE":"talkspacee","TALKSPACEE":"talkspacee","AIHEAVARUUS":"subjectspace","ARTIKKELIAVARUUS":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","AIHEAVARUUSE":"subjectspacee","ARTIKKELIAVARUUSE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKKELIMÄÄRÄ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","TIEDOSTOMÄÄRÄ":"numberoffiles","NUMBEROFFILES":"numberoffiles","KÄYTTÄJÄMÄÄRÄ":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SIVUMÄÄRÄ":"numberofpages","NUMBEROFPAGES":"numberofpages","YLLÄPITÄJÄMÄÄRÄ":"numberofadmins", +"NUMBEROFADMINS":"numberofadmins","MUOKKAUSMÄÄRÄ":"numberofedits","NUMBEROFEDITS":"numberofedits","NÄKYVÄOTSIKKO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","KULUVAKUU":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","KULUVAKUUNIMI":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","KULUVAKUUNIMIGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","KULUVAKUUNIMILYHYT":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","KULUVAPÄIVÄ":"currentday","CURRENTDAY":"currentday","KULUVAPÄIVÄ2":"currentday2","CURRENTDAY2":"currentday2","KULUVAPÄIVÄNIMI":"currentdayname","CURRENTDAYNAME":"currentdayname","KULUVAVUOSI":"currentyear","CURRENTYEAR":"currentyear","KULUVAAIKA":"currenttime","CURRENTTIME":"currenttime","KULUVATUNTI":"currenthour","CURRENTHOUR":"currenthour","PAIKALLINENKUU":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1": +"localmonth1","PAIKALLINENKUUNIMI":"localmonthname","LOCALMONTHNAME":"localmonthname","PAIKALLINENKUUNIMIGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","PAIKALLINENKUUNIMILYHYT":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","PAIKALLINENPÄIVÄ":"localday","LOCALDAY":"localday","PAIKALLINENPÄIVÄ2":"localday2","LOCALDAY2":"localday2","PAIKALLINENPÄIVÄNIMI":"localdayname","LOCALDAYNAME":"localdayname","PAIKALLINENVUOSI":"localyear","LOCALYEAR":"localyear","PAIKALLINENAIKA":"localtime","LOCALTIME":"localtime","PAIKALLINENTUNTI":"localhour","LOCALHOUR":"localhour","SIVUSTONIMI":"sitename","SITENAME":"sitename","KULUVAVIIKKO":"currentweek","CURRENTWEEK":"currentweek","KULUVAVIIKONPÄIVÄ":"currentdow","CURRENTDOW":"currentdow","PAIKALLINENVIIKKO":"localweek","LOCALWEEK":"localweek","PAIKALLINENVIIKONPÄIVÄ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","NYKYINENVERSIO":"currentversion","CURRENTVERSION":"currentversion","KULUVAAIKALEIMA": +"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","PAIKALLINENAIKALEIMA":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zčČšŠžŽäÄöÖ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-om.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-om.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-om.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-or.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-or.json new file mode 100644 index 0000000..e1822ce --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-or.json @@ -0,0 +1,9 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","_ବଦଳା_ନହେବାଶ୍ରେଣୀ_":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__": +"nocontentconvert"},{"_ନୂଆବିଭାଗଲିଙ୍କ_":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_ନୂଆ_ବିଭାଗ_ନକରିବା_ଲିଙ୍କ_":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","_ଲୁଚିଥିବାବିଭାଗ_":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","ବ୍ୟାକରଣ":"grammar","grammar":"grammar","ଲିଙ୍ଗ":"gender","gender":"gender","ବହୁବଚନ" +:"plural","plural":"plural","bidi":"bidi","#language":"language","ବାଆଁପ୍ୟାଡ଼":"padleft","padleft":"padleft","ଡାହାଣପ୍ୟାଡ଼":"padright","padright":"padright","anchorencode":"anchorencode","ଫାଇଲରାହା":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#ବିଶେଷ":"special","#special":"special","#speciale":"speciale","#ଟାଗ":"tag","#tag":"tag","#ତାରିଖରପ୍ରକାର":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#ସମୟ":"time","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth", +"noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","ଲେଖାର_ପଥ":"articlepath","articlepath":"articlepath","ସର୍ଭର":"server","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ଫରଦଆକାର":"pagesize","PAGESIZE":"pagesize","ପ୍ରତିରକ୍ଷାସ୍ତର":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ପୃଷ୍ଠା_ନାଆଁ":"pagename","PAGENAME":"pagename","ପୃଷ୍ଠା_ନାମକାରଣକାରୀ":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME": +"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ନେମସ୍ପେସ":"namespace","NAMESPACE":"namespace","ନେମସ୍ପେସକାରୀ":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","ଟକସ୍ପେସ":"talkspace","TALKSPACE":"talkspace","ଟକସ୍ପେସକାରୀ": +"talkspacee","TALKSPACEE":"talkspacee","ବିଷୟସ୍ପେସ":"subjectspace","ଲେଖାସ୍ପେସ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ଲେଖା_ସଂଖ୍ୟା":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ଫାଇଲ_ସଂଖ୍ୟା":"numberoffiles","NUMBEROFFILES":"numberoffiles","ବ୍ୟବାହାରକାରୀ_ସଂଖ୍ୟା":"numberofusers","NUMBEROFUSERS":"numberofusers","ସଚଳ_ବ୍ୟବାହାରକାରୀଙ୍କ_ସଂଖ୍ୟା":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ପୃଷ୍ଠା_ସଂଖ୍ୟା":"numberofpages","NUMBEROFPAGES":"numberofpages","ପରିଛାମାନଙ୍କତାଲିକା":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ବଦଳ_ସଂଖ୍ୟା":"numberofedits","NUMBEROFEDITS":"numberofedits","ଦେଖଣାନାଆଁ":"displaytitle", +"DISPLAYTITLE":"displaytitle","!":"!","=":"=","ଏବେକାର_ମାସ":"currentmonth","ଏବେର_ମାସ୨":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ଏବେର_ମାସ":"currentmonth1","CURRENTMONTH1":"currentmonth1","ଏବେକାର_ମାସ_ନାଆଁ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","ଏବେକାର_ମାସ_ନାଆଁ_ସାଧାରଣ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ଏବେକାର_ମାସ_ନାଆଁ_ସଂକ୍ଷିପ୍ତ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ଏବେକାର_ଦିନ":"currentday","CURRENTDAY":"currentday","ଏବେକାର_ଦିନ୨":"currentday2","CURRENTDAY2":"currentday2","ଏବେକାର_ଦିନ_ନାଆଁ":"currentdayname","CURRENTDAYNAME":"currentdayname","ଏବେକାର_ବର୍ଷ":"currentyear","CURRENTYEAR":"currentyear","ଏବେକାର_ସମୟ":"currenttime", +"CURRENTTIME":"currenttime","ଏବେକାର_ଘଣ୍ଟା":"currenthour","CURRENTHOUR":"currenthour","ଏବେର_ମାସ୧":"localmonth","ସ୍ଥାନୀୟ_ମାସ୨":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","ଏବେକାର_ମାସ୧":"localmonth1","LOCALMONTH1":"localmonth1","ମାସ୧ର_ନାଆଁ":"localmonthname","LOCALMONTHNAME":"localmonthname","ସ୍ଥାନୀୟ_ମାସ୧_ନାଆଁ_ସାଧାରଣ":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ସ୍ଥାନୀୟ_ମାସର୧_ନାଆଁ_ସଂକ୍ଷିପ୍ତ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","Local_ଦିନ":"localday","LOCALDAY":"localday","ସ୍ଥାନୀୟ_ଦିନ୨":"localday2","LOCALDAY2":"localday2","ଦିନ":"localdayname","LOCALDAYNAME":"localdayname","ସ୍ଥାନୀୟ_ବର୍ଷ":"localyear","LOCALYEAR":"localyear","ସ୍ଥାନୀୟ_ସମୟ":"localtime","LOCALTIME":"localtime", +"ସ୍ଥାନୀୟ_ଘଣ୍ଟା":"localhour","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ନଗଦ_ରିଭିଜନ":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z\\x{0B00}-\\x{0B7F}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-os.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-os.json new file mode 100644 index 0000000..e8046ed --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-os.json @@ -0,0 +1,14 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__æнæсæр__":"notoc","__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__æнæгалерей__":"nogallery","__без_галереи__":"nogallery","__nogallery__":"nogallery","__сæртимæ__":"forcetoc","__обязательное_оглавление__":"forcetoc", +"__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__сæртæ__":"toc","__оглавление__":"toc","__огл__":"toc","__toc__":"toc","__æнæхайивынæй__":"noeditsection","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index", +"__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle": +"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender","множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates", +"#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername", +"путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ФАРСЫНОМ":"pagename","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee", +"ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename", +"НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber", +"NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","УАЦТЫНЫМÆЦ":"numberofarticles","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ФÆРСТЫНЫМÆЦ":"numberofpages","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES": +"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","АЦЫМÆЙ":"currentmonth","АЦЫМÆЙ2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","АЦЫМÆЙ1":"currentmonth1","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","АЦЫМÆЙЫНОМ":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","АЦЫМÆЙЫНОМГУЫР":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","АЦЫМÆЙЫНОМЦЫБ":"currentmonthabbrev", +"НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","АБОН":"currentday","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","АБОН2":"currentday2","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","АБОНЫБОНЫНОМ":"currentdayname","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","АЦЫАЗ":"currentyear","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","НЫРЫРÆСТÆГ":"currenttime","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","НЫРЫСАХАТ":"currenthour","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА" +:"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ": +"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters": +"/^((?:[a-z]|а|æ|б|в|г|д|е|ё|ж|з|и|й|к|л|м|н|о|п|р|с|т|у|ф|х|ц|ч|ш|щ|ъ|ы|ь|э|ю|я|“|»)+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pa.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pa.json new file mode 100644 index 0000000..e930b9a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pa.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#ਸਮੇ":"time","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE": +"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY": +"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters": +"/^([ਁਂਃਅਆਇਈਉਊਏਐਓਔਕਖਗਘਙਚਛਜਝਞਟਠਡਢਣਤਥਦਧਨਪਫਬਭਮਯਰਲਲ਼ਵਸ਼ਸਹ਼ਾਿੀੁੂੇੈੋੌ੍ਖ਼ਗ਼ਜ਼ੜਫ਼ੰੱੲੳa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pag.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pag.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pag.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pam.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pam.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pam.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pap.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pap.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pap.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pcd.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pcd.json new file mode 100644 index 0000000..30830cb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pcd.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode", +"initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int", +"#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property", +"#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGE":"pagename","PAGENAME":"pagename", +"NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid", +"REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee", +"ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL": +"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pcm.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pcm.json new file mode 100644 index 0000000..64d7ca7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pcm.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry" +,"PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE" +:"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname", +"LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zá]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pdc.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pdc.json new file mode 100644 index 0000000..d777619 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pdc.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__":"forcetoc","__inhaltsverzeichnis__":"toc","__toc__":"toc", +"__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__":"noindex","__NOINDEX__":"noindex","__PERMANENTE_WEITERLEITUNG__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","lokale_url":"localurl","localurl":"localurl","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zahlenformat":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#sprache":"language","#language":"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright": +"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorienbaum":"categorytree","#kategoriebaum":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx", +"#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE":"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SEITENNAME":"pagename","PAGENAME":"pagename","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSSIONSSEITE":"talkpagename","DISK":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","HAUPTSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR":"revisionyear","VERSIONSJAHR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp", +"REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","NAMENSRAUM":"namespace","NAMESPACE":"namespace","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DATEIANZAHL":"numberoffiles","NUMBEROFFILES":"numberoffiles","BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTIVE_BENUTZER": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JETZIGER_MONATSNAME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JETZIGER_KALENDERTAG":"currentday","JETZIGER_TAG":"currentday","CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2","JETZIGER_TAG_2": +"currentday2","CURRENTDAY2":"currentday2","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV":"localmonthnamegen","LOKALER_MONATSNAME_GEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2","LOKALER_TAG_2":"localday2","LOCALDAY2":"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME":"localdayname","LOKALES_JAHR" +:"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize","JETZIGE_VERSION":"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHALTSSPRACHE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE": +"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöüßa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pfl.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pfl.json new file mode 100644 index 0000000..d777619 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pfl.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__":"forcetoc","__inhaltsverzeichnis__":"toc","__toc__":"toc", +"__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__":"noindex","__NOINDEX__":"noindex","__PERMANENTE_WEITERLEITUNG__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","lokale_url":"localurl","localurl":"localurl","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zahlenformat":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#sprache":"language","#language":"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright": +"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorienbaum":"categorytree","#kategoriebaum":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx", +"#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE":"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SEITENNAME":"pagename","PAGENAME":"pagename","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSSIONSSEITE":"talkpagename","DISK":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","HAUPTSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR":"revisionyear","VERSIONSJAHR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp", +"REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","NAMENSRAUM":"namespace","NAMESPACE":"namespace","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DATEIANZAHL":"numberoffiles","NUMBEROFFILES":"numberoffiles","BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTIVE_BENUTZER": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JETZIGER_MONATSNAME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JETZIGER_KALENDERTAG":"currentday","JETZIGER_TAG":"currentday","CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2","JETZIGER_TAG_2": +"currentday2","CURRENTDAY2":"currentday2","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV":"localmonthnamegen","LOKALER_MONATSNAME_GEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2","LOKALER_TAG_2":"localday2","LOCALDAY2":"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME":"localdayname","LOKALES_JAHR" +:"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize","JETZIGE_VERSION":"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHALTSSPRACHE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE": +"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöüßa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pi.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pi.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pi.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pih.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pih.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pih.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pl.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pl.json new file mode 100644 index 0000000..5777c0e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pl.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__bezspisu__":"notoc","__notoc__":"notoc","__bezgalerii__":"nogallery","__nogallery__":"nogallery","__zespisem__":"forcetoc","__wymuśspis__":"forcetoc","__forcetoc__":"forcetoc","__spis__":"toc","__toc__":"toc","__bezedycjisekcji__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert", +"__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LINKNOWEJSEKCJI__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORIAUKRYTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKSUJ__":"index","__INDEX__":"index","__NIEINDEKSUJ__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"pn":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","zmałej":"lcfirst","odmałej":"lcfirst","lcfirst":"lcfirst","zwielkiej":"ucfirst","zdużej":"ucfirst","odwielkiej":"ucfirst","oddużej":"ucfirst","ucfirst":"ucfirst","małe":"lc","lc":"lc","wielkie":"uc","duże":"uc","uc":"uc","localurl":"localurl","localurle":"localurle","pełnyurl":"fullurl","fullurl":"fullurl","fullurle":"fullurle", +"canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","odmiana":"grammar","grammar":"grammar","płeć":"gender","gender":"gender","mnoga":"plural","plural":"plural","bidi":"bidi","#język":"language","#language":"language","dolewej":"padleft","padleft":"padleft","doprawej":"padright","padright":"padright","anchorencode":"anchorencode","ścieżkapliku":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#specjalna":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#drzewokategorii":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx", +"#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#właściwość":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","ścieżkaartykułów":"articlepath","articlepath":"articlepath","serwer":"server","server":"server","nazwaserwera":"servername","servername":"servername","ścieżkaskryptu":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTUJ":"defaultsort","DOMYŚLNIESORTUJ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","STRONYWKATEGORII":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ROZMIARSTRONY":"pagesize","PAGESIZE":"pagesize","__POZIOMZABEZPIECZEŃ__":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","NAZWASTRONY":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","PELNANAZWASTRONY":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","NAZWAPODSTRONY":"subpagename","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BAZOWANAZWASTRONY":"basepagename","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","NAZWASTRONYDYSKUSJI":"talkpagename","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAZWAPRZESTRZENI":"namespace" +,"NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","DYSKUSJA":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTYKUŁÓW":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","PLIKÓW":"numberoffiles","NUMBEROFFILES":"numberoffiles","UŻYTKOWNIKÓW":"numberofusers","NUMBEROFUSERS":"numberofusers","LICZBAAKTYWNYCHUŻYTKOWNIKÓW":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","STRON":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINISTRATORÓW":"numberofadmins","NUMBEROFADMINS":"numberofadmins","EDYCJI":"numberofedits","NUMBEROFEDITS":"numberofedits","WYŚWIETLANYTYTUŁ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","AKTUALNYDZIEŃ":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","NAZWADNIA":"currentdayname","CURRENTDAYNAME":"currentdayname","AKTUALNYROK":"currentyear","CURRENTYEAR":"currentyear","AKTUALNYCZAS":"currenttime","CURRENTTIME":"currenttime","AKTUALNAGODZINA":"currenthour","CURRENTHOUR":"currenthour","MIESIĄC":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","MIESIĄCNAZWA":"localmonthname","LOCALMONTHNAME":"localmonthname","MIESIĄCNAZWAD":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MIESIĄCNAZWASKR":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DZIEŃ":"localday","LOCALDAY":"localday","DZIEŃ2":"localday2","LOCALDAY2":"localday2","DZIEŃTYGODNIA":"localdayname","LOCALDAYNAME":"localdayname","ROK":"localyear","LOCALYEAR":"localyear","CZAS":"localtime","LOCALTIME":"localtime","GODZINA":"localhour","LOCALHOUR":"localhour","PROJEKT": +"sitename","SITENAME":"sitename","AKTUALNYTYDZIEŃ":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","TYDZIEŃROKU":"localweek","LOCALWEEK":"localweek","DZIEŃTYGODNIANR":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","AKTUALNAWERSJA":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zęóąśłżźćńĘÓĄŚŁŻŹĆŃ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pms.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pms.json new file mode 100644 index 0000000..e0a99a4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pms.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag","#formatodata":"formatdate","#formatdate":"formatdate", +"#dateformat":"formatdate","#alberocategorie":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE": +"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1", +"REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2","CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE": +"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME":"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NOMESITO":"sitename", +"SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pnb.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pnb.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pnb.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pnt.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pnt.json new file mode 100644 index 0000000..48d7cdc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pnt.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__χωρισππ__":"notoc","__χωρισπινακαπεριεχομενων__":"notoc","__notoc__":"notoc","__χωρισπινακοθηκη__":"nogallery","__nogallery__":"nogallery","__μεππ__":"forcetoc","__μεπινακαπεριεχομενων__":"forcetoc","__forcetoc__":"forcetoc","__ππ__":"toc", +"__πινακασπεριεχομενων__":"toc","__toc__":"toc","__χωρισεπεξενοτ__":"noeditsection","__χωρισεπεξεργασιαενοτητων__":"noeditsection","__noeditsection__":"noeditsection","__χωρισμετατροπητιτλου__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__χωρισμετατροπηπεριχομενου__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ΔΕΣΜΟΣΝΕΑΣΕΝΟΤΗΤΑΣ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__ΚΡΥΦΗΚΑΤΗΓΟΡΙΑ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ΕΥΡΕΤΗΡΙΟ__":"index","__INDEX__":"index","__ΧΩΡΙΣΕΥΡΕΤΗΡΙΟ__":"noindex","__NOINDEX__":"noindex","__ΣΤΑΤΙΚΗΑΝΑΚΑΤΕΥΘΥΝΣΗ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"χο":"ns","χωροσονοματων":"ns","οχ":"ns","ονοματοχωροσ":"ns","ns":"ns","nse":"nse","κωδικοποιησηurl":"urlencode","urlencode":"urlencode","πρωτοπεζο":"lcfirst","lcfirst":"lcfirst","πρωτοκεφαλαιο":"ucfirst","ucfirst":"ucfirst","πεζα":"lc","lc":"lc","κεφαλαια":"uc","uc":"uc","τοπικοurl":"localurl","localurl":"localurl","τοπικοurlκ":"localurle","localurle":"localurle","πληρεσurl":"fullurl","fullurl":"fullurl","πληρεσurlκ":"fullurle","fullurle":"fullurle","κανονικοurl":"canonicalurl","canonicalurl":"canonicalurl","κανονικοurlκ":"canonicalurle","canonicalurle":"canonicalurle","μορφοποιησηαριθμου":"formatnum","formatnum":"formatnum","γραμματικη":"grammar","grammar":"grammar","φυλο":"gender","gender":"gender","πληθυντικοσ":"plural","plural":"plural","bidi" +:"bidi","#γλωσσα":"language","#language":"language","αριστεροπαραγεμισμα":"padleft","padleft":"padleft","δεξιπαραγεμισμα":"padright","padright":"padright","κωδικοποιησηαγκυρασ":"anchorencode","anchorencode":"anchorencode","διαδρομηαρχειου":"filepath","filepath":"filepath","pageid":"pageid","εσωτ":"int","int":"int","#λειτουργία":"special","#special":"special","#speciale":"speciale","#ετικέτα":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth", +"noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","εξυπηρετητησ":"server","server":"server","ονομαεξυπηρετητη":"servername","servername":"servername","διαδρομηπρογραμματοσ":"scriptpath","scriptpath":"scriptpath","διαδρομηστυλ":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ΑΡΙΘΜΟΣΣΕΛΙΔΩΝ":"numberofpages","NUMBEROFPAGES":"numberofpages","ΑΡΙΘΜΟΣΧΡΗΣΤΩΝ":"numberofusers","NUMBEROFUSERS":"numberofusers","ΕΝΕΡΓΟΙΧΡΗΣΤΕΣ":"numberofactiveusers","ΑΡΙΘΜΟΣΕΝΕΡΓΩΝΧΡΗΣΤΩΝ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ΑΡΙΘΜΟΣΑΡΘΡΩΝ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ΑΡΙΘΜΟΣΑΡΧΕΙΩΝ":"numberoffiles","NUMBEROFFILES":"numberoffiles", +"ΑΡΙΘΜΟΣΔΙΑΧΕΙΡΙΣΤΩΝ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ΟΜΑΔΑΑΡΙΘΜΗΣΗΣ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ΑΡΙΘΜΟΣΕΠΕΞΕΡΓΑΣΙΩΝ":"numberofedits","NUMBEROFEDITS":"numberofedits","ΠΡΟΚΑΘΟΡΙΣΜΕΝΗΤΑΞΙΝΟΜΗΣΗ":"defaultsort","ΚΛΕΙΔΙΠΡΟΚΑΘΟΡΙΣΜΕΝΗΣΤΑΞΙΝΟΜΗΣΗΣ":"defaultsort","ΠΡΟΚΑΘΟΡΙΣΜΕΝΗΤΑΞΙΝΟΜΗΣΗΚΑΤΗΓΟΡΙΑΣ":"defaultsort","ΠΡΟΚΤΑΞ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","ΣΕΛΙΔΕΣΣΤΗΝΚΑΤΗΓΟΡΙΑ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ΜΕΓΕΘΟΣΣΕΛΙΔΑΣ":"pagesize","PAGESIZE":"pagesize","ΕΠΙΠΕΔΟΠΡΟΣΤΑΣΙΑΣ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ΠΕΡΙΟΧΗΚ":"namespacee", +"NAMESPACEE":"namespacee","ΑΡΙΘΜΟΣΟΝΟΜΑΤΟΣΧΩΡΟΥ":"namespacenumber","ΑΡΙΘΜΟΣΟΝΟΜΑΤΟΧΩΡΟΥ":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ΠΕΡΙΟΧΗΣΥΖΗΤΗΣΕΩΝ":"talkspace","TALKSPACE":"talkspace","ΠΕΡΙΟΧΗΣΥΖΗΤΗΣΕΩΝΚ":"talkspacee","TALKSPACEE":"talkspacee","ΠΕΡΙΟΧΗΘΕΜΑΤΩΝ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ΠΕΡΙΟΧΗΘΕΜΑΤΩΝΚ":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ΟΝΟΜΑΣΕΛΙΔΑΣ":"pagename","PAGENAME":"pagename","ΟΝΟΜΑΣΕΛΙΔΑΣΚ":"pagenamee","PAGENAMEE":"pagenamee","ΠΛΗΡΕΣΟΝΟΜΑΣΕΛΙΔΑΣ":"fullpagename","FULLPAGENAME":"fullpagename","ΠΛΗΡΕΣΟΝΟΜΑΣΕΛΙΔΑΣΚ":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ΒΑΣΗΟΝΟΜΑΤΟΣΣΕΛΙΔΑΣ":"basepagename","BASEPAGENAME":"basepagename", +"ΒΑΣΗΟΝΟΜΑΤΟΣΣΕΛΙΔΑΣΚ":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ΟΝΟΜΑΥΠΟΣΕΛΙΔΑΣ":"subpagename","SUBPAGENAME":"subpagename","ΟΝΟΜΑΥΠΟΣΕΛΙΔΑΣΚ":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ΟΝΟΜΑΣΕΛΙΔΑΣΣΥΖΗΤΗΣΕΩΝ":"talkpagename","TALKPAGENAME":"talkpagename","ΟΝΟΜΑΣΕΛΙΔΑΣΣΥΖΗΤΗΣΕΩΝΚ":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","ΟΝΟΜΑΣΕΛΙΔΑΣΘΕΜΑΤΟΣ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ΟΝΟΜΑΣΕΛΙΔΑΣΘΕΜΑΤΟΣΚ":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ΚΩΔΙΚΟΣΑΛΛΑΓΗΣ":"revisionid","REVISIONID":"revisionid","ΜΕΡΑΑΛΛΑΓΗΣ":"revisionday","REVISIONDAY":"revisionday","ΜΕΡΑΑΛΛΑΓΗΣ2":"revisionday2","REVISIONDAY2":"revisionday2","ΜΗΝΑΣΑΛΛΑΓΗΣ":"revisionmonth","REVISIONMONTH":"revisionmonth", +"ΜΗΝΑΣΑΝΑΘΕΩΡΗΣΗΣ1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ΕΤΟΣΑΛΛΑΓΗΣ":"revisionyear","REVISIONYEAR":"revisionyear","ΧΡΟΝΟΣΗΜΑΝΣΗΑΛΛΑΓΗΣ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ΧΡΗΣΤΗΣΑΝΑΘΕΩΡΗΣΗΣ":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ΠΕΡΙΟΧΗ":"namespace","NAMESPACE":"namespace","ΔΕΙΞΕΤΙΤΛΟ":"displaytitle","ΠΡΟΒΟΛΗΤΙΤΛΟΥ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","ΤΡΕΧΩΝΜΗΝΑΣ":"currentmonth","ΤΡΕΧΩΝΜΗΝΑΣ2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ΤΡΕΧΩΝΜΗΝΑΣ1":"currentmonth1","CURRENTMONTH1":"currentmonth1","ΤΡΕΧΩΝΜΗΝΑΣΟΝΟΜΑ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","ΤΡΕΧΩΝΜΗΝΑΣΓΕΝΙΚΗ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ΤΡΕΧΩΝΜΗΝΑΣΣΥΝΤ": +"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ΤΡΕΧΟΥΣΑΜΕΡΑ":"currentday","CURRENTDAY":"currentday","ΤΡΕΧΟΥΣΑΜΕΡΑ2":"currentday2","CURRENTDAY2":"currentday2","ΤΡΕΧΟΥΣΑΜΕΡΑΟΝΟΜΑ":"currentdayname","CURRENTDAYNAME":"currentdayname","ΤΡΕΧΟΝΕΤΟΣ":"currentyear","CURRENTYEAR":"currentyear","ΤΡΕΧΩΝΧΡΟΝΟΣ":"currenttime","CURRENTTIME":"currenttime","ΤΡΕΧΟΥΣΑΩΡΑ":"currenthour","CURRENTHOUR":"currenthour","ΤΟΠΙΚΟΣΜΗΝΑΣ":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","ΤΟΠΙΚΟΣΜΗΝΑΣ1":"localmonth1","LOCALMONTH1":"localmonth1","ΤΟΠΙΚΟΣΜΗΝΑΣΟΝΟΜΑ":"localmonthname","LOCALMONTHNAME":"localmonthname","ΤΟΠΙΚΟΣΜΗΝΑΣΓΕΝΙΚΗ":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ΤΟΠΙΚΟΣΜΗΝΑΣΣΥΝΤ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ΤΟΠΙΚΗΜΕΡΑ":"localday","LOCALDAY":"localday", +"ΤΟΠΙΚΗΜΕΡΑ2":"localday2","LOCALDAY2":"localday2","ΤΟΠΙΚΗΜΕΡΑΟΝΟΜΑ":"localdayname","LOCALDAYNAME":"localdayname","ΤΟΠΙΚΟΕΤΟΣ":"localyear","LOCALYEAR":"localyear","ΤΟΠΙΚΟΣΧΡΟΝΟΣ":"localtime","LOCALTIME":"localtime","ΤΟΠΙΚΗΩΡΑ":"localhour","LOCALHOUR":"localhour","ΙΣΤΟΧΩΡΟΣ":"sitename","SITENAME":"sitename","ΤΡΕΧΟΥΣΑΕΒΔΟΜΑΔΑ":"currentweek","CURRENTWEEK":"currentweek","ΤΡΕΧΟΥΣΑΜΕΡΑΕΒΔΟΜΑΔΑΣ":"currentdow","CURRENTDOW":"currentdow","ΤΟΠΙΚΗΕΒΔΟΜΑΔΑ":"localweek","LOCALWEEK":"localweek","ΤΟΠΙΚΗΜΕΡΑΕΒΔΟΜΑΔΑΣ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ΤΡΕΧΟΥΣΑΕΚΔΟΣΗ":"currentversion","CURRENTVERSION":"currentversion","ΤΡΕΧΟΥΣΑΧΡΟΝΟΣΗΜΑΝΣΗ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ΤΟΠΙΚΗΧΡΟΝΟΣΗΜΑΝΣΗ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp", +"ΚΩΔΙΚΟΣΦΟΡΑΣ":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ΓΛΩΣΣΑΠΕΡΙΕΧΟΜΕΝΟΥ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zαβγδεζηθικλμνξοπρστυφχψωςΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίόύώϊϋΐΰΆΈΉΊΌΎΏΪΫ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ps.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ps.json new file mode 100644 index 0000000..4c68c91 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ps.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__بی‌نيولک__":"notoc","__notoc__":"notoc","__بی‌نندارتونه__":"nogallery","__nogallery__":"nogallery","__نيوليکداره__":"forcetoc","__forcetoc__":"forcetoc","__نيوليک__":"toc","__toc__":"toc","__بی‌برخې__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__": +"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__پټه_وېشنيزه__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ليکلړ__":"index","__INDEX__":"index","__بې_ليکلړ__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","ګرامر":"grammar","grammar":"grammar","gender":"gender","جمع":"plural","plural":"plural","bidi":"bidi","#ژبه":"language","#language":"language","padleft":"padleft", +"padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#ځانګړی":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#بابېل":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#که":"if","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#وخت":"time","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","پالنګر":"server","server":"server","دپالنګر_نوم":"servername","servername":"servername", +"scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"دمخونوشمېر":"numberofpages","NUMBEROFPAGES":"numberofpages","دکارونکوشمېر":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","دليکنوشمېر":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ددوتنوشمېر":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","مخکچه":"pagesize","PAGESIZE":"pagesize","ژغورکچه":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","د_نوم_تشيال_نښه":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber", +"دخبرواترو_تشيال":"talkspace","TALKSPACE":"talkspace","دخبرواترو_تشيال_نښه":"talkspacee","TALKSPACEE":"talkspacee","دسکالوتشيال":"subjectspace","دليکنې_تشيال":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","دسکالوتشيال_نښه":"subjectspacee","دليکنې_تشيال_نښه":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","دمخ_نوم":"pagename","PAGENAME":"pagename","دمخ_نښه":"pagenamee","PAGENAMEE":"pagenamee","دمخ_بشپړنوم":"fullpagename","FULLPAGENAME":"fullpagename","دمخ_بشپړنوم_نښه":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME": +"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","نوم_تشيال":"namespace","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","روانه_مياشت":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","دروانې_مياشت_نوم":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","دروانې_مياشت_لنډون":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","نن":"currentday","CURRENTDAY":"currentday","نن۲":"currentday2","CURRENTDAY2":"currentday2","دننۍورځې_نوم":"currentdayname","CURRENTDAYNAME": +"currentdayname","سږکال":"currentyear","CURRENTYEAR":"currentyear","داوخت":"currenttime","CURRENTTIME":"currenttime","دم_ګړۍ":"currenthour","CURRENTHOUR":"currenthour","سيمه_يزه_مياشت":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","دسيمه_يزې_مياشت_نوم":"localmonthname","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","دسيمه_يزې_مياشت_لنډون":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","سيمه_يزه_ورځ":"localday","LOCALDAY":"localday","سيمه_يزه_ورځ۲":"localday2","LOCALDAY2":"localday2","دسيمه_يزې_ورځ_نوم":"localdayname","LOCALDAYNAME":"localdayname","سيمه_يزکال":"localyear","LOCALYEAR":"localyear","سيمه_يزوخت":"localtime","LOCALTIME":"localtime","سيمه_يزه_ګړۍ":"localhour","LOCALHOUR":"localhour","دوېبځي_نوم":"sitename","SITENAME":"sitename","روانه_اوونۍ": +"currentweek","CURRENTWEEK":"currentweek","داوونۍورځ":"currentdow","CURRENTDOW":"currentdow","سيمه_يزه_اوونۍ":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pt.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pt.json new file mode 100644 index 0000000..95660a4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pt.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"dynamicpagelist":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"trecho":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__semtdc__":"notoc","__semsumário__":"notoc","__notoc__":"notoc","__semgaleria__":"nogallery","__nogallery__":"nogallery","__forcartdc__":"forcetoc","__forcarsumario__":"forcetoc","__forçartdc__":"forcetoc","__forçarsumário__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc", +"__sumário__":"toc","__sumario__":"toc","__toc__":"toc","__nãoeditarseção__":"noeditsection","__semeditarseção__":"noeditsection","__naoeditarsecao__":"noeditsection","__semeditarsecao__":"noeditsection","__noeditsection__":"noeditsection","__semconvertertitulo__":"notitleconvert","__semconvertertítulo__":"notitleconvert","__semct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__semconverterconteudo__":"nocontentconvert","__semconverterconteúdo__":"nocontentconvert","__semcc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LINKDENOVASECAO__":"newsectionlink","__LINKDENOVASEÇÃO__":"newsectionlink","__LIGACAODENOVASECAO__":"newsectionlink","__LIGAÇÃODENOVASEÇÃO__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__SEMLINKDENOVASECAO__":"nonewsectionlink","__SEMLINKDENOVASEÇÃO__":"nonewsectionlink","__SEMLIGACAODENOVASECAO__":"nonewsectionlink","__SEMLIGAÇÃODENOVASEÇÃO__": +"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATEGORIAOCULTA__":"hiddencat","__CATOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NAOINDEXAR__":"noindex","__NÃOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECIONAMENTOESTATICO__":"staticredirect","__REDIRECIONAMENTOESTÁTICO__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","codificaurl":"urlencode","urlencode":"urlencode","primeiraminuscula":"lcfirst","primeiraminúscula":"lcfirst","lcfirst":"lcfirst","primeiramaiuscula":"ucfirst","primeiramaiúscula":"ucfirst","ucfirst":"ucfirst","minuscula":"lc","minúscula":"lc","minusculas":"lc","minúsculas":"lc","lc":"lc","maiuscula":"uc","maiúscula":"uc","maiusculas":"uc","maiúsculas":"uc","uc":"uc", +"localurl":"localurl","localurle":"localurle","urlcompleto":"fullurl","fullurl":"fullurl","urlcompletoc":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genero":"gender","gênero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#idioma":"language","#language":"language","padleft":"padleft","padright":"padright","codificaancora":"anchorencode","codificaâncora":"anchorencode","anchorencode":"anchorencode","caminhodoarquivo":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invocar":"invoke","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seigual":"ifeq","#ifeq":"ifeq","#switch":"switch","#seexiste":"ifexist","#ifexist":"ifexist","#seexpr":"ifexpr","#ifexpr":"ifexpr","#seerro": +"iferror","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#partesdotítulo":"titleparts","#partesdotitulo":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#trecho":"lst","#lstx":"lstx","#section-x":"lstx","#trecho-x":"lstx","#lsth":"lsth","#section-h":"lsth","naointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriedade":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","servidor":"server","server":"server","nomedoservidor":"servername","servername":"servername","caminhodoscript":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMERONOGRUPO":"numberingroup","NÚMERONOGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENACAOPADRAO":"defaultsort", +"ORDENAÇÃOPADRÃO":"defaultsort","ORDEMPADRAO":"defaultsort","ORDEMPADRÃO":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINASNACATEGORIA":"pagesincategory","PÁGINASNACATEGORIA":"pagesincategory","PAGINASNACAT":"pagesincategory","PÁGINASNACAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMANHODAPAGINA":"pagesize","TAMANHODAPÁGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECAO":"protectionlevel","NÍVELDEPROTEÇÃO":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMEDAPAGINA":"pagename","NOMEDAPÁGINA":"pagename","PAGENAME":"pagename","NOMEDAPAGINAC":"pagenamee","NOMEDAPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMECOMPLETODAPAGINA":"fullpagename","NOMECOMPLETODAPÁGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMECOMPLETODAPAGINAC":"fullpagenamee","NOMECOMPLETODAPÁGINAC":"fullpagenamee","FULLPAGENAMEE": +"fullpagenamee","NOMEDASUBPAGINA":"subpagename","NOMEDASUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","NOMEDASUBPAGINAC":"subpagenamee","NOMEDASUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","NOMEDAPAGINABASE":"basepagename","NOMEDAPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMEDAPAGINABASEC":"basepagenamee","NOMEDAPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMEDAPAGINADEDISCUSSAO":"talkpagename","NOMEDAPÁGINADEDISCUSSÃO":"talkpagename","TALKPAGENAME":"talkpagename","NOMEDAPAGINADEDISCUSSAOC":"talkpagenamee","NOMEDAPÁGINADEDISCUSSÃOC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMEDAPAGINADECONTEUDO":"subjectpagename","NOMEDAPÁGINADECONTEÚDO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMEDAPAGINADECONTEUDOC":"subjectpagenamee","NOMEDAPÁGINADECONTEÚDOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee", +"ARTICLEPAGENAMEE":"subjectpagenamee","IDDAREVISAO":"revisionid","IDDAREVISÃO":"revisionid","REVISIONID":"revisionid","DIADAREVISAO":"revisionday","DIADAREVISÃO":"revisionday","REVISIONDAY":"revisionday","DIADAREVISAO2":"revisionday2","DIADAREVISÃO2":"revisionday2","REVISIONDAY2":"revisionday2","MESDAREVISAO":"revisionmonth","MÊSDAREVISÃO":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ANODAREVISAO":"revisionyear","ANODAREVISÃO":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODAREVISAO":"revisionuser","USUÁRIODAREVISÃO":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","DOMINIO":"namespace","DOMÍNIO":"namespace","ESPACONOMINAL":"namespace","ESPAÇONOMINAL":"namespace","NAMESPACE":"namespace","DOMINIOC":"namespacee","DOMÍNIOC":"namespacee","ESPACONOMINALC":"namespacee","ESPAÇONOMINALC":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber", +"PAGINADEDISCUSSAO":"talkspace","PÁGINADEDISCUSSÃO":"talkspace","TALKSPACE":"talkspace","PAGINADEDISCUSSAOC":"talkspacee","PÁGINADEDISCUSSÃOC":"talkspacee","TALKSPACEE":"talkspacee","PAGINADECONTEUDO":"subjectspace","PAGINADECONTEÚDO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","PAGINADECONTEUDOC":"subjectspacee","PAGINADECONTEÚDOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMERODEARTIGOS":"numberofarticles","NÚMERODEARTIGOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMERODEARQUIVOS":"numberoffiles","NÚMERODEARQUIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMERODEUSUARIOS":"numberofusers","NÚMERODEUSUÁRIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMERODEUSUARIOSATIVOS":"numberofactiveusers","NÚMERODEUSUÁRIOSATIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMERODEPAGINAS":"numberofpages","NÚMERODEPÁGINAS":"numberofpages","NUMBEROFPAGES": +"numberofpages","NUMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMERODEEDICOES":"numberofedits","NÚMERODEEDIÇÕES":"numberofedits","NUMBEROFEDITS":"numberofedits","EXIBETITULO":"displaytitle","EXIBETÍTULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESATUAL":"currentmonth","MESATUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESATUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMEDOMESATUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESATUALABREV":"currentmonthabbrev","MESATUALABREVIADO":"currentmonthabbrev","ABREVIATURADOMESATUAL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DIAATUAL":"currentday","CURRENTDAY":"currentday","DIAATUAL2":"currentday2","CURRENTDAY2":"currentday2","NOMEDODIAATUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANOATUAL":"currentyear", +"CURRENTYEAR":"currentyear","HORARIOATUAL":"currenttime","CURRENTTIME":"currenttime","HORAATUAL":"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEDOMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREV":"localmonthabbrev","MESLOCALABREVIADO":"localmonthabbrev","ABREVIATURADOMESLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","LOCALDAY2":"localday2","NOMEDODIALOCAL":"localdayname","LOCALDAYNAME":"localdayname","ANOLOCAL":"localyear","LOCALYEAR":"localyear","HORARIOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMEDOSITE":"sitename","NOMEDOSÍTIO":"sitename","NOMEDOSITIO":"sitename","SITENAME":"sitename","SEMANAATUAL":"currentweek","CURRENTWEEK":"currentweek", +"DIADASEMANAATUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","DIADASEMANALOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","REVISAOATUAL":"currentversion","REVISÃOATUAL":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","IDIOMADOCONTEUDO":"contentlanguage","IDIOMADOCONTEÚDO":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters": +"/^([áâãàéêẽçíòóôõq̃úüűũa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pwn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pwn.json new file mode 100644 index 0000000..8f21bcd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-pwn.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__無目錄__":"notoc","__无目录__":"notoc","__notoc__":"notoc","__無圖庫__":"nogallery","__无图库__":"nogallery","__nogallery__":"nogallery","__強制目錄__":"forcetoc","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__目錄__":"toc","__目录__":"toc","__toc__":"toc","__無段落編輯__": +"noeditsection","__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__不轉換標題__":"notitleconvert","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換內容__":"nocontentconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__新段落链接__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__无新段落链接__":"nonewsectionlink","__隱藏分類__":"hiddencat","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__索引__":"index","__NOINDEX__":"noindex","__无索引__":"noindex","__靜態重新導向__":"staticredirect","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__消除歧义__": +"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"命名空間":"ns","名字空间":"ns","ns":"ns","名称空间":"ns","命名空間e":"nse","名字空间e":"nse","nse":"nse","名称空间e":"nse","urlencode":"urlencode","url编码":"urlencode","lcfirst":"lcfirst","小写首字":"lcfirst","ucfirst":"ucfirst","大写首字":"ucfirst","lc":"lc","小写":"lc","uc":"uc","大写":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","fullurl":"fullurl","完整url":"fullurl","fullurle":"fullurle","完整url等同":"fullurle","canonicalurl":"canonicalurl","规范url":"canonicalurl","canonicalurle":"canonicalurle","规范url等同":"canonicalurle","formatnum":"formatnum","格式化数字":"formatnum","grammar":"grammar","语法":"grammar","性別":"gender","性":"gender","性别":"gender","gender":"gender","plural":"plural","复数":"plural","bidi":"bidi","#語言": +"language","#语言":"language","#language":"language","padleft":"padleft","左填充":"padleft","padright":"padright","右填充":"padright","anchorencode":"anchorencode","锚编码":"anchorencode","filepath":"filepath","文件路径":"filepath","頁面id":"pageid","页面id":"pageid","pageid":"pageid","int":"int","界面":"int","#special":"special","#特殊":"special","#speciale":"speciale","#特殊等同":"speciale","#tag":"tag","#标记":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#格式化日期":"formatdate","#日期格式化":"formatdate","#目標":"target","#目标":"target","#target":"target","#巴別":"babel","#巴别":"babel","#babel":"babel","#coordinates":"coordinates","#調動":"invoke","#调用":"invoke","#invoke":"invoke","#related":"related","#若":"if","#非空式":"if","#如果":"if","#if":"if","#相同式":"ifeq","#匹配式":"ifeq","#若相等":"ifeq","#如果相等":"ifeq","#ifeq":"ifeq","#轉換":"switch","#多选式":"switch","#多条件式": +"switch","#双射式":"switch","#开关":"switch","#转换":"switch","#switch":"switch","#存在式":"ifexist","#若有":"ifexist","#如有":"ifexist","#ifexist":"ifexist","#若表達式":"ifexpr","#若表达式":"ifexpr","#ifexpr":"ifexpr","#如果錯誤":"iferror","#错误式":"iferror","#如果错误":"iferror","#iferror":"iferror","#時間":"time","#时间":"time","#time":"time","#時間l":"timel","#时间l":"timel","#timel":"timel","#表達式":"expr","#计算式":"expr","#表达式":"expr","#expr":"expr","#rel2abs":"rel2abs","#标题组成部分":"titleparts","#titleparts":"titleparts","#分類樹":"categorytree","#分类树":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","隱藏跨語言連結":"noexternallanglinks","无外部语言连接":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#屬性":"property","#属性":"property","#property":"property","#statements" +:"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","条目路径":"articlepath","伺服器":"server","服务器":"server","server":"server","伺服器名稱":"servername","服务器名":"servername","servername":"servername","scriptpath":"scriptpath","脚本路径":"scriptpath","stylepath":"stylepath","样式路径":"stylepath","numberofwikis":"numberofwikis","wb報表名稱":"wbreponame","wb报告名":"wbreponame","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","组中用户数":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","默认排序":"defaultsort","默认排序关键字":"defaultsort","默认分类排序":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","分类中页面数":"pagesincategory","PAGESIZE":"pagesize","页面大小":"pagesize","PROTECTIONLEVEL":"protectionlevel","保护级别": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","頁面名稱":"pagename","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","页面名等同":"pagenamee","页面名称等同":"pagenamee","FULLPAGENAME":"fullpagename","页面全称":"fullpagename","完整页面名称":"fullpagename","FULLPAGENAMEE":"fullpagenamee","完整页面名称等同":"fullpagenamee","SUBPAGENAME":"subpagename","子页面名称":"subpagename","SUBPAGENAMEE":"subpagenamee","子页面名称等同":"subpagenamee","根頁面名稱":"rootpagename","ROOTPAGENAME":"rootpagename","根页面名称":"rootpagename","根頁面名稱E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","根页面名称等同":"rootpagenamee","BASEPAGENAME":"basepagename","基础页面名称":"basepagename","BASEPAGENAMEE":"basepagenamee","基础页面名称等同":"basepagenamee","TALKPAGENAME":"talkpagename","讨论页面名称":"talkpagename","对话页面名称": +"talkpagename","TALKPAGENAMEE":"talkpagenamee","讨论页面名称等同":"talkpagenamee","对话页面名称等同":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主名字空间页面名称":"subjectpagename","条目页面名称":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","主名字空间页面名称等同":"subjectpagenamee","条目页面名称等同":"subjectpagenamee","REVISIONID":"revisionid","修订ID":"revisionid","REVISIONDAY":"revisionday","修订日":"revisionday","REVISIONDAY2":"revisionday2","修订日2":"revisionday2","REVISIONMONTH":"revisionmonth","修订月":"revisionmonth","REVISIONMONTH1":"revisionmonth1","修订月1":"revisionmonth1","REVISIONYEAR":"revisionyear","修订年":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","修订时间戳":"revisiontimestamp","修訂使用者":"revisionuser","REVISIONUSER":"revisionuser","修订用户":"revisionuser", +"CASCADINGSOURCES":"cascadingsources","级联来源":"cascadingsources","命名空間":"namespace","名字空间":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","名字空间等同":"namespacee","命名空間數":"namespacenumber","名字空间编号":"namespacenumber","NAMESPACENUMBER":"namespacenumber","對話空間":"talkspace","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","讨论空间等同":"talkspacee","讨论名字空间等同":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主名字空间":"subjectspace","条目名字空间":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","主名字空间等同":"subjectspacee","条目名字空间等同":"subjectspacee","文章數":"numberofarticles","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","檔案數":"numberoffiles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles", +"使用者人數量":"numberofusers","用户数":"numberofusers","NUMBEROFUSERS":"numberofusers","活躍使用者人數":"numberofactiveusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","頁面數":"numberofpages","页面数":"numberofpages","NUMBEROFPAGES":"numberofpages","管理員數":"numberofadmins","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","编辑数":"numberofedits","顯示標題":"displaytitle","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","本月1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","本月名":"currentmonthname","本月名称":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月名属格":"currentmonthnamegen","本月名称属格":"currentmonthnamegen","本月縮寫": +"currentmonthabbrev","本月简称":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","今天2":"currentday2","CURRENTDAYNAME":"currentdayname","星期":"currentdayname","今天名":"currentdayname","今天名称":"currentdayname","CURRENTYEAR":"currentyear","今年":"currentyear","目前時間":"currenttime","当前时间":"currenttime","此时":"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","当前小时":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","本地月":"localmonth","本地月2":"localmonth","LOCALMONTH1":"localmonth1","本地月1":"localmonth1","LOCALMONTHNAME":"localmonthname","本地月份名":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","本地月历":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","本地月缩写":"localmonthabbrev","LOCALDAY":"localday","本地日":"localday","LOCALDAY2":"localday2","本地日2": +"localday2","LOCALDAYNAME":"localdayname","本地日名":"localdayname","LOCALYEAR":"localyear","本地年":"localyear","LOCALTIME":"localtime","本地时间":"localtime","LOCALHOUR":"localhour","本地小时":"localhour","網站名稱":"sitename","站点名称":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","本周":"currentweek","CURRENTDOW":"currentdow","当前DOW":"currentdow","LOCALWEEK":"localweek","本地周":"localweek","LOCALDOW":"localdow","本地DOW":"localdow","REVISIONSIZE":"revisionsize","修订大小":"revisionsize","目前版本":"currentversion","当前版本":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","当前时间戳":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","本地时间戳":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","方向标记":"directionmark","內容語言":"contentlanguage","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^()(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-qu.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-qu.json new file mode 100644 index 0000000..92b25aa --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-qu.json @@ -0,0 +1,19 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__yuyarinannaq__":"notoc","__notdc__":"notoc","__sin_tdc__":"notoc","__notoc__":"notoc","__rikchasuyunnaq__":"nogallery","__nogalería__":"nogallery","__nogaleria__":"nogallery","__sin_galería__":"nogallery","__nogallery__":"nogallery","__yuyarinataatipachiy__":"forcetoc","__forzartdc__":"forcetoc","__forzartoc__": +"forcetoc","__forzar_tdc__":"forcetoc","__forcetoc__":"forcetoc","__yuyarina__":"toc","__tdc__":"toc","__toc__":"toc","__amarakitahukchaychu__":"noeditsection","__noeditarsección__":"noeditsection","__noeditarseccion__":"noeditsection","__no_editar_sección__":"noeditsection","__noeditsection__":"noeditsection","__amasutitahukchaychu__":"notitleconvert","__noconvertirtitulo__":"notitleconvert","__noconvertirtítulo__":"notitleconvert","__noct___":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__amasamiqtahukchaychu__":"nocontentconvert","__noconvertircontenido__":"nocontentconvert","__nocc___":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__VINCULARANUEVASECCION__":"newsectionlink","__ENLACECREARSECCIÓN__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NOVINCULARANUEVASECCION__":"nonewsectionlink","__SINENLACECREARSECCIÓN__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink", +"__PAKASQAKATIGURIYA__":"hiddencat","__CATEGORÍAOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__UNANCHAY__":"index","__INDEXAR__":"index","__INDEX__":"index","__AMAUNANCHAYCHU__":"noindex","__NOINDEXAR__":"noindex","__NOINDEX__":"noindex","__TIYAQLLAPUSAPUNA__":"staticredirect","__REDIRECCIONESTATICA__":"staticredirect","__REDIRECCIÓNESTÁTICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIGUACION__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"skiti":"ns","en":"ns","ns":"ns","nse":"nse","urlllawichay":"urlencode","url-llawichay":"urlencode","codificar":"urlencode","codificarurl":"urlencode","urlencode":"urlencode","uchuyñawpaq":"lcfirst","uchuynawpaq":"lcfirst","primerominus;":"lcfirst","primerominús":"lcfirst","primerominus":"lcfirst","lcfirst":"lcfirst","hatunñawpaq":"ucfirst","hatunnawpaq": +"ucfirst","primeromayus;":"ucfirst","primeromayús":"ucfirst","primeromayus":"ucfirst","ucfirst":"ucfirst","uchuy":"lc","minus":"lc","minús":"lc","lc":"lc","hatun":"uc","mayus":"uc","mayús":"uc","uc":"uc","kayllaurl":"localurl","urllocal":"localurl","localurl":"localurl","kayllaurle":"localurle","urllocalc":"localurle","localurle":"localurle","huntaurl":"fullurl","urlcompleta":"fullurl","fullurl":"fullurl","huntaurle":"fullurle","urlcompletac":"fullurle","fullurle":"fullurle","urlcanonica":"canonicalurl","canonicalurl":"canonicalurl","urlcanonicac":"canonicalurle","canonicalurle":"canonicalurle","yupayrikchakuy":"formatnum","formatonúmero":"formatnum","formatonumero":"formatnum","formatnum":"formatnum","simikamachiy":"grammar","gramatica":"grammar","gramática":"grammar","grammar":"grammar","qhariwarmi":"gender","qariwarmi":"gender","género":"gender","genero":"gender","gender":"gender","achka":"plural","plural":"plural","bidi":"bidi","#rimay":"language","#idioma":"language", +"#language":"language","padlluqi":"padleft","padichuq":"padleft","padleft":"padleft","rellenarizquierda":"padleft","rellenarizq":"padleft","padpaña":"padright","padalliq":"padright","padright":"padright","rellenarderecha":"padright","rellenarder":"padright","watanallawichay":"anchorencode","anchorencode":"anchorencode","willañiqiñan":"filepath","willaniqinan":"filepath","rutaarchivo":"filepath","rutarchivo":"filepath","rutadearchivo":"filepath","filepath":"filepath","iddepágina":"pageid","idpágina":"pageid","iddepagina":"pageid","idpagina":"pageid","pageid":"pageid","willay":"int","int":"int","#sapaq":"special","#especial":"special","#special":"special","#especialc":"speciale","#speciale":"speciale","#unanchacha":"tag","#etiqueta":"tag","#tag":"tag","#punchawrikchakuy":"formatdate","#formatodefecha":"formatdate","#formatearfecha":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#destino":"target","#target":"target","#babel":"babel","#coordinates":"coordinates", +"#invocar":"invoke","#invoke":"invoke","#related":"related","#kaptin":"if","#si":"if","#if":"if","#kaqllakaptin":"ifeq","#siigual":"ifeq","#ifeq":"ifeq","#winkuy":"switch","#según":"switch","#switch":"switch","#kachkaptin":"ifexist","#siexiste":"ifexist","#ifexist":"ifexist","#rikuchiykaptin":"ifexpr","#siexpr":"ifexpr","#ifexpr":"ifexpr","#pantasqakaptin":"iferror","#sierror":"iferror","#iferror":"iferror","#pacha":"time","#tiempo":"time","#time":"time","#pachal":"timel","#tiempol":"timel","#timel":"timel","#rikuchiy":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#árboldecategorías":"categorytree","#arboldecategorias":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","nointerwikis":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propiedad":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList", +"#mentor":"mentor","rutaartículo":"articlepath","rutaarticulo":"articlepath","articlepath":"articlepath","sirwiq":"server","servidor":"server","server":"server","sirwiqsuti":"servername","nombreservidor":"servername","servername":"servername","qillqañan":"scriptpath","qillqanan":"scriptpath","rutascript":"scriptpath","rutadescript":"scriptpath","scriptpath":"scriptpath","rutaestilo":"stylepath","rutadeestilo":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"HUÑUPIYUPAY":"numberingroup","HUNUPIYUPAY":"numberingroup","NÚMEROENGRUPO":"numberingroup","NUMEROENGRUPO":"numberingroup","NUMENGRUPO":"numberingroup","NÚMENGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ALLINCHAY":"defaultsort","SIQINCHAY":"defaultsort","ORDENAR":"defaultsort","ORDENPREDETERMINADO":"defaultsort","CLAVEDEORDENPREDETERMINADO":"defaultsort","ORDENDECATEGORIAPREDETERMINADO":"defaultsort","ORDENDECATEGORÍAPREDETERMINADO": +"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","KATIGURIYAPIPANQAKUNA":"pagesincategory","PÁGINASENCATEGORÍA":"pagesincategory","PÁGINASENCAT":"pagesincategory","PAGSENCAT":"pagesincategory","PAGINASENCATEGORIA":"pagesincategory","PAGINASENCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PANQACHHIKAN":"pagesize","PANQACHIKAN":"pagesize","PANQACHIKA":"pagesize","TAMAÑOPÁGINA":"pagesize","TAMAÑODEPÁGINA":"pagesize","TAMAÑOPAGINA":"pagesize","TAMAÑODEPAGINA":"pagesize","PAGESIZE":"pagesize","HAYKAAMACHAY":"protectionlevel","IMASINCHIAMACHAY":"protectionlevel","NIVELDEPROTECCIÓN":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","NIVELDEPROTECCION":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PANQASUTI":"pagename","NOMBREDEPAGINA":"pagename","NOMBREDEPÁGINA":"pagename","PAGENAME":"pagename","PANQASUTIE":"pagenamee","NOMBREDEPAGINAC":"pagenamee", +"NOMBREDEPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","HUNTAPANQASUTI":"fullpagename","NOMBREDEPÁGINACOMPLETA":"fullpagename","NOMBREDEPAGINACOMPLETA":"fullpagename","NOMBREDEPÁGINAENTERA":"fullpagename","NOMBREDEPAGINAENTERA":"fullpagename","NOMBRECOMPLETODEPÁGINA":"fullpagename","NOMBRECOMPLETODEPAGINA":"fullpagename","FULLPAGENAME":"fullpagename","HUNTAPANQASUTIE":"fullpagenamee","NOMBRECOMPLETODEPAGINAC":"fullpagenamee","NOMBRECOMPLETODEPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","URINPANQASUTI":"subpagename","NOMBREDESUBPAGINA":"subpagename","NOMBREDESUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename","URINPANQASUTIE":"subpagenamee","NOMBREDESUBPAGINAC":"subpagenamee","NOMBREDESUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMBREDEPAGINARAIZ":"rootpagename","NOMBREDEPÁGINARAÍZ":"rootpagename","ROOTPAGENAME":"rootpagename","NOMBREDEPAGINARAIZC":"rootpagenamee","NOMBREDEPÁGINARAÍZC":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","TIKSIPANQASUTI": +"basepagename","NOMBREDEPAGINABASE":"basepagename","NOMBREDEPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","TIKSIPANQASUTIE":"basepagenamee","NOMBREDEPAGINABASEC":"basepagenamee","NOMBREDEPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","RIMANAKUYPANQASUTI":"talkpagename","NOMBREDEPÁGINADEDISCUSIÓN":"talkpagename","NOMBREDEPAGINADEDISCUSION":"talkpagename","NOMBREDEPAGINADISCUSION":"talkpagename","NOMBREDEPÁGINADISCUSIÓN":"talkpagename","TALKPAGENAME":"talkpagename","RIMANAKUYPANQASUTIE":"talkpagenamee","NOMBREDEPÁGINADEDISCUSIÓNC":"talkpagenamee","NOMBREDEPAGINADEDISCUSIONC":"talkpagenamee","NOMBREDEPAGINADISCUSIONC":"talkpagenamee","NOMBREDEPÁGINADISCUSIÓNC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","QILLQAPANQASUTI":"subjectpagename","NOMBREDEPAGINADETEMA":"subjectpagename","NOMBREDEPÁGINADETEMA":"subjectpagename","NOMBREDEPÁGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEASUNTO":"subjectpagename","NOMBREDEPAGINADEARTICULO":"subjectpagename", +"NOMBREDEPÁGINADEARTÍCULO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","QILLQAPANQASUTIE":"subjectpagenamee","NOMBREDEPAGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADETEMAC":"subjectpagenamee","NOMBREDEPÁGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEASUNTOC":"subjectpagenamee","NOMBREDEPAGINADEARTICULOC":"subjectpagenamee","NOMBREDEPÁGINADEARTÍCULOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","MUSUQCHASQAID":"revisionid","IDDEREVISION":"revisionid","IDREVISION":"revisionid","IDDEREVISIÓN":"revisionid","IDREVISIÓN":"revisionid","REVISIONID":"revisionid","MUSUQCHASQAPUNCHAW":"revisionday","DIADEREVISION":"revisionday","DIAREVISION":"revisionday","DÍADEREVISIÓN":"revisionday","DÍAREVISIÓN":"revisionday","REVISIONDAY":"revisionday","MUSUQCHASQAPUNCHAW2":"revisionday2","DIADEREVISION2":"revisionday2","DIAREVISION2":"revisionday2","DÍADEREVISIÓN2":"revisionday2", +"DÍAREVISIÓN2":"revisionday2","REVISIONDAY2":"revisionday2","MUSUQCHASQAKILLA":"revisionmonth","MESDEREVISION":"revisionmonth","MESDEREVISIÓN":"revisionmonth","MESREVISION":"revisionmonth","MESREVISIÓN":"revisionmonth","REVISIONMONTH":"revisionmonth","MESDEREVISION1":"revisionmonth1","MESDEREVISIÓN1":"revisionmonth1","MESREVISION1":"revisionmonth1","MESREVISIÓN1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","MUSUQCHASQAWATA":"revisionyear","AÑODEREVISION":"revisionyear","AÑODEREVISIÓN":"revisionyear","AÑOREVISION":"revisionyear","AÑOREVISIÓN":"revisionyear","REVISIONYEAR":"revisionyear","MUSUQCHASQAPACHAQILLPA":"revisiontimestamp","MARCADEHORADEREVISION":"revisiontimestamp","MARCADEHORADEREVISIÓN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","MUSUQCHASQARURAQ":"revisionuser","USUARIODEREVISION":"revisionuser","USUARIODEREVISIÓN":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","SUTIKITI":"namespace","ESPACIODENOMBRE": +"namespace","NAMESPACE":"namespace","SUTIKITIE":"namespacee","ESPACIODENOMBREC":"namespacee","NAMESPACEE":"namespacee","NÚMERODELESPACIO":"namespacenumber","NAMESPACENUMBER":"namespacenumber","RIMANAKUYKITI":"talkspace","RIMAYKITI":"talkspace","ESPACIODEDISCUSION":"talkspace","ESPACIODEDISCUSIÓN":"talkspace","TALKSPACE":"talkspace","RIMANAKUYKITIE":"talkspacee","RIMAYKITIE":"talkspacee","ESPACIODEDISCUSIONC":"talkspacee","TALKSPACEE":"talkspacee","QILLQAKITI":"subjectspace","ESPACIODEASUNTO":"subjectspace","ESPACIODETEMA":"subjectspace","ESPACIODEARTÍCULO":"subjectspace","ESPACIODEARTICULO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","QILLQAKITIE":"subjectspacee","ESPACIODETEMAC":"subjectspacee","ESPACIODEASUNTOC":"subjectspacee","ESPACIODEARTICULOC":"subjectspacee","ESPACIODEARTÍCULOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","HAYKAQILLQA":"numberofarticles","NÚMERODEARTÍCULOS":"numberofarticles", +"NUMERODEARTICULOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","HAYKAWILLANIQI":"numberoffiles","NÚMERODEARCHIVOS":"numberoffiles","NUMERODEARCHIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","HAYKARURAQ":"numberofusers","NÚMERODEUSUARIOS":"numberofusers","NUMERODEUSUARIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","HAYKARURACHKAQ":"numberofactiveusers","NÚMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMERODEUSUARIOSACTIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","HAYKAPANQA":"numberofpages","NÚMERODEPÁGINAS":"numberofpages","NUMERODEPAGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","HAYKAKAMACHIQ":"numberofadmins","NÚMEROADMINISITRADORES":"numberofadmins","NÚMEROADMINS":"numberofadmins","NUMEROADMINS":"numberofadmins","NUMEROADMINISTRADORES":"numberofadmins","NUMERODEADMINISTRADORES":"numberofadmins","NUMERODEADMINS":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINS":"numberofadmins", +"NÚMEROADMINIISTRADORES":"numberofadmins","NUMBEROFADMINS":"numberofadmins","HAYKALLAMKAPUSQA":"numberofedits","NÚMERODEEDICIONES":"numberofedits","NUMERODEEDICIONES":"numberofedits","NUMBEROFEDITS":"numberofedits","SUTITARIKUCHIY":"displaytitle","MOSTRARTÍTULO":"displaytitle","MOSTRARTITULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","KUNANKILLA":"currentmonth","MESACTUAL":"currentmonth","MES_ACTUAL":"currentmonth","MESACTUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESACTUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","KUNANKILLASUTI":"currentmonthname","NOMBREMESACTUAL":"currentmonthname","NOMBRE_MES_ACTUAL":"currentmonthname","MESACTUALCOMPLETO":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","KUNANKILLASUTIP":"currentmonthnamegen","GENERADORNOMBREMESACTUAL":"currentmonthnamegen","MESACTUALGENITIVO":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","KUNANKILLAPISI":"currentmonthabbrev", +"ABREVIACIONNOMBREMESACTUAL":"currentmonthabbrev","ABREVIACIÓNNOMBREMESACTUAL":"currentmonthabbrev","MESACTUALABREVIADO":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","KUNANPUNCHAW":"currentday","DÍAACTUAL":"currentday","DIAACTUAL":"currentday","DÍA_ACTUAL":"currentday","DIA_ACTUAL":"currentday","CURRENTDAY":"currentday","KUNANPUNCHAW2":"currentday2","DÍAACTUAL2":"currentday2","DIAACTUAL2":"currentday2","DÍA_ACTUAL2":"currentday2","DIA_ACTUAL2":"currentday2","CURRENTDAY2":"currentday2","KUNANPUNCHAWSUTI":"currentdayname","NOMBREDÍAACTUAL":"currentdayname","NOMBREDIAACTUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","KUNANWATA":"currentyear","AÑOACTUAL":"currentyear","AÑO_ACTUAL":"currentyear","CURRENTYEAR":"currentyear","KUNANPACHA":"currenttime","HORAACTUAL":"currenthour","HORA_ACTUAL":"currenthour","HORA_MINUTOS_ACTUAL":"currenttime","HORAMINUTOSACTUAL":"currenttime","TIEMPOACTUAL":"currenttime","CURRENTTIME":"currenttime","KUNANURA":"currenthour", +"CURRENTHOUR":"currenthour","KAYPIKILLA":"localmonth","MESLOCAL":"localmonth","MESLOCAL2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","KAYPIKILLASUTI":"localmonthname","NOMBREMESLOCAL":"localmonthname","MESLOCALCOMPLETO":"localmonthname","LOCALMONTHNAME":"localmonthname","KAYPIKILLASUTIP":"localmonthnamegen","GENERADORNOMBREMESLOCAL":"localmonthnamegen","MESLOCALGENITIVO":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","KAYPIKILLAPISI":"localmonthabbrev","ABREVIACIONMESLOCAL":"localmonthabbrev","MESLOCALABREVIADO":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","KAYPIPUNCHAW":"localday","DÍALOCAL":"localday","DIALOCAL":"localday","LOCALDAY":"localday","KAYPIPUNCHAW2":"localday2","DIALOCAL2":"localday2","DÍALOCAL2":"localday2","LOCALDAY2":"localday2","KAYPIPUNCHAWSUTI":"localdayname","NOMBREDIALOCAL":"localdayname","NOMBREDÍALOCAL":"localdayname","LOCALDAYNAME":"localdayname","KAYPIWATA": +"localyear","AÑOLOCAL":"localyear","LOCALYEAR":"localyear","KAYPIPACHA":"localtime","HORALOCAL":"localhour","HORAMINUTOSLOCAL":"localtime","TIEMPOLOCAL":"localtime","LOCALTIME":"localtime","KAYPIURA":"localhour","LOCALHOUR":"localhour","TIYAYSUTI":"sitename","NOMBREDESITIO":"sitename","NOMBREDELSITIO":"sitename","SITENAME":"sitename","KUNANSIMANA":"currentweek","SEMANAACTUAL":"currentweek","CURRENTWEEK":"currentweek","KUNANSIMANAPUNCHAW":"currentdow","DDSACTUAL":"currentdow","DIADESEMANAACTUAL":"currentdow","DÍADESEMANAACTUAL":"currentdow","CURRENTDOW":"currentdow","KAYLLASIMANA":"localweek","SEMANALOCAL":"localweek","LOCALWEEK":"localweek","KAYLLASIMANAPUNCHAW":"localdow","DDSLOCAL":"localdow","DIADESEMANALOCAL":"localdow","DÍADESEMANALOCAL":"localdow","LOCALDOW":"localdow","TAMAÑODEREVISIÓN":"revisionsize","TAMAÑODEREVISION":"revisionsize","REVISIONSIZE":"revisionsize","KUNANMUSUQCHASQA":"currentversion","REVISIÓNACTUAL":"currentversion","VERSIONACTUAL":"currentversion", +"VERSIÓNACTUAL":"currentversion","CURRENTVERSION":"currentversion","KUNANPACHAQILLPA":"currenttimestamp","MARCADEHORAACTUAL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","KAYLLAPACHAQILLPA":"localtimestamp","MARCADEHORALOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","PURIRIYSANANCHA":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","SAMIQRIMAY":"contentlanguage","IDIOMADELCONTENIDO":"contentlanguage","IDIOMADELCONT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záéíóúñ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rm.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rm.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rm.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rmy.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rmy.json new file mode 100644 index 0000000..2dab473 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rmy.json @@ -0,0 +1,10 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__faracuprins__":"notoc","__notoc__":"notoc","__faragalerie__":"nogallery","__nogallery__":"nogallery","__forteazacuprins__":"forcetoc","__forcetoc__":"forcetoc","__cuprins__":"toc","__toc__":"toc","__faraeditsectiune__":"noeditsection","__noeditsection__":"noeditsection","__faraconvertiretitlu__":"notitleconvert","__fct__": +"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__faraconvertirecontinut__":"nocontentconvert","__fcc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LEGATURASECTIUNENOUA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__FARALEGATURASECTIUNENOUA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__ASCUNDECAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__FARAINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONARESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"sn":"ns","ns":"ns","nse":"nse","codificareurl":"urlencode","urlencode":"urlencode","minusculaprima":"lcfirst","lcfirst":"lcfirst","majusculaprima":"ucfirst","ucfirst":"ucfirst","minuscula":"lc","lc":"lc","majuscula":"uc","uc":"uc","urllocal": +"localurl","localurl":"localurl","urllocale":"localurle","localurle":"localurle","urlcomplet":"fullurl","fullurl":"fullurl","urlcomplete":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnr":"formatnum","formatnum":"formatnum","gramatica":"grammar","grammar":"grammar","gen":"gender","gender":"gender","plural":"plural","bidi":"bidi","#limba":"language","#language":"language","padleft":"padleft","padright":"padright","codificareancora":"anchorencode","anchorencode":"anchorencode","caleafisierului":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#eticheta":"tag","#tag":"tag","#formatdata":"formatdate","#dataformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#arborecategorie":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq", +"#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","numeserver":"servername","servername":"servername","calescript":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMARDEPAGINI":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMARDEUTILIZATORI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMARDEUTILIZATORIACTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMARDEARTICOLE":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMARDEFISIERE":"numberoffiles","NUMBEROFFILES": +"numberoffiles","NUMARADMINI":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMARINGRUP":"numberingroup","NUMINGRUP":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMARDEMODIFICARI":"numberofedits","NUMBEROFEDITS":"numberofedits","SORTAREIMPLICITA":"defaultsort","CHEIESORTAREIMPLICITA":"defaultsort","CATEGORIESORTAREIMPLICITA":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINIINCATEGORIE":"pagesincategory","PAGINIINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","MARIMEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELPROTECTIE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SPATIUUDENUME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","SPATIUDEDISCUTIE":"talkspace","TALKSPACE":"talkspace","SPATIUUDEDISCUTIE":"talkspacee","TALKSPACEE":"talkspacee","SPATIUSUBIECT": +"subjectspace","SPATIUARTICOL":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SPATIUUSUBIECT":"subjectspacee","SPATIUUARTICOL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMEPAGINA":"pagename","PAGENAME":"pagename","NUMEEPAGINA":"pagenamee","PAGENAMEE":"pagenamee","NUMEPAGINACOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NUMEEPAGINACOMPLET":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","NUMEDEBAZAPAGINA":"basepagename","BASEPAGENAME":"basepagename","NUMEEDEBAZAPAGINA":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NUMESUBPAGINA":"subpagename","SUBPAGENAME":"subpagename","NUMEESUBPAGINA":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NUMEPAGINADEDISCUTIE":"talkpagename","TALKPAGENAME":"talkpagename","NUMEEPAGINADEDISCUTIE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NUMEPAGINASUBIECT":"subjectpagename","NUMEPAGINAARTICOL":"subjectpagename", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NUMEEPAGINASUBIECT":"subjectpagenamee","NUMEEPAGINAARTICOL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVIZIE":"revisionid","REVISIONID":"revisionid","ZIREVIZIE":"revisionday","REVISIONDAY":"revisionday","ZIREVIZIE2":"revisionday2","REVISIONDAY2":"revisionday2","LUNAREVIZIE":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ANREVIZIE":"revisionyear","REVISIONYEAR":"revisionyear","STAMPILATIMPREVIZIE":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILIZATORREVIZIE":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","SPATIUDENUME":"namespace","NAMESPACE":"namespace","ARATATITLU":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","NUMARLUNACURENTA":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","LUNACURENTA1":"currentmonth1","CURRENTMONTH1": +"currentmonth1","NUMELUNACURENTA":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NUMELUNACURENTAGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","LUNACURENTAABREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","NUMARZIUACURENTA":"currentday","CURRENTDAY":"currentday","NUMARZIUACURENTA2":"currentday2","CURRENTDAY2":"currentday2","NUMEZIUACURENTA":"currentdayname","CURRENTDAYNAME":"currentdayname","ANULCURENT":"currentyear","CURRENTYEAR":"currentyear","TIMPULCURENT":"currenttime","CURRENTTIME":"currenttime","ORACURENTA":"currenthour","CURRENTHOUR":"currenthour","LUNALOCALA":"localmonth","LUNALOCALA2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LUNALOCALA1":"localmonth1","LOCALMONTH1":"localmonth1","NUMELUNALOCALA":"localmonthname","LOCALMONTHNAME":"localmonthname","NUMELUNALOCALAGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LUNALOCALAABREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev" +,"ZIUALOCALA":"localday","LOCALDAY":"localday","ZIUALOCALA2":"localday2","LOCALDAY2":"localday2","NUMEZIUALOCALA":"localdayname","LOCALDAYNAME":"localdayname","ANULLOCAL":"localyear","LOCALYEAR":"localyear","TIMPULLOCAL":"localtime","LOCALTIME":"localtime","ORALOCALA":"localhour","LOCALHOUR":"localhour","NUMESITE":"sitename","SITENAME":"sitename","SAPTAMANACURENTA":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SAPTAMANALOCALA":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIUNECURENTA":"currentversion","CURRENTVERSION":"currentversion","STAMPILATIMPCURENT":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STAMPILATIMPLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","SEMNDIRECTIE":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LIMBACONTINUT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zăâîşţșțĂÂÎŞŢȘȚ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rn.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rn.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ro.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ro.json new file mode 100644 index 0000000..b819ec7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ro.json @@ -0,0 +1,10 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__faracuprins__":"notoc","__notoc__":"notoc","__faragalerie__":"nogallery","__nogallery__":"nogallery","__forteazacuprins__":"forcetoc","__forcetoc__":"forcetoc","__cuprins__":"toc","__toc__":"toc","__faraeditsectiune__":"noeditsection","__noeditsection__":"noeditsection","__faraconvertiretitlu__":"notitleconvert","__fct__": +"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__faraconvertirecontinut__":"nocontentconvert","__fcc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LEGATURASECTIUNENOUA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__FARALEGATURASECTIUNENOUA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__ASCUNDECAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__FARAINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONARESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"sn":"ns","ns":"ns","nse":"nse","codificareurl":"urlencode","urlencode":"urlencode","minusculaprima":"lcfirst","lcfirst":"lcfirst","majusculaprima":"ucfirst","ucfirst":"ucfirst","minuscula": +"lc","lc":"lc","majuscula":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","urllocale":"localurle","localurle":"localurle","urlcomplet":"fullurl","fullurl":"fullurl","urlcomplete":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnr":"formatnum","formatnum":"formatnum","gramatica":"grammar","grammar":"grammar","gen":"gender","gender":"gender","plural":"plural","bidi":"bidi","#limba":"language","#language":"language","padleft":"padleft","padright":"padright","codificareancora":"anchorencode","anchorencode":"anchorencode","caleafisierului":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#eticheta":"tag","#tag":"tag","#formatdata":"formatdate","#dataformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch": +"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#arborecategorie":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","numeserver":"servername","servername":"servername","calescript":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMARINGRUP":"numberingroup","NUMINGRUP":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTAREIMPLICITA":"defaultsort","CHEIESORTAREIMPLICITA":"defaultsort","CATEGORIESORTAREIMPLICITA":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY": +"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINIINCATEGORIE":"pagesincategory","PAGINIINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","MARIMEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELPROTECTIE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NUMEPAGINA":"pagename","PAGENAME":"pagename","NUMEEPAGINA":"pagenamee","PAGENAMEE":"pagenamee","NUMEPAGINACOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NUMEEPAGINACOMPLET":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NUMESUBPAGINA":"subpagename","SUBPAGENAME":"subpagename","NUMEESUBPAGINA":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","NUMEDEBAZAPAGINA":"basepagename","BASEPAGENAME":"basepagename","NUMEEDEBAZAPAGINA":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NUMEPAGINADEDISCUTIE":"talkpagename","TALKPAGENAME":"talkpagename","NUMEEPAGINADEDISCUTIE":"talkpagenamee", +"TALKPAGENAMEE":"talkpagenamee","NUMEPAGINASUBIECT":"subjectpagename","NUMEPAGINAARTICOL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NUMEEPAGINASUBIECT":"subjectpagenamee","NUMEEPAGINAARTICOL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVIZIE":"revisionid","REVISIONID":"revisionid","ZIREVIZIE":"revisionday","REVISIONDAY":"revisionday","ZIREVIZIE2":"revisionday2","REVISIONDAY2":"revisionday2","LUNAREVIZIE":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ANREVIZIE":"revisionyear","REVISIONYEAR":"revisionyear","STAMPILATIMPREVIZIE":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILIZATORREVIZIE":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","SPATIUDENUME":"namespace","NAMESPACE":"namespace","SPATIUUDENUME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","SPATIUDEDISCUTIE": +"talkspace","TALKSPACE":"talkspace","SPATIUUDEDISCUTIE":"talkspacee","TALKSPACEE":"talkspacee","SPATIUSUBIECT":"subjectspace","SPATIUARTICOL":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SPATIUUSUBIECT":"subjectspacee","SPATIUUARTICOL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMARDEARTICOLE":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMARDEFISIERE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMARDEUTILIZATORI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMARDEUTILIZATORIACTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMARDEPAGINI":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMARADMINI":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMARDEMODIFICARI":"numberofedits","NUMBEROFEDITS":"numberofedits","ARATATITLU":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","NUMARLUNACURENTA":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2": +"currentmonth","LUNACURENTA1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NUMELUNACURENTA":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NUMELUNACURENTAGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","LUNACURENTAABREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","NUMARZIUACURENTA":"currentday","CURRENTDAY":"currentday","NUMARZIUACURENTA2":"currentday2","CURRENTDAY2":"currentday2","NUMEZIUACURENTA":"currentdayname","CURRENTDAYNAME":"currentdayname","ANULCURENT":"currentyear","CURRENTYEAR":"currentyear","TIMPULCURENT":"currenttime","CURRENTTIME":"currenttime","ORACURENTA":"currenthour","CURRENTHOUR":"currenthour","LUNALOCALA":"localmonth","LUNALOCALA2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LUNALOCALA1":"localmonth1","LOCALMONTH1":"localmonth1","NUMELUNALOCALA":"localmonthname","LOCALMONTHNAME":"localmonthname","NUMELUNALOCALAGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen", +"LUNALOCALAABREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ZIUALOCALA":"localday","LOCALDAY":"localday","ZIUALOCALA2":"localday2","LOCALDAY2":"localday2","NUMEZIUALOCALA":"localdayname","LOCALDAYNAME":"localdayname","ANULLOCAL":"localyear","LOCALYEAR":"localyear","TIMPULLOCAL":"localtime","LOCALTIME":"localtime","ORALOCALA":"localhour","LOCALHOUR":"localhour","NUMESITE":"sitename","SITENAME":"sitename","SAPTAMANACURENTA":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SAPTAMANALOCALA":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIUNECURENTA":"currentversion","CURRENTVERSION":"currentversion","STAMPILATIMPCURENT":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STAMPILATIMPLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","SEMNDIRECTIE":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LIMBACONTINUT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage", +"CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zăâîşţșțĂÂÎŞŢȘȚ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-roa-rup.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-roa-rup.json new file mode 100644 index 0000000..b819ec7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-roa-rup.json @@ -0,0 +1,10 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__faracuprins__":"notoc","__notoc__":"notoc","__faragalerie__":"nogallery","__nogallery__":"nogallery","__forteazacuprins__":"forcetoc","__forcetoc__":"forcetoc","__cuprins__":"toc","__toc__":"toc","__faraeditsectiune__":"noeditsection","__noeditsection__":"noeditsection","__faraconvertiretitlu__":"notitleconvert","__fct__": +"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__faraconvertirecontinut__":"nocontentconvert","__fcc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LEGATURASECTIUNENOUA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__FARALEGATURASECTIUNENOUA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__ASCUNDECAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__FARAINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONARESTATICA__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"sn":"ns","ns":"ns","nse":"nse","codificareurl":"urlencode","urlencode":"urlencode","minusculaprima":"lcfirst","lcfirst":"lcfirst","majusculaprima":"ucfirst","ucfirst":"ucfirst","minuscula": +"lc","lc":"lc","majuscula":"uc","uc":"uc","urllocal":"localurl","localurl":"localurl","urllocale":"localurle","localurle":"localurle","urlcomplet":"fullurl","fullurl":"fullurl","urlcomplete":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnr":"formatnum","formatnum":"formatnum","gramatica":"grammar","grammar":"grammar","gen":"gender","gender":"gender","plural":"plural","bidi":"bidi","#limba":"language","#language":"language","padleft":"padleft","padright":"padright","codificareancora":"anchorencode","anchorencode":"anchorencode","caleafisierului":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#eticheta":"tag","#tag":"tag","#formatdata":"formatdate","#dataformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch": +"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#arborecategorie":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","numeserver":"servername","servername":"servername","calescript":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMARINGRUP":"numberingroup","NUMINGRUP":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTAREIMPLICITA":"defaultsort","CHEIESORTAREIMPLICITA":"defaultsort","CATEGORIESORTAREIMPLICITA":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY": +"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINIINCATEGORIE":"pagesincategory","PAGINIINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","MARIMEPAGINA":"pagesize","PAGESIZE":"pagesize","NIVELPROTECTIE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NUMEPAGINA":"pagename","PAGENAME":"pagename","NUMEEPAGINA":"pagenamee","PAGENAMEE":"pagenamee","NUMEPAGINACOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NUMEEPAGINACOMPLET":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NUMESUBPAGINA":"subpagename","SUBPAGENAME":"subpagename","NUMEESUBPAGINA":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","NUMEDEBAZAPAGINA":"basepagename","BASEPAGENAME":"basepagename","NUMEEDEBAZAPAGINA":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NUMEPAGINADEDISCUTIE":"talkpagename","TALKPAGENAME":"talkpagename","NUMEEPAGINADEDISCUTIE":"talkpagenamee", +"TALKPAGENAMEE":"talkpagenamee","NUMEPAGINASUBIECT":"subjectpagename","NUMEPAGINAARTICOL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NUMEEPAGINASUBIECT":"subjectpagenamee","NUMEEPAGINAARTICOL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVIZIE":"revisionid","REVISIONID":"revisionid","ZIREVIZIE":"revisionday","REVISIONDAY":"revisionday","ZIREVIZIE2":"revisionday2","REVISIONDAY2":"revisionday2","LUNAREVIZIE":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ANREVIZIE":"revisionyear","REVISIONYEAR":"revisionyear","STAMPILATIMPREVIZIE":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILIZATORREVIZIE":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","SPATIUDENUME":"namespace","NAMESPACE":"namespace","SPATIUUDENUME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","SPATIUDEDISCUTIE": +"talkspace","TALKSPACE":"talkspace","SPATIUUDEDISCUTIE":"talkspacee","TALKSPACEE":"talkspacee","SPATIUSUBIECT":"subjectspace","SPATIUARTICOL":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SPATIUUSUBIECT":"subjectspacee","SPATIUUARTICOL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMARDEARTICOLE":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMARDEFISIERE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMARDEUTILIZATORI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMARDEUTILIZATORIACTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMARDEPAGINI":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMARADMINI":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMARDEMODIFICARI":"numberofedits","NUMBEROFEDITS":"numberofedits","ARATATITLU":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","NUMARLUNACURENTA":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2": +"currentmonth","LUNACURENTA1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NUMELUNACURENTA":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NUMELUNACURENTAGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","LUNACURENTAABREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","NUMARZIUACURENTA":"currentday","CURRENTDAY":"currentday","NUMARZIUACURENTA2":"currentday2","CURRENTDAY2":"currentday2","NUMEZIUACURENTA":"currentdayname","CURRENTDAYNAME":"currentdayname","ANULCURENT":"currentyear","CURRENTYEAR":"currentyear","TIMPULCURENT":"currenttime","CURRENTTIME":"currenttime","ORACURENTA":"currenthour","CURRENTHOUR":"currenthour","LUNALOCALA":"localmonth","LUNALOCALA2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LUNALOCALA1":"localmonth1","LOCALMONTH1":"localmonth1","NUMELUNALOCALA":"localmonthname","LOCALMONTHNAME":"localmonthname","NUMELUNALOCALAGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen", +"LUNALOCALAABREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ZIUALOCALA":"localday","LOCALDAY":"localday","ZIUALOCALA2":"localday2","LOCALDAY2":"localday2","NUMEZIUALOCALA":"localdayname","LOCALDAYNAME":"localdayname","ANULLOCAL":"localyear","LOCALYEAR":"localyear","TIMPULLOCAL":"localtime","LOCALTIME":"localtime","ORALOCALA":"localhour","LOCALHOUR":"localhour","NUMESITE":"sitename","SITENAME":"sitename","SAPTAMANACURENTA":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SAPTAMANALOCALA":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIUNECURENTA":"currentversion","CURRENTVERSION":"currentversion","STAMPILATIMPCURENT":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STAMPILATIMPLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","SEMNDIRECTIE":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LIMBACONTINUT":"contentlanguage","CONTENTLANGUAGE":"contentlanguage", +"CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zăâîşţșțĂÂÎŞŢȘȚ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-roa-tara.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-roa-tara.json new file mode 100644 index 0000000..03b7a4a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-roa-tara.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag", +"#formatodata":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#alberocategorie":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath": +"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2", +"CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME" +:"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NOMESITO":"sitename","SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ru.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ru.json new file mode 100644 index 0000000..1cb65c6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ru.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender", +"множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist": +"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ": +"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY": +"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour", +"МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename", +"ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rue.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rue.json new file mode 100644 index 0000000..7a9b573 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rue.json @@ -0,0 +1,20 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_змісту__":"notoc","__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереї__":"nogallery","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обов_зміст__":"forcetoc","__обязательное_оглавление__": +"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__зміст__":"toc","__оглавление__":"toc","__огл__":"toc","__toc__":"toc","__без_редагув_розділу__":"noeditsection","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_перетворення_заголовку__":"notitleconvert","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_перетворення_тексту__":"nocontentconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ПОСИЛАННЯ_НА_НОВИЙ_РОЗДІЛ__":"newsectionlink","__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ПОСИЛАННЯ_НА_НОВИЙ_РОЗДІЛ__": +"nonewsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__ПРИХОВ_КАТ__":"hiddencat","__ПРИХОВАНА_КАТЕГОРІЯ__":"hiddencat","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ІНДЕКС__":"index","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ІНДЕКСУ__":"noindex","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧНЕ_ПЕРЕНАПРАВЛЕННЯ__":"staticredirect","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пн":"ns","пи":"ns","ns":"ns","пн_2":"nse","пик":"nse","nse":"nse","закодована_адреса":"urlencode", +"закодированный_адрес":"urlencode","urlencode":"urlencode","нр_перша":"lcfirst","перша_буква_мала":"lcfirst","перша_літера_мала":"lcfirst","мала_перша_літера":"lcfirst","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","вр_перша":"ucfirst","перша_буква_велика":"ucfirst","перша_літера_велика":"ucfirst","велика_перша_літера":"ucfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","нр":"lc","нижній_регістр":"lc","малими_буквами":"lc","малими_літерами":"lc","маленькими_буквами":"lc","lc":"lc","вр":"uc","верхній_регістр":"uc","великими_буквами":"uc","великими_літерами":"uc","большими_буквами":"uc","uc":"uc","локальна_адреса":"localurl","локальный_адрес":"localurl", +"localurl":"localurl","локальна_адреса_2":"localurle","локальный_адрес_2":"localurle","localurle":"localurle","повна_адреса":"fullurl","полный_адрес":"fullurl","fullurl":"fullurl","повна_адреса_2":"fullurle","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматувати_число":"formatnum","форматировать_число":"formatnum","formatnum":"formatnum","відмінок":"grammar","падеж":"grammar","grammar":"grammar","стать":"gender","пол":"gender","gender":"gender","множина":"plural","множественное_число":"plural","plural":"plural","bidi":"bidi","#мова":"language","#язык":"language","#language":"language","заповнити_ліворуч":"padleft","заполнить_слева":"padleft","padleft":"padleft","заповнити_праворуч":"padright", +"заполнить_справа":"padright","padright":"padright","кодувати_мітку":"anchorencode","кодировать_метку":"anchorencode","anchorencode":"anchorencode","шлях_до_файлу":"filepath","путь_к_файлу":"filepath","filepath":"filepath","ідентифікатор_сторінки":"pageid","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#спеціальна":"special","#служебная":"special","#special":"special","#speciale":"speciale","#тег":"tag","#мітка":"tag","#теґ":"tag","#метка":"tag","#тэг":"tag","#tag":"tag","#форматдати":"formatdate","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#вавілон":"babel","#babel":"babel","#coordinates":"coordinates","#викликати":"invoke","#вызвать":"invoke","#invoke":"invoke","#related":"related", +"#якщо":"if","#если":"if","#if":"if","#якщорівні":"ifeq","#рівні":"ifeq","#ifeq":"ifeq","#вибірка":"switch","#переключатель":"switch","#switch":"switch","#якщоіснує":"ifexist","#ifexist":"ifexist","#якщовираз":"ifexpr","#ifexpr":"ifexpr","#якщопомилка":"iferror","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#вираз":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорій":"categorytree","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","беззовнішніхпосилань":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#властивість":"property","#свойство":"property","#property":"property","#statements": +"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","шлях_до_статті":"articlepath","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","назва_сервера":"servername","название_сервера":"servername","servername":"servername","шлях_до_скрипту":"scriptpath","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","шлях_до_стилю":"stylepath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"КІЛЬКІСТЬ_У_ГРУПІ":"numberingroup","ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СТАНДАРТНЕ_СОРТУВАННЯ:_КЛЮЧ_СОРТУВАННЯ":"defaultsort","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort", +"DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТОР_В_КАТ":"pagesincategory","СТОР_У_КАТ":"pagesincategory","СТОРІНОК_У_КАТЕГОРІЇ":"pagesincategory","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РОЗМІР":"pagesize","РОЗМІР_СТОРІНКИ":"pagesize","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","РІВЕНЬ_ЗАХИСТУ":"protectionlevel","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВА_СТОРІНКИ":"pagename","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВА_СТОРІНКИ_2":"pagenamee","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОВНА_НАЗВА_СТОРІНКИ":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ": +"fullpagename","FULLPAGENAME":"fullpagename","ПОВНА_НАЗВА_СТОРІНКИ_2":"fullpagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВА_ПІДСТОРІНКИ":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВА_ПІДСТОРІНКИ_2":"subpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВИ_ПІДСТОРІНКИ":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВИ_ПІДСТОРІНКИ_2":"basepagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВА_СТОРІНКИ_ОБГОВОРЕННЯ":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ": +"talkpagename","TALKPAGENAME":"talkpagename","НАЗВА_СТОРІНКИ_ОБГОВОРЕННЯ_2":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВА_СТАТТІ":"subjectpagename","НАЗВА_СТОРІНКИ_СТАТТІ":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВА_СТАТТІ_2":"subjectpagenamee","НАЗВА_СТОРІНКИ_СТАТТІ_2":"subjectpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ІД_ВЕРСІЇ":"revisionid","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСІЇ":"revisionday","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСІЇ_2":"revisionday2","ДЕНЬ_ВЕРСИИ_2": +"revisionday2","REVISIONDAY2":"revisionday2","МІСЯЦЬ_ВЕРСІЇ":"revisionmonth","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МІСЯЦЬ_ВЕРСІЇ_1":"revisionmonth1","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","РІК_ВЕРСІЇ":"revisionyear","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","МІТКА_ЧАСУ_ВЕРСІЇ":"revisiontimestamp","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСІЯ_КОРИСТУВАЧА":"revisionuser","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТІР_НАЗВ":"namespace","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТІР_НАЗВ_2":"namespacee","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН": +"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТІР_ОБГОВОРЕННЯ":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТІР_ОБГОВОРЕННЯ_2":"talkspacee","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТІР_СТАТЕЙ":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТІР_СТАТЕЙ_2":"subjectspacee","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КІЛЬКІСТЬ_СТАТЕЙ":"numberofarticles","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КІЛЬКІСТЬ_ФАЙЛІВ":"numberoffiles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КІЛЬКІСТЬ_КОРИСТУВАЧІВ": +"numberofusers","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КІЛЬКІСТЬ_АКТИВНИХ_КОРИСТУВАЧІВ":"numberofactiveusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КІЛЬКІСТЬ_СТОРІНОК":"numberofpages","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КІЛЬКІСТЬ_АДМІНІСТРАТОРІВ":"numberofadmins","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КІЛЬКІСТЬ_РЕДАГУВАНЬ":"numberofedits","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТИ_ЗАГОЛОВОК":"displaytitle","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ПОТОЧНИЙ_МІСЯЦЬ":"currentmonth", +"ПОТОЧНИЙ_МІСЯЦЬ_2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ПОТОЧНИЙ_МІСЯЦЬ_1":"currentmonth1","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВА_ПОТОЧНОГО_МІСЯЦЯ":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВА_ПОТОЧНОГО_МІСЯЦЯ_РОД":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВА_ПОТОЧНОГО_МІСЯЦЯ_АБР":"currentmonthabbrev","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ПОТОЧНИЙ_ДЕНЬ":"currentday","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday", +"ПОТОЧНИЙ_ДЕНЬ_2":"currentday2","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВА_ПОТОЧНОГО_ДНЯ":"currentdayname","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ПОТОЧНИЙ_РІК":"currentyear","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ПОТОЧНИЙ_ЧАС":"currenttime","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ПОТОЧНА_ГОДИНА":"currenthour","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","ЛОКАЛЬНИЙ_МІСЯЦЬ":"localmonth","ЛОКАЛЬНИЙ_МІСЯЦЬ_2":"localmonth","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","ЛОКАЛЬНИЙ_МІСЯЦЬ_1":"localmonth1","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВА_ЛОКАЛЬНОГО_МІСЯЦЯ": +"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВА_ЛОКАЛЬНОГО_МІСЯЦЯ_РОД":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВА_ЛОКАЛЬНОГО_МІСЯЦЯ_АБР":"localmonthabbrev","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ЛОКАЛЬНИЙ_ДЕНЬ":"localday","МІСЦЕВИЙ_ДЕНЬ":"localday","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","ЛОКАЛЬНИЙ_ДЕНЬ_2":"localday2","МІСЦЕВИЙ_ДЕНЬ_2":"localday2","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВА_ЛОКАЛЬНОГО_ДНЯ":"localdayname","НАЗВА_МІСЦЕВОГО_ДНЯ":"localdayname","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname", +"ЛОКАЛЬНИЙ_РІК":"localyear","МІСЦЕВИЙ_РІК":"localyear","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","ЛОКАЛЬНИЙ_ЧАС":"localtime","МІСЦЕВИЙ_ЧАС":"localtime","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","ЛОКАЛЬНА_ГОДИНА":"localhour","МІСЦЕВА_ГОДИНА":"localhour","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВА_САЙТУ":"sitename","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename","ПОТОЧНИЙ_ТИЖДЕНЬ":"currentweek","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ПОТОЧНИЙ_ДЕНЬ_ТИЖНЯ":"currentdow","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","ЛОКАЛЬНИЙ_ТИЖДЕНЬ":"localweek","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","ЛОКАЛЬНИЙ_ДЕНЬ_ТИЖНЯ":"localdow","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW" +:"localdow","REVISIONSIZE":"revisionsize","ПОТОЧНА_ВЕРСІЯ":"currentversion","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","МІТКА_ПОТОЧНОГО_ЧАСУ":"currenttimestamp","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","МІТКА_ЛОКАЛЬНОГО_ЧАСУ":"localtimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРЯМОК_ПИСЬМА":"directionmark","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","МОВА_ВМІСТУ":"contentlanguage","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгґдеєжзиіїйклмнопрстуфхцчшщьєюяёъы“»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rw.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rw.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-rw.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sa.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sa.json new file mode 100644 index 0000000..4ecb14f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sa.json @@ -0,0 +1,23 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__नैवानुक्रमणी__":"notoc","__विषयसूचीहीनः__":"notoc","__नैवअनुक्रमणी__":"notoc","__notoc__":"notoc","__बिना_अनुक्रम__":"notoc","__विषय_सूची_हीन__":"notoc","__वीथिकाहीनः__":"nogallery", +"__नैवसंक्रमणका__":"nogallery","__nogallery__":"nogallery","__गैलरी_नहीं__":"nogallery","__अनुक्रमणी_दर्श्यताम्__":"forcetoc","__अनुक्रमणीसचते__":"forcetoc","__forcetoc__":"forcetoc","__अनुक्रम_दिखाएँ__":"forcetoc","__विषय_सूची_दिखाएँ__":"forcetoc","__विषय_सूची_दिखायें__":"forcetoc","__अनुक्रमणी__":"toc","__विषयसूची__":"toc","__toc__":"toc","__अनुक्रम__":"toc","__विषय_सूची__":"toc","__अनुभागसम्पादनं_नास्ति__":"noeditsection","__नैवसम्पादनविभाग__":"noeditsection","__noeditsection__":"noeditsection","__अनुभाग_सम्पादन_नहीं__":"noeditsection","__न_शीर्षकपरिवर्तितम्__":"notitleconvert", +"__नैवशिर्षकपरिवर्त__":"notitleconvert","__नैशिप__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__न_लेखपरिवर्तनम्__":"nocontentconvert","__नैवलेखपरिवर्त__":"nocontentconvert","__नैलेप__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__नवीनविभागपरिसन्धिः__":"newsectionlink","__नूतनविभागसम्बद्धं__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__विषय_जोड़ें_कड़ी__":"newsectionlink","__विषय_जोड़े_कड़ी_रहित__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__निगूहितवर्गः__":"hiddencat","__लुप्तवर्ग__":"hiddencat","__HIDDENCAT__":"hiddencat","__छुपी_श्रेणी__":"hiddencat", +"__छिपी_श्रेणी__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__अनुक्रमणी__":"index","__अनुक्रमणिका__":"index","__INDEX__":"index","__सूचीबद्ध__":"index","__अननुक्रमणी__":"noindex","__नैवअनुक्रमणिका__":"noindex","__NOINDEX__":"noindex","__असूचीबद्ध__":"noindex","__अनित्यपुनर्निर्देशनम्__":"staticredirect","__अनित्यपुनर्निदेशन__":"staticredirect","__STATICREDIRECT__":"staticredirect","__स्थिर_पुनर्प्रेषण__":"staticredirect","__स्थिर_अनुप्रेषण__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"नामस्थान":"ns","ns":"ns","नामस्थान_कोड":"nse","nse":"nse", +"यू_आर_एल_कोड":"urlencode","urlencode":"urlencode","छोटे_अक्षर_से_शुरू":"lcfirst","lcfirst":"lcfirst","बड़े_अक्षर_से_शुरू":"ucfirst","ucfirst":"ucfirst","छोटे_अक्षर":"lc","lc":"lc","बड़े_अक्षर":"uc","uc":"uc","स्थानीय_यू_आर_एल":"localurl","localurl":"localurl","स्थानीय_यू_आर_एल_कोड":"localurle","localurle":"localurle","पूर्ण_यू_आर_एल":"fullurl","fullurl":"fullurl","पूर्ण_यू_आर_एल_कोड":"fullurle","fullurle":"fullurle","मानक_यू_आर_एल":"canonicalurl","canonicalurl":"canonicalurl","मानक_यू_आर_एल_कोड":"canonicalurle","canonicalurle":"canonicalurle","प्रारूपसङ्ख्या":"formatnum","रचनासंख्या":"formatnum","formatnum":"formatnum","संख्या_रूप":"formatnum", +"व्याकरणम्":"grammar","व्याकरण":"grammar","grammar":"grammar","लिंग":"gender","gender":"gender","बहुवचनम्":"plural","अनेकवचन":"plural","plural":"plural","वचन":"plural","bidi":"bidi","#भाषा":"language","#language":"language","बाएँ_जोड़ें":"padleft","बायें_जोड़ें":"padleft","padleft":"padleft","दाएँ_जोड़ें":"padright","दायें_जोड़ें":"padright","padright":"padright","ऐंकर_कोड":"anchorencode","anchorencode":"anchorencode","सञ्चिकापथः":"filepath","संचिकापथ":"filepath","filepath":"filepath","फ़ाइल_पथ":"filepath","पृष्ठ_आइ_डी":"pageid","pageid":"pageid","विश्व":"int","int":"int","#विशेषः":"special","#विशेष":"special","#special":"special","#विशेष_कोड":"speciale","#speciale":"speciale", +"#अङ्कनम्":"tag","#वीजक":"tag","#tag":"tag","#टैग":"tag","#तिथि_रूप":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#लक्ष्य":"target","#target":"target","#बेबल":"babel","#babel":"babel","#coordinates":"coordinates","#बुलाएँ":"invoke","#बुलायें":"invoke","#invoke":"invoke","#related":"related","#यदि":"if","#if":"if","#यदिसम":"ifeq","#यदि_समान":"ifeq","#यदि_बराबर":"ifeq","#ifeq":"ifeq","#बदलें":"switch","#switch":"switch","#यदि_मौजूद":"ifexist","#ifexist":"ifexist","#यदि_सूत्र":"ifexpr","#ifexpr":"ifexpr","#यदि_त्रुटि":"iferror","#iferror":"iferror","#समय":"time","#time":"time","#समय_स्थानीय":"timel","#timel":"timel","#सूत्र":"expr","#expr":"expr","#सम्बन्धित_से_पूर्ण":"rel2abs", +"#संबंधित_से_पूर्ण":"rel2abs","#rel2abs":"rel2abs","#शीर्षक_भाग":"titleparts","#titleparts":"titleparts","#श्रेणी_वृक्ष":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","कोई_अंतरविकी_कड़ियाँ_नहीं":"noexternallanglinks","कोई_अंतरविकि_कड़ियाँ_नहीं":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#गुण":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","लेख_पथ":"articlepath","articlepath":"articlepath","सर्वर":"server","server":"server","सर्वर_नाम":"servername","servername":"servername","स्क्रिप्ट_पथ":"scriptpath","scriptpath":"scriptpath","स्टाइल_पथ":"stylepath", +"stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"समूहस्थसङ्ख्या":"numberingroup","गणानामसंख्या":"numberingroup","गणसंख्या":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","समूह_संख्या":"numberingroup","मूल_सॉर्ट":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","वर्गस्थपृष्ठानि":"pagesincategory","वर्गेपृष्ठ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","श्रेणी_में_पृष्ठ":"pagesincategory","पृष्ठाकारः":"pagesize","पृष्ठाकार":"pagesize","PAGESIZE":"pagesize","पृष्ठ_आकार":"pagesize","रक्षास्तरः":"protectionlevel","रक्षास्तर":"protectionlevel", +"PROTECTIONLEVEL":"protectionlevel","सुरक्षा_स्तर":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","पृष्ठनाम":"pagename","PAGENAME":"pagename","पृष्ठ_नाम":"pagename","पृष्ठ_नाम_कोड":"pagenamee","PAGENAMEE":"pagenamee","पूर्णपृष्ठनाम":"fullpagename","FULLPAGENAME":"fullpagename","पूर्ण_पृष्ठ_नाम":"fullpagename","पूर्ण_पृष्ठ_नाम_कोड":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","उपपृष्ठनाम":"subpagename","SUBPAGENAME":"subpagename","उपपृष्ठ_नाम":"subpagename","उपपृष्ठ_नाम_कोड":"subpagenamee","SUBPAGENAMEE":"subpagenamee","मूल_पृष्ठ_नाम":"rootpagename","ROOTPAGENAME":"rootpagename","मूल_पृष्ठ_नाम_कोड":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","आधारपृष्ठनाम":"basepagename", +"BASEPAGENAME":"basepagename","तल_पृष्ठ_नाम":"basepagename","तल_पृष्ठ_नाम_कोड":"basepagenamee","BASEPAGENAMEE":"basepagenamee","सम्भाषणपृष्ठनाम":"talkpagename","संवादपृष्ठनाम":"talkpagename","TALKPAGENAME":"talkpagename","चर्चा_पृष्ठ_नाम":"talkpagename","चर्चा_पृष्ठ_नाम_कोड":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","विषयपृष्ठनाम":"subjectpagename","लेखपृष्ठनाम":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","सामग्री_पृष्ठ_नाम":"subjectpagename","लेख_पृष्ठ_नाम":"subjectpagename","सामग्री_पृष्ठ_नाम_कोड":"subjectpagenamee","लेख_पृष्ठ_नाम_कोड":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE": +"subjectpagenamee","आवृत्तिक्रमाङ्कः":"revisionid","आवृत्तीक्रमांक":"revisionid","REVISIONID":"revisionid","अवतरण_संख्या":"revisionid","आवृत्तिदिवसः":"revisionday","आवृत्तीदिवसे":"revisionday","REVISIONDAY":"revisionday","अवतरण_दिन":"revisionday","आवृत्तिदिवसः२":"revisionday2","आवृत्तीदिवसे२":"revisionday2","REVISIONDAY2":"revisionday2","अवतरण_दिन2":"revisionday2","अवतरण_दिन२":"revisionday2","आवृत्तिमासः":"revisionmonth","आवृत्तीमासे":"revisionmonth","REVISIONMONTH":"revisionmonth","अवतरण_माह":"revisionmonth","अवतरण_माह1":"revisionmonth1","अवतरण_माह१":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","आवृत्तिवर्षम्":"revisionyear", +"आवृत्तीवर्षे":"revisionyear","REVISIONYEAR":"revisionyear","अवतरण_वर्ष":"revisionyear","आवृत्तिसमयमुद्रा":"revisiontimestamp","आवृत्तीसमयमुद्रा":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","अवतरण_समय":"revisiontimestamp","अवतरण_सदस्य":"revisionuser","REVISIONUSER":"revisionuser","सीढ़ी_सुरक्षा_स्रोत":"cascadingsources","CASCADINGSOURCES":"cascadingsources","नामस्थानम्":"namespace","नामविश्व":"namespace","NAMESPACE":"namespace","नामस्थान":"namespace","नामस्थान_कोड":"namespacee","NAMESPACEE":"namespacee","नामस्थान_संख्या":"namespacenumber","NAMESPACENUMBER":"namespacenumber","सम्भाषणस्थानम्":"talkspace","व्यासपिठ":"talkspace","TALKSPACE":"talkspace", +"चर्चा_स्थान":"talkspace","चर्चा_स्थान_कोड":"talkspacee","TALKSPACEE":"talkspacee","विषयस्थानम्":"subjectspace","लेखस्थानम्":"subjectspace","विषयविश्व":"subjectspace","लेखविश्व":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","सामग्री_स्थान":"subjectspace","लेख_स्थान":"subjectspace","सामग्री_स्थान_कोड":"subjectspacee","लेख_स्थान_कोड":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","लेखसङ्ख्या":"numberofarticles","लेखस्य‌सङ्ख्या":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","लेख_संख्या":"numberofarticles","सञ्चिकासङ्ख्या":"numberoffiles","संचिकानाम्‌‌सङ्ख्या":"numberoffiles", +"NUMBEROFFILES":"numberoffiles","फ़ाइल_संख्या":"numberoffiles","फाइल_संख्या":"numberoffiles","सदस्यसङ्ख्या":"numberofusers","योजकस्यसङ्ख्या":"numberofusers","NUMBEROFUSERS":"numberofusers","सदस्य_संख्या":"numberofusers","सक्रिय_सदस्य_संख्या":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","पृष्ठसङ्ख्या":"numberofpages","पृष्ठानाम्‌सङ्ख्या":"numberofpages","NUMBEROFPAGES":"numberofpages","पृष्ठ_संख्या":"numberofpages","प्रबन्धकसङ्ख्या":"numberofadmins","प्रचालकसंख्या":"numberofadmins","NUMBEROFADMINS":"numberofadmins","प्रबन्धक_संख्या":"numberofadmins","प्रबंधक_संख्या":"numberofadmins","सम्पादनसङ्ख्या": +"numberofedits","NUMBEROFEDITS":"numberofedits","सम्पादन_संख्या":"numberofedits","दृश्यशीर्षकम्":"displaytitle","प्रदर्शनशीर्षक":"displaytitle","उपाधिदर्शन":"displaytitle","DISPLAYTITLE":"displaytitle","दृश्य_शीर्षक":"displaytitle","!":"!","=":"=","सद्योमासः":"currentmonth","वर्तमानमासः":"currentmonth","वर्तमानमासः२":"currentmonth","अद्यमासे":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","वर्तमान_माह":"currentmonth","वर्तमान_माह2":"currentmonth","वर्तमान_माह२":"currentmonth","वर्तमान_माह1":"currentmonth1","वर्तमान_माह१":"currentmonth1","CURRENTMONTH1":"currentmonth1","सद्योमासनाम":"currentmonthname","अद्यमासेनाम" +:"currentmonthname","CURRENTMONTHNAME":"currentmonthname","वर्तमान_माह_नाम":"currentmonthname","सद्योमासनामसम्बन्धः":"currentmonthnamegen","अद्यमासेनामसाधारण":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","वर्तमान_माह_सम्बन्ध":"currentmonthnamegen","सद्योमाससङ्क्षिप्तम्":"currentmonthabbrev","अद्यमासेसंक्षीप्त":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","वर्तमान_माह_संक्षेप":"currentmonthabbrev","सद्योदिवसः":"currentday","वर्तमानदिवसः":"currentday","अद्यदिवसे":"currentday","CURRENTDAY":"currentday","वर्तमान_दिन":"currentday","सद्योदिवसः२":"currentday2","वर्तमानदिवसः२":"currentday2", +"अद्यदिवसे२":"currentday2","CURRENTDAY2":"currentday2","वर्तमान_दिन2":"currentday2","वर्तमान_दिन२":"currentday2","सद्योदिवसनाम":"currentdayname","वर्तमानदिवसनाम":"currentdayname","अद्यदिवसेनाम":"currentdayname","CURRENTDAYNAME":"currentdayname","वर्तमान_दिन_नाम":"currentdayname","सद्योवर्षम्":"currentyear","वर्तमानवर्षम्":"currentyear","अद्यवर्ष":"currentyear","CURRENTYEAR":"currentyear","वर्तमान_वर्ष":"currentyear","सद्यस्समयः":"currenttime","वर्तमानसमयः":"currenttime","सद्यसमय":"currenttime","CURRENTTIME":"currenttime","वर्तमान_समय":"currenttime","सद्योहोरा":"currenthour","वर्तमानहोरा":"currenthour", +"सद्यघण्टा":"currenthour","CURRENTHOUR":"currenthour","वर्तमान_घंटा":"currenthour","स्थानिकमासः":"localmonth","स्थानिकमासे":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","स्थानीय_माह":"localmonth","स्थानीय_माह2":"localmonth","स्थानीय_माह२":"localmonth","स्थानीय_माह1":"localmonth1","स्थानीय_माह१":"localmonth1","LOCALMONTH1":"localmonth1","स्थानिकमासनाम":"localmonthname","स्थानिकमासेनाम":"localmonthname","LOCALMONTHNAME":"localmonthname","स्थानीय_माह_नाम":"localmonthname","स्थानिकमासनामसम्बन्धः":"localmonthnamegen","स्थानिकमासेनामसाधारण":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen", +"स्थानीय_माह_सम्बन्ध":"localmonthnamegen","स्थानिकमाससङ्क्षिप्तम्":"localmonthabbrev","स्थानिकमासेसंक्षीप्त":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","स्थानीय_माह_संक्षेप":"localmonthabbrev","स्थानिकदिवसः":"localday","स्थानिकदिवसे":"localday","LOCALDAY":"localday","स्थानीय_दिन":"localday","स्थानिकदिवसः२":"localday2","स्थानिकदिवसे२":"localday2","LOCALDAY2":"localday2","स्थानीय_दिन2":"localday2","स्थानीय_दिन२":"localday2","स्थानिकदिवसनाम":"localdayname","स्थानिकदिवसेनाम":"localdayname","LOCALDAYNAME":"localdayname","स्थानीय_दिन_नाम":"localdayname", +"स्थानिकवर्षम्":"localyear","स्थानिकवर्षे":"localyear","LOCALYEAR":"localyear","स्थानीय_वर्ष":"localyear","स्थानिकसमयः":"localtime","स्थानिकसमये":"localtime","LOCALTIME":"localtime","स्थानीय_समय":"localtime","स्थानिकहोरा":"localhour","स्थानिकघण्टा":"localhour","LOCALHOUR":"localhour","स्थानीय_घंटा":"localhour","स्थाननाम":"sitename","स्थलनाम":"sitename","SITENAME":"sitename","साइट_नाम":"sitename","सद्यस्सप्ताहः":"currentweek","अद्यसप्ताह":"currentweek","CURRENTWEEK":"currentweek","वर्तमान_सप्ताह":"currentweek","वर्तमान_सप्ताह_का_दिन":"currentdow","CURRENTDOW":"currentdow","स्थानिकसप्ताहः":"localweek", +"स्थानिकसप्ताह":"localweek","LOCALWEEK":"localweek","स्थानीय_सप्ताह":"localweek","स्थानीय_सप्ताह_का_दिन":"localdow","LOCALDOW":"localdow","अवतरण_आकार":"revisionsize","REVISIONSIZE":"revisionsize","सद्यरावृत्तिः":"currentversion","अद्यआवृत्ती":"currentversion","CURRENTVERSION":"currentversion","वर्तमान_अवतरण":"currentversion","सद्यस्समयमुद्रा":"currenttimestamp","सद्यसमयमुद्रा":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","वर्तमान_समय_मुहर":"currenttimestamp","स्थानिकसमयमुद्रा":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","स्थानीय_समय_मुहर":"localtimestamp","दिशाचिह्नम्":"directionmark","दिशाचिह्न":"directionmark" +,"दिशे":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","दिशा_चिन्ह":"directionmark","सामग्रीभाषा":"contentlanguage","विषयभाषा":"contentlanguage","आधेयभाषा":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","सामग्री_भाषा":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z\\x{0900}-\\x{0963}\\x{0966}-\\x{A8E0}-\\x{A8FF}]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sah.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sah.json new file mode 100644 index 0000000..c491b12 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sah.json @@ -0,0 +1,18 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__иһинээҕитэ_суох__":"notoc","__иһн_суох__":"notoc","__notoc__":"notoc","__без_оглавления__":"notoc","__без_огл__":"notoc","__галереята_суох__":"nogallery","__nogallery__":"nogallery","__без_галереи__":"nogallery","__булгуччу_иһинээҕилээх__": +"forcetoc","__блг_иһн__":"forcetoc","__forcetoc__":"forcetoc","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__иһинээҕитэ__":"toc","__иһн__":"toc","__toc__":"toc","__оглавление__":"toc","__огл__":"toc","__салааны_уларыппакка__":"noeditsection","__noeditsection__":"noeditsection","__без_редактирования_раздела__":"noeditsection","__аатын_уларыппакка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_заголовка__":"notitleconvert","__тиэкиһин_уларыппакка__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert","__без_преобразования_текста__":"nocontentconvert"},{"__САҤА_САЛААҔА_СИГЭ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink", +"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__САҤА_САЛААҔА_СИГЭТЭ_СУОХ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__КИСТЭММИТ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__ИНДЕКСТЭЭМЭ__":"noindex","__NOINDEX__":"noindex","__БЕЗ_ИНДЕКСА__":"noindex","__ХАЛБАҤНААБАТ_УТААРЫЫ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ад":"ns","ns":"ns","пи":"ns","адк":"nse","nse":"nse","пик":"nse", +"куодтаммыт_аадырыс":"urlencode","urlencode":"urlencode","закодированный_адрес":"urlencode","бастакы_буукуба_кыра":"lcfirst","lcfirst":"lcfirst","первая_буква_маленькая":"lcfirst","бастакы_буукуба_улахан":"ucfirst","ucfirst":"ucfirst","первая_буква_большая":"ucfirst","кыра_буукубаннан":"lc","lc":"lc","маленькими_буквами":"lc","улахан_буукубаннан":"uc","uc":"uc","большими_буквами":"uc","олохтоох_аадырыс":"localurl","localurl":"localurl","локальный_адрес":"localurl","олохтоох_аадырыс_2":"localurle","localurle":"localurle","локальный_адрес_2":"localurle","толору_аадырыс":"fullurl","fullurl":"fullurl","полный_адрес":"fullurl","толору_аадырыс_2":"fullurle","fullurle":"fullurle","полный_адрес_2":"fullurle", +"canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","чыыһыланы_формааттаа":"formatnum","formatnum":"formatnum","форматировать_число":"formatnum","түһүк":"grammar","grammar":"grammar","падеж":"grammar","аҥардам":"gender","gender":"gender","пол":"gender","элбэх_ахсаан":"plural","plural":"plural","множественное_число":"plural","bidi":"bidi","#тыл":"language","#language":"language","#язык":"language","хаҥастан_толор":"padleft","padleft":"padleft","заполнить_слева":"padleft","уҥаттан_толор":"padright","padright":"padright","заполнить_справа":"padright","тиэги_куодтаа":"anchorencode","anchorencode":"anchorencode","кодировать_метку":"anchorencode","билэ_суола":"filepath","filepath":"filepath","путь_к_файлу":"filepath","сирэй_нүөмэрэ":"pageid","pageid":"pageid", +"идентификатор_страницы":"pageid","ис":"int","int":"int","внутр":"int","#аналлаах":"special","#special":"special","#служебная":"special","#speciale":"speciale","#тиэк":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#метка":"tag","#күн-дьыл_формаата":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#форматдаты":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree": +"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","ыстатыйа_суола":"articlepath","articlepath":"articlepath","путь_к_статье":"articlepath","сиэрбэр":"server","server":"server","сервер":"server","сиэрбэр_аата":"servername","servername":"servername","название_сервера":"servername","скрип_суола":"scriptpath","scriptpath":"scriptpath","путь_к_скрипту":"scriptpath","истиил_суола":"stylepath","stylepath":"stylepath","путь_к_стилю":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"БӨЛӨХХӨ_АХСААНА":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup", +"ЧИСЛО_В_ГРУППЕ":"numberingroup","АНААН_ЭТИЛЛИБЭТЭҔИНЭ_НАРДААҺЫН":"defaultsort","НААРДААҺЫН_КҮЛҮҮҺЭ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","КАТЕГОРИЯ_СИРЭЙИН_АХСААНА":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","СИРЭЙ_КЭЭМЭЙЭ":"pagesize","PAGESIZE":"pagesize","РАЗМЕР_СТРАНИЦЫ":"pagesize","КӨМҮСКЭЛ_ТАҺЫМА":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","СИРЭЙ_ААТА":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","СИРЭЙ_ААТА_2":"pagenamee","PAGENAMEE": +"pagenamee","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","СИРЭЙ_ТОЛОРУ_ААТА":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","СИРЭЙ_ТОЛОРУ_ААТА_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","ИҺИНЭЭҔИ_СИРЭЙ_ААТА":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","ИҺИНЭЭҔИ_СИРЭЙ_ААТА_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","СИРЭЙ_ААТЫН_ТӨРДӨ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","СИРЭЙ_ААТЫН_ТӨРДӨ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee", +"ЫРЫТАР_СИРЭЙ_ААТА":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","ЫРЫТАР_СИРЭЙ_ААТА_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","ЫСТАТЫЙА_СИРЭЙИН_ААТА":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","ЫСТАТЫЙА_СИРЭЙИН_ААТА_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","ТОРУМ_НҮӨМЭРЭ":"revisionday","REVISIONID":"revisionid","ИД_ВЕРСИИ":"revisionid","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСИИ":"revisionday","ТОРУМ_НҮӨМЭРЭ_2":"revisionday2","REVISIONDAY2":"revisionday2","ДЕНЬ_ВЕРСИИ_2": +"revisionday2","ТОРУМ_ЫЙА":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ":"revisionmonth","ТОРУМ_ЫЙА_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","ТОРУМ_СЫЛА":"revisionyear","REVISIONYEAR":"revisionyear","ГОД_ВЕРСИИ":"revisionyear","ТОРУМ_КЭМИН_БЭЛИЭТЭ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","КЫТТААЧЧЫ_ТОРУМА":"revisionuser","REVISIONUSER":"revisionuser","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","CASCADINGSOURCES":"cascadingsources","ААТ_ДАЛА":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН":"namespace","ААТ_ДАЛА_2":"namespacee","NAMESPACEE":"namespacee","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","ААТ_ДАЛЫН_НҮӨМЭРЭ":"namespacenumber","NAMESPACENUMBER":"namespacenumber", +"НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","ЫРЫТЫЫ_ДАЛА":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","ЫРЫТЫЫ_ДАЛА_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","ЫСТАТЫЙА_ДАЛА":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","ЫСТАТЫЙА_ДАЛА_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","ЫСТАТЫЙА_АХСААНА":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","БИЛЭ_АХСААНА":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","КЫТТААЧЧЫ_АХСААНА":"numberofusers","NUMBEROFUSERS":"numberofusers", +"КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","КӨХТӨӨХ_КЫТТААЧЧЫ_АХСААНА":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","СИРЭЙ_АХСААНА":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","ДЬАҺАБЫЛ_АХСААНА":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","УЛАРЫТЫЫ_АХСААНА":"numberofedits","NUMBEROFEDITS":"numberofedits","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","ААТЫН_КӨРДӨР":"displaytitle","DISPLAYTITLE":"displaytitle","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","!":"!","=":"=","БУ_ЫЙ":"currentmonth","БУ_ЫЙ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth", +"ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","БУ_ЫЙ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","БУ_ЫЙ_ААТА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","БУ_ЫЙ_ААТА_АРТҺК":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","БУ_ЫЙ_ААТА_АББР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","БУ_КҮН":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ":"currentday","БУ_КҮН_2":"currentday2","CURRENTDAY2":"currentday2","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","БУ_КҮН_ААТА":"currentdayname","CURRENTDAYNAME":"currentdayname","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","БУ_СЫЛ":"currentyear", +"CURRENTYEAR":"currentyear","ТЕКУЩИЙ_ГОД":"currentyear","БУ_КЭМ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","БУ_ЧААС":"currenthour","CURRENTHOUR":"currenthour","ТЕКУЩИЙ_ЧАС":"currenthour","ОЛОХТООХ_ЫЙ":"localmonth","ОЛОХТООХ_ЫЙ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","ОЛОХТООХ_ЫЙ_1":"localmonth1","LOCALMONTH1":"localmonth1","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","ОЛОХТООХ_ЫЙ_ААТА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","ОЛОХТООХ_ЫЙ_ААТА_АРТҺК":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","ОЛОХТООХ_ЫЙ_ААТА_АББР":"localmonthabbrev","LOCALMONTHABBREV": +"localmonthabbrev","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","ОЛОХТООХ_КҮН":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ":"localday","ОЛОХТООХ_КҮН_2":"localday2","LOCALDAY2":"localday2","МЕСТНЫЙ_ДЕНЬ_2":"localday2","ОЛОХТООХ_КҮН_ААТА":"localdayname","LOCALDAYNAME":"localdayname","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","ОЛОХТООХ_СЫЛ":"localyear","LOCALYEAR":"localyear","МЕСТНЫЙ_ГОД":"localyear","ОЛОХТООХ_КЭМ":"localtime","LOCALTIME":"localtime","МЕСТНОЕ_ВРЕМЯ":"localtime","ОЛОХТООХ_ЧААС":"localhour","LOCALHOUR":"localhour","МЕСТНЫЙ_ЧАС":"localhour","СИТИМ-СИР_ААТА":"sitename","SITENAME":"sitename","НАЗВАНИЕ_САЙТА":"sitename","БУ_НЭДИЭЛЭ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","НЭДИЭЛЭ_БУ_КҮНЭ":"currentdow","CURRENTDOW": +"currentdow","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","ОЛОХТООХ_НЭДИЭЛЭ":"localweek","LOCALWEEK":"localweek","МЕСТНАЯ_НЕДЕЛЯ":"localweek","ОЛОХТООХ_НЭДИЭЛЭ_КҮНЭ":"localdow","LOCALDOW":"localdow","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","REVISIONSIZE":"revisionsize","БИЛИҤҤИ_ТОРУМ":"currentversion","CURRENTVERSION":"currentversion","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","БИЛИҤҤИ_КЭМ_БЭЛИЭТЭ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","ОЛОХТООХ_КЭМ_БЭЛИЭТЭ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","СУРУК-БИЧИК_ХАЙЫСХАТА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","ИҺИНЭЭҔИТИН_ТЫЛА": +"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгҕдеёжзийклмнҥоөпрсһтуүфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sat.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sat.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sat.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sc.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sc.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sc.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-scn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-scn.json new file mode 100644 index 0000000..03b7a4a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-scn.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag", +"#formatodata":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#alberocategorie":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath": +"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2", +"CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME" +:"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NOMESITO":"sitename","SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sco.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sco.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sco.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sd.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sd.json new file mode 100644 index 0000000..a41b2e5 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sd.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__ لڪل زمرو __":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"نپ":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","مقامييوآريل":"localurl","localurl":"localurl","localurle":"localurle","مڪمليوآريل":"fullurl","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","وياڪرڻ":"grammar","grammar":"grammar","gender":"gender","جمع":"plural","plural":"plural","bidi":"bidi","#ٻولي":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","فائيلڏس":"filepath","filepath":"filepath", +"pageid":"pageid","int":"int","#خاص":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup", +"DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","زمريجاصفحا":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","صفحيجيماپ":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","صفحيجوعنوان":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","صحفيجوپورونالو":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2", +"REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","نانئپولار":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","بحثپولار":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","مضمونپولار":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","مضموننجوتعداد":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","فائيلنجوتعداد":"numberoffiles","NUMBEROFFILES":"numberoffiles","يوزرسجوتعداد":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","صفحنجوتعداد":"numberofpages","NUMBEROFPAGES":"numberofpages","منتظمينجوتعداد":"numberofadmins","NUMBEROFADMINS": +"numberofadmins","ترميمنجوتعداد":"numberofedits","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","مقاميمهينو":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","مقاميمهينونالو":"localmonthname","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","مقاميڏينهن":"localday","LOCALDAY":"localday","مقاميڏينهن2":"localday2","LOCALDAY2":"localday2","مقاميڏينهننالو":"localdayname","LOCALDAYNAME":"localdayname", +"مقاميسال":"localyear","LOCALYEAR":"localyear","مقاميوقت":"localtime","LOCALTIME":"localtime","مقاميڪلاڪ":"localhour","LOCALHOUR":"localhour","سرزميننالو":"sitename","SITENAME":"sitename","هلندڙهفتو":"currentweek","CURRENTWEEK":"currentweek","اڄوڪوڏينهن":"currentdow","CURRENTDOW":"currentdow","مقاميهفتو":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","هلندڙوقتمهر":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","مقاميوقتمهر":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","طرفنشان":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","موادٻولي":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-se.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-se.json new file mode 100644 index 0000000..9ed14ba --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-se.json @@ -0,0 +1,14 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__iisisdoallu__":"notoc","__iisis__":"notoc","__notoc__":"notoc","__ingeninnholdsfortegnelse__":"notoc","__eisisluett__":"notoc","__iigalleriija__":"nogallery","__nogallery__":"nogallery","__intetgalleri__":"nogallery","__tvinginnholdsfortegnelse__":"forcetoc","__forcetoc__":"forcetoc","__sisluettpakotus__":"forcetoc", +"__sisdoallu__":"toc"," __sis__":"toc","__toc__":"toc","__innholdsfortegnelse__":"toc","__sisällysluettelo__":"toc","__iirievdaditoasi__":"noeditsection","__noeditsection__":"noeditsection","__ingenseksjonsredigering__":"noeditsection","__eiosiomuokkausta__":"noeditsection","__ingentittelkonvertering__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__ingeninnholdskonvertering__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NYSEKSJONSLENKE__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__INGENNYSEKSJONSLENKE__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__SKJULTKATEGORI__":"hiddencat","__HIDDENCAT__":"hiddencat","__PIILOLUOKKA__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKSER__":"index","__INDEX__":"index","__INGENINDEKSERING__":"noindex","__NOINDEX__":"noindex","__HAKUKONEKIELTO__":"noindex","__STATISTOMDIRIGERING__":"staticredirect", +"__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__PEKER__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","na":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","lokalurl":"localurl","localurl":"localurl","paikallinenosoite":"localurl","lokalurle":"localurle","localurle":"localurle","paikallinenosoitee":"localurle","täysiosoite":"fullurl","fullurl":"fullurl","täysiosoitee":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","muotoileluku":"formatnum","formatnum":"formatnum","grammatikk":"grammar","grammar":"grammar","taivutus":"grammar","kjønn":"gender","gender":"gender","sukupuoli":"gender","flertall":"plural","plural":"plural","monikko":"plural","bidi":"bidi","#kieli":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode": +"anchorencode","filsti":"filepath","filepath":"filepath","tiedostopolku":"filepath","pageid":"pageid","int":"int","#spesial":"special","#special":"special","#speciale":"speciale","#tag":"tag","#datoformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#kjør":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategoritre":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","ingenspråklenkerutenfra":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#egenskap":"property","#ominaisuus":"property","#property":"property","#påstander":"statements","#statements":"statements","#kommaadskiltliste": +"commaSeparatedList","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikkelsti":"articlepath","articlepath":"articlepath","tjener":"server","server":"server","palvelin":"server","tjenernavn":"servername","servername":"servername","palvelinnimi":"servername","skriptsti":"scriptpath","scriptpath":"scriptpath","skriptipolku":"scriptpath","stilsti":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMMERIGRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","STANDARDSORTERING":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","AAKKOSTUS":"defaultsort","OLETUSAAKKOSTUS":"defaultsort","SIDERIKATEGORI":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SIDESTØRRELSE":"pagesize","PAGESIZE":"pagesize","SIVUKOKO":"pagesize","BESKYTTELSESNIVÅ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel", +"SUOJAUSTASO":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SIDENAVN":"pagename","PAGENAME":"pagename","SIVUNIMI":"pagename","SIDENAVNE":"pagenamee","PAGENAMEE":"pagenamee","SIVUNIMIE":"pagenamee","FULLTSIDENAVN":"fullpagename","FULLPAGENAME":"fullpagename","KOKOSIVUNIMI":"fullpagename","FULLTSIDENAVNE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","KOKOSIVUNIMIE":"fullpagenamee","UNDERSIDENAVN":"subpagename","SUBPAGENAME":"subpagename","ALASIVUNIMI":"subpagename","UNDERSIDENAVNE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ALASIVUNIMIE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","GRUNNSIDENAVN":"basepagename","BASEPAGENAME":"basepagename","KANTASIVUNIMI":"basepagename","GRUNNSIDENAVNE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","KANTASIVUNIMIE":"basepagenamee","DISKUSJONSSIDENAVN":"talkpagename","TALKPAGENAME":"talkpagename","KESKUSTELUSIVUNIMI":"talkpagename","DISKUSJONSSIDENAVNE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"KESKUSTELUSIVUNIMIE":"talkpagenamee","SUBJEKTSIDENAVN":"subjectpagename","ARTIKKELSIDENAVN":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","AIHESIVUNIMI":"subjectpagename","ARTIKKELISIVUNIMI":"subjectpagename","SUBJEKTSIDENAVNE":"subjectpagenamee","ARTIKKELSIDENAVNE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","AIHESIVUNIMIE":"subjectpagenamee","ARTIKKELISIVUNIMIE":"subjectpagenamee","REVISJONSID":"revisionid","REVISIONID":"revisionid","VERSIOID":"revisionid","REVISJONSDAG":"revisionday","REVISIONDAY":"revisionday","VERSIOPÄIVÄ":"revisionday","REVISJONSDAG2":"revisionday2","REVISIONDAY2":"revisionday2","VERSIOPÄIVÄ2":"revisionday2","REVISJONSMÅNED":"revisionmonth","REVISIONMONTH":"revisionmonth","VERSIOKUUKAUSI":"revisionmonth","REVISJONSMÅNED1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISJONSÅR":"revisionyear","REVISIONYEAR":"revisionyear","VERSIOVUOSI":"revisionyear", +"REVISJONSTIDSSTEMPEL":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","VERSIOAIKALEIMA":"revisiontimestamp","REVISJONSBRUKER":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAVNEROM":"namespace","NAMESPACE":"namespace","NIMIAVARUUS":"namespace","NAVNEROME":"namespacee","NAMESPACEE":"namespacee","NIMIAVARUUSE":"namespacee","NAMESPACENUMBER":"namespacenumber","DISKUSJONSROM":"talkspace","TALKSPACE":"talkspace","KESKUSTELUAVARUUS":"talkspace","DISKUSJONSROME":"talkspacee","TALKSPACEE":"talkspacee","KESKUSTELUAVARUUSE":"talkspacee","SUBJEKTROM":"subjectspace","ARTIKKELROM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","AIHEAVARUUS":"subjectspace","ARTIKKELIAVARUUS":"subjectspace","SUBJEKTROME":"subjectspacee","ARTIKKELROME":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","AIHEAVARUUSE":"subjectspacee","ARTIKKELIAVARUUSE":"subjectspacee","ARTIHKKALIIDMEARRI":"numberofarticles", +" ARTIHKALMEARRI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ANTALLARTIKLER":"numberofarticles","ARTIKKELIMÄÄRÄ":"numberofarticles","FIILLAIDMEARRI":"numberoffiles","FIILAMEARRI":"numberoffiles"," GOVAIDMEARRI":"numberoffiles"," GOVVAMEARRI":"numberoffiles","NUMBEROFFILES":"numberoffiles","ANTALLFILER":"numberoffiles","TIEDOSTOMÄÄRÄ":"numberoffiles","GEAVAHEDDJIIDMEARRI":"numberofusers"," GEAVAHEADDJIMEARRI":"numberofusers"," GEAVAHEADDJEMEARRI":"numberofusers","NUMBEROFUSERS":"numberofusers","ANTALLBRUKERE":"numberofusers","KÄYTTÄJÄMÄÄRÄ":"numberofusers","AKTIIVAGEAVAHEDDJIIDMEARRI":"numberofactiveusers"," AKTIIVAGEAVAHEADDJIMEARRI":"numberofactiveusers"," AKTIIVAGEAVAHEADDJEMEARRI":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ANTALLAKTIVEBRUKERE":"numberofactiveusers","ANTALLSIDER":"numberofpages","NUMBEROFPAGES":"numberofpages","SIVUMÄÄRÄ":"numberofpages","ANTALLADMINISTRATORER":"numberofadmins","NUMBEROFADMINS": +"numberofadmins","YLLÄPITÄJÄMÄÄRÄ":"numberofadmins","RIEVDADUSAIDMEARRI":"numberofedits"," RIEVDADUSMEARRI":"numberofedits","NUMBEROFEDITS":"numberofedits","ANTALLREDIGERINGER":"numberofedits","MUOKKAUSMÄÄRÄ":"numberofedits","VISTITTEL":"displaytitle","DISPLAYTITLE":"displaytitle","NÄKYVÄOTSIKKO":"displaytitle","!":"!","=":"=","NÅVÆRENDEMÅNED":"currentmonth","NÅVÆRENDEMÅNED2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","KULUVAKUU":"currentmonth","NÅVÆRENDEMÅNED1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NÅVÆRENDEMÅNEDSNAVN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","KULUVAKUUNIMI":"currentmonthname","NÅVÆRENDEMÅNEDSNAVNGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","KULUVAKUUNIMIGEN":"currentmonthnamegen","NÅVÆRENDEMÅNEDSNAVNKORT":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","KULUVAKUUNIMILYHYT":"currentmonthabbrev","NÅVÆRENDEDAG":"currentday","CURRENTDAY": +"currentday","KULUVAPÄIVÄ":"currentday","NÅVÆRENDEDAG2":"currentday2","CURRENTDAY2":"currentday2","KULUVAPÄIVÄ2":"currentday2","NÅVÆRENDEDAGSNAVN":"currentdayname","CURRENTDAYNAME":"currentdayname","KULUVAPÄIVÄNIMI":"currentdayname","NÅVÆRENDEÅR":"currentyear","CURRENTYEAR":"currentyear","KULUVAVUOSI":"currentyear","NÅVÆRENDETID":"currenttime","CURRENTTIME":"currenttime","KULUVAAIKA":"currenttime","NÅVÆRENDETIME":"currenthour","CURRENTHOUR":"currenthour","KULUVATUNTI":"currenthour","LOKALMÅNED":"localmonth","LOKALMÅNED2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","PAIKALLINENKUU":"localmonth","LOKALMÅNED1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALMÅNEDSNAVN":"localmonthname","LOCALMONTHNAME":"localmonthname","PAIKALLINENKUUNIMI":"localmonthname","LOKALMÅNEDSNAVNGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","PAIKALLINENKUUNIMIGEN":"localmonthnamegen","LOKALMÅNEDSNAVNKORT":"localmonthabbrev","LOCALMONTHABBREV": +"localmonthabbrev","PAIKALLINENKUUNIMILYHYT":"localmonthabbrev","LOKALDAG":"localday","LOCALDAY":"localday","PAIKALLINENPÄIVÄ":"localday","LOKALDAG2":"localday2","LOCALDAY2":"localday2","PAIKALLINENPÄIVÄ2":"localday2","LOKALDAGSNAVN":"localdayname","LOCALDAYNAME":"localdayname","PAIKALLINENPÄIVÄNIMI":"localdayname","LOKALTÅR":"localyear","LOCALYEAR":"localyear","PAIKALLINENVUOSI":"localyear","LOKALTID":"localtime","LOCALTIME":"localtime","PAIKALLINENAIKA":"localtime","LOKALTIME":"localhour","LOCALHOUR":"localhour","PAIKALLINENTUNTI":"localhour","SIVUSTONIMI":"sitename","SITENAME":"sitename","NÅVÆRENDEUKE":"currentweek","CURRENTWEEK":"currentweek","KULUVAVIIKKO":"currentweek","NÅVÆRENDEUKEDAG":"currentdow","CURRENTDOW":"currentdow","KULUVAVIIKONPÄIVÄ":"currentdow","LOKALUKE":"localweek","LOCALWEEK":"localweek","PAIKALLINENVIIKKO":"localweek","LOKALUKEDAG":"localdow","LOCALDOW":"localdow","PAIKALLINENVIIKONPÄIVÄ":"localdow","REVISIONSIZE":"revisionsize", +"NÅVÆRENDEVERSJON":"currentversion","CURRENTVERSION":"currentversion","NYKYINENVERSIO":"currentversion","NÅVÆRENDETIDSSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","KULUVAAIKALEIMA":"currenttimestamp","LOKALTTIDSSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","PAIKALLINENAIKALEIMA":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INNHOLDSSPRÅK":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters": +"/^(:?[a-zàáâçčʒǯđðéèêëǧǥȟíìîïıǩŋñóòôõßšŧúùûýÿüžþæøåäö]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sg.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sg.json new file mode 100644 index 0000000..30830cb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sg.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode", +"initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int", +"#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property", +"#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGE":"pagename","PAGENAME":"pagename", +"NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid", +"REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee", +"ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL": +"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sh.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sh.json new file mode 100644 index 0000000..d1ccad7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sh.json @@ -0,0 +1,20 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__bezsadržaja__":"notoc","__notoc__":"notoc","__bez_sadržaja__":"notoc","__bezgalerije__":"nogallery","__nogallery__":"nogallery","__bez_galerije__":"nogallery","__forsiranisadržaj__":"forcetoc","__uključisadržaj__":"forcetoc","__forcetoc__":"forcetoc","__forsirani_sadržaj__":"forcetoc","__primoranisadržaj__": +"forcetoc","__primorani_sadržaj__":"forcetoc","__sadržaj__":"toc","__toc__":"toc","__bez_izmjena__":"noeditsection","__bezizmjena__":"noeditsection","__bez_izmena__":"noeditsection","__bezizmena__":"noeditsection","__noeditsection__":"noeditsection","__bezuređivanjaodlomaka__":"noeditsection","__beztc__":"notitleconvert","__bezkn__":"notitleconvert","__bpn__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__bezpretvaranjanaslova__":"notitleconvert","__bezcc__":"nocontentconvert","__bps__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert","__bezpretvaranjasadržaja__":"nocontentconvert"},{"__LINKNOVESEKCIJE__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NOVAVEZAODELJKA__":"newsectionlink","__NOVA_VEZA_ODELJKA__":"newsectionlink","__NOVIODLOMAKPOVEZNICA__":"newsectionlink","__BEZNOVEVEZEODELJKA__":"nonewsectionlink","__BEZ_NOVE_VEZE_ODELJKA__":"nonewsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__SAKRIVENAKATEGORIJA__":"hiddencat","SKRIVENAKAT":"hiddencat","__SAKRIVENAKAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__SKRIVENAKAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__SADRZAJ__":"index","__INDEX__":"index","__INDEKS__":"index","__KAZALO__":"index","__BEZSADRZAJA__":"noindex","__NOINDEX__":"noindex","__BEZINDEKSA__":"noindex","__BEZ_INDEKSA__":"noindex","__BEZKAZALA__":"noindex","__STATISTICNOPREUSMJERENJE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__STATIČKOPREUSMERENJE__":"staticredirect","STATIČKO_PREUSMERENJE":"staticredirect","__NEPOMIČNOPREUSMJERAVANJE__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ip":"ns","ns":"ns","imp":"ns","nse":"nse","dekodirajadresu":"urlencode","urlencode":"urlencode","kodiranjeadrese":"urlencode","kodiranje_adrese":"urlencode","urlkodiranje":"urlencode","lcprvi":"lcfirst","lcfirst":"lcfirst","msprvo":"lcfirst","ucprvi": +"ucfirst","ucfirst":"ucfirst","vsprvo":"ucfirst","ms":"lc","lc":"lc","vs":"uc","uc":"uc","lokalniurl":"localurl","localurl":"localurl","lokalnaadresa":"localurl","lokalna_adresa":"localurl","mjesniurl":"localurl","lokalniurle":"localurle","localurle":"localurle","lokalneadrese":"localurle","lokalne_adrese":"localurle","mjesniurle":"localurle","puniurl":"fullurl","punurl":"fullurl","fullurl":"fullurl","celaadresa":"fullurl","cela_adresa":"fullurl","puniurle":"fullurle","punurle":"fullurle","fullurle":"fullurle","celeadrese":"fullurle","cele_adrese":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","numerickiformat":"formatnum","formatnum":"formatnum","oblikbroja":"formatnum","gramatika":"grammar","grammar":"grammar","pol":"gender","gender":"gender","rod":"gender","lice":"gender","množina":"plural","plural":"plural","bidi":"bidi","#jezik":"language","#language":"language","jastuklijevo":"padleft","padleft":"padleft","poravnajulevo":"padleft","poravnaj_ulevo": +"padleft","postavalijevo":"padleft","jastukdesno":"padright","padright":"padright","poravnajudesno":"padright","poravnaj_udesno":"padright","postavadesno":"padright","kodiranjeveze":"anchorencode","kodiranje_veze":"anchorencode","anchorencode":"anchorencode","sidrokodiranje":"anchorencode","stazadatoteke":"filepath","filepath":"filepath","putanjadatoteke":"filepath","putanja_datoteke":"filepath","pageid":"pageid","int":"int","#posebno":"special","#special":"special","#specijalno":"special","#speciale":"speciale","#oznaka":"tag","#tag":"tag","#formatdatuma":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#format_datuma":"formatdate","#stablokategorije":"categorytree","#stablo_kategorije":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel": +"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","putanjačlanka":"articlepath","putanja_članka":"articlepath","articlepath":"articlepath","server":"server","imeservera":"servername","servername":"servername","ime_servera":"servername","skripta":"scriptpath","scriptpath":"scriptpath","skript":"scriptpath","putanjaskripte":"scriptpath","putanjastila":"stylepath","putanja_stila":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"BROJSTRANICA":"numberofpages","BROJSTRANA":"numberofpages","NUMBEROFPAGES":"numberofpages","BROJ_STRANICA":"numberofpages","BROJKORISNIKA":"numberofusers","NUMBEROFUSERS":"numberofusers","BROJ_KORISNIKA":"numberofusers","BROJSURADNIKA": +"numberofusers","BROJAKTIVNIHKORISNIKA":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","BROJ_AKTIVNIH_KORISNIKA":"numberofactiveusers","BROJAKTIVNIHSURADNIKA":"numberofactiveusers","BROJČLANAKA":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","BROJ_ČLANAKA":"numberofarticles","BROJDATOTEKA":"numberoffiles","BROJFAJLOVA":"numberoffiles","NUMBEROFFILES":"numberoffiles","BROJ_DATOTEKA":"numberoffiles","BROJ_FAJLOVA":"numberoffiles","BROJADMINISTRATORA":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BROJADMINA":"numberofadmins","BROJ_ADMINA":"numberofadmins","BROJUGRUPI":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","BROJ_U_GRUPI":"numberingroup","BROJIZMJENA":"numberofedits","BROJIZMENA":"numberofedits","BROJUREĐIVANJA":"numberofedits","NUMBEROFEDITS":"numberofedits","BROJPROMJENA":"numberofedits","BROJ_IZMENA":"numberofedits","PODRAZUMEVANOSORTIRANJE":"defaultsort","PODRAZUMEVANIKLJUČZASORTIRANJE":"defaultsort", +"PODRAZMEVANOSORTIRANJEKATEGORIJE":"defaultsort","DEFAULTSORT":"defaultsort","GLAVNIRASPORED":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","STRANICEUKATEGORIJI":"pagesincategory","STRANICEUKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","STRANICAUKATEGORIJI":"pagesincategory","STRANAUKATEGORIJI":"pagesincategory","STRANICA_U_KATEGORIJI":"pagesincategory","STRANA_U_KATEGORIJI":"pagesincategory","STRANICEPOKATEGORIJI":"pagesincategory","VELICINASTRANICE":"pagesize","VELIČINASTRANICE":"pagesize","VELIČINASTRANE":"pagesize","VELICINASTRANE":"pagesize","PAGESIZE":"pagesize","VELIČINA_STRANICE":"pagesize","VELIČINA_STRANE":"pagesize","NIVOZASTITE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","NIVOZAŠTITE":"protectionlevel","NIVO_ZAŠTITE":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","IMENSKIPROSTORI":"namespacee","NAMESPACEE":"namespacee","IMENSKI_PROSTORI":"namespacee","IMENSKIPROSTORE": +"namespacee","BROJIMENSKOGPROSTORA":"namespacenumber","NAMESPACENUMBER":"namespacenumber","PROSTORZARAZGOVOR":"talkspace","TALKSPACE":"talkspace","RAZGOVOR":"talkspace","PROSTORIZARAZGOVOR":"talkspacee","TALKSPACEE":"talkspacee","RAZGOVORI":"talkspacee","RAZGOVORE":"talkspacee","PROSTORSUBJEKTA":"subjectspace","PROSTORCLANAKA":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","IMENSKIPROSTORČLANKA":"subjectspace","IMENSKI_PROSTOR_ČLANKA":"subjectspace","PROSTORSTRANICE":"subjectspace","IMPSTRANICE":"subjectspace","PROSTORISUBJEKTA":"subjectspacee","PROSTORICLANKA":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","IMENSKIPROSTORČLANAKA":"subjectspacee","IMENSKI_PROSTOR_ČLANAKA":"subjectspacee","PROSTORSTRANICEE":"subjectspacee","IMPSTRANICEE":"subjectspacee","STRANICA":"pagename","IMESTRANICE":"pagename","PAGENAME":"pagename","IME_STRANICE":"pagename","STRANICE":"pagenamee","IMESTRANICEE":"pagenamee","PAGENAMEE":"pagenamee", +"IMENASTRANICA":"pagenamee","IMENA_STRANICA":"pagenamee","PUNOIMESTRANE":"fullpagename","PUNOIMESTRANICE":"fullpagename","FULLPAGENAME":"fullpagename","PUNO_IME_STRANICE":"fullpagename","PUNO_IME_STRANE":"fullpagename","PUNOIMESTRANEE":"fullpagenamee","PUNOIMESTRANICEE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","PUNAIMENASTRANICA":"fullpagenamee","PUNAIMENASTRANA":"fullpagenamee","PUNA_IMENA_STRANICA":"fullpagenamee","PUNA_IMENA_STRANA":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","IMEBAZNESTRANICE":"basepagename","BASEPAGENAME":"basepagename","IMEOSNOVE":"basepagename","IME_OSNOVE":"basepagename","IMEOSNOVNESTRANICE":"basepagename","IMENABAZNESTRANICE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","IMENAOSNOVA":"basepagenamee","IMENA_OSNOVA":"basepagenamee","IMEOSNOVNESTRANICEE":"basepagenamee","IMEPODSTRANICE":"subpagename","SUBPAGENAME":"subpagename","IMEPODSTRANE":"subpagename","IME_PODSTRANICE":"subpagename","IME_PODSTRANE":"subpagename", +"IMENAPODSTRANICE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","IMENAPODSTRANICA":"subpagenamee","IMENAPODSTRANA":"subpagenamee","IMENA_PODSTRANICA":"subpagenamee","IMENA_PODSTRANA":"subpagenamee","IMEPODSTRANICEE":"subpagenamee","IMESTRANICERAZGOVORA":"talkpagename","TALKPAGENAME":"talkpagename","IMERAZGOVORA":"talkpagename","IME_RAZGOVORA":"talkpagename","IMERAZGOVORASTRANICE":"talkpagename","IMENASTRANICERAZGOVORA":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","IMENARAZGOVORA":"talkpagenamee","IMENA_RAZGOVORA":"talkpagenamee","IMERAZGOVORASTRANICEE":"talkpagenamee","IMESTRANICESUBKJEKTA":"subjectpagename","IMESTRANICECLANKA":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","IMEČLANKA":"subjectpagename","IME_ČLANKA":"subjectpagename","IMEGLAVNESTRANICE":"subjectpagename","IMENASTRANICESUBJEKTA":"subjectpagenamee","IMENASTRANICECLANAKA":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee", +"IMENAČLANAKA":"subjectpagenamee","IMENA_ČLANAKA":"subjectpagenamee","IMEGLAVNESTRANICEE":"subjectpagenamee","IDIZMJENE":"revisionid","IDIZMENE":"revisionid","REVISIONID":"revisionid","IDREVIZIJE":"revisionid","ID_REVIZIJE":"revisionid","IB_IZMENE":"revisionid","IZMJENEDANA":"revisionday","IZMENEDANA":"revisionday","REVISIONDAY":"revisionday","REVIZIJEDANA":"revisionday","DANIZMENE":"revisionday","DAN_IZMENE":"revisionday","DANIZMJENE":"revisionday","IZMJENEDANA2":"revisionday2","IZMENEDANA2":"revisionday2","REVISIONDAY2":"revisionday2","REVIZIJEDANA2":"revisionday2","DANIZMENE2":"revisionday2","DAN_IZMENE2":"revisionday2","DANIZMJENE2":"revisionday2","MJESECIZMJENE":"revisionmonth","MESECIZMENE":"revisionmonth","REVISIONMONTH":"revisionmonth","REVIZIJAMJESECA":"revisionmonth","MESEC_IZMENE":"revisionmonth","MJESECIZMJENE1":"revisionmonth1","MESECIZMENE1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","MESEC_IZMENE1":"revisionmonth1","GODINAIZMJENE":"revisionyear","GODINAIZMENE": +"revisionyear","REVISIONYEAR":"revisionyear","REVIZIJAGODINE":"revisionyear","GODINA_IZMENE":"revisionyear","OZNAKAVREMENAIZMJENE":"revisiontimestamp","OZNAKAVREMENAIZMENE":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVIZIJAVREMENSKOGPECATA":"revisiontimestamp","VREMEIZMENE":"revisiontimestamp","VREME_IZMENE":"revisiontimestamp","VREMENSKAOZNAKAIZMJENE":"revisiontimestamp","KORISNIKIZMENE":"revisionuser","KORISNIK_IZMENE":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","IMENSKIPROSTOR":"namespace","NAMESPACE":"namespace","IMENSKI_PROSTOR":"namespace","POKAZINASLOV":"displaytitle","DISPLAYTITLE":"displaytitle","NAZIVPRIKAZA":"displaytitle","NAZIV_PRIKAZA":"displaytitle","POKAŽINASLOV":"displaytitle","!":"!","TRENUTNIMJESEC":"currentmonth","TRENUTNIMESEC":"currentmonth","TRENUTAČNIMJESEC":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","TRENUTNI_MESEC":"currentmonth","TEKUĆIMESEC":"currentmonth", +"TEKUĆI_MESEC":"currentmonth","TRENUTNIMJESEC1":"currentmonth1","TRENUTNIMESEC1":"currentmonth1","TRENUTAČNIMJESEC1":"currentmonth1","CURRENTMONTH1":"currentmonth1","TRENUTNI_MESEC1":"currentmonth1","TEKUĆIMESEC1":"currentmonth1","TEKUĆI_MESEC1":"currentmonth1","TRENUTNIMJESECIME":"currentmonthname","TRENUTNIMESECIME":"currentmonthname","TRENUTAČNIMJESECIME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","IMETEKUĆEGMESECA":"currentmonthname","IME_TEKUĆEG_MESECA":"currentmonthname","TRENUTNIMJESECROD":"currentmonthnamegen","TRENUTNIMESECROD":"currentmonthnamegen","TRENUTAČNIMJESECROD":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","TRENUTNIMESECGEN":"currentmonthnamegen","TEKUĆIMESECGEN":"currentmonthnamegen","TEKUĆI_MESEC_GEN":"currentmonthnamegen","TRENUTAČNIMJESECIMEGEN":"currentmonthnamegen","TRENUTNIMJESECSKR":"currentmonthabbrev","TRENUTNIMESECSKR":"currentmonthabbrev","TRENUTAČNIMJESECSKR":"currentmonthabbrev","CURRENTMONTHABBREV": +"currentmonthabbrev","TEKUĆIMESECSKR":"currentmonthabbrev","TEKUĆI_MESEC_SKR":"currentmonthabbrev","TRENUTAČNIMJESECKRAT":"currentmonthabbrev","TRENUTNIDAN":"currentday","TRENUTAČNIDAN":"currentday","CURRENTDAY":"currentday","TEKUĆIDAN":"currentday","TEKUĆI_DAN":"currentday","TRENUTNIDAN2":"currentday2","TRENUTAČNIDAN2":"currentday2","CURRENTDAY2":"currentday2","TEKUĆIDAN2":"currentday2","TEKUĆI_DAN_2":"currentday2","TRENUTNIDANIME":"currentdayname","TRENUTAČNIDANIME":"currentdayname","CURRENTDAYNAME":"currentdayname","IMETEKUĆEGDANA":"currentdayname","IME_TEKUĆEG_DANA":"currentdayname","TRENUTNAGODINA":"currentyear","TRENUTAČNAGODINA":"currentyear","CURRENTYEAR":"currentyear","TEKUĆAGODINA":"currentyear","TEKUĆA_GODINA":"currentyear","TRENUTNOVRIJEME":"currenttime","TRENUTNOVREME":"currenttime","TRENUTAČNOVRIJEME":"currenttime","CURRENTTIME":"currenttime","TEKUĆEVREME":"currenttime","TEKUĆE_VREME":"currenttime","TRENUTNISAT":"currenthour","TRENUTAČNISAT": +"currenthour","CURRENTHOUR":"currenthour","TEKUĆISAT":"currenthour","TEKUĆI_SAT":"currenthour","LOKALNIMJESEC":"localmonth","LOKALNIMESEC":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALNI_MESEC":"localmonth","MJESNIMJESEC":"localmonth","LOKALNIMJESEC1":"localmonth1","LOKALNIMESEC1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALNIMESEC2":"localmonth1","LOKALNI_MESEC2":"localmonth1","MJESNIMJESEC1":"localmonth1","LOKALNIMJESECIME":"localmonthname","LOKALNIMESECIME":"localmonthname","LOCALMONTHNAME":"localmonthname","IMELOKALNOGMESECA":"localmonthname","IME_LOKALNOG_MESECA":"localmonthname","MJESNIMJESECIME":"localmonthname","LOKALNIMJESECROD":"localmonthnamegen","LOKALNIMESECROD":"localmonthnamegen","LOKALNIMJESECGEN":"localmonthnamegen","LOKALNIMESECGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALNIMJESECIMEROD":"localmonthnamegen","LOKALNI_MESEC_GEN":"localmonthnamegen","MJESNIMJESECIMEGEN":"localmonthnamegen","LOKALNIMJESECSKR": +"localmonthabbrev","LOKALNIMESECSKR":"localmonthabbrev","LOKALNIMJESECKRAT":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALNI_MESEC_SKR":"localmonthabbrev","MJESNIMJESECKRAT":"localmonthabbrev","LOKALNIDAN":"localday","LOCALDAY":"localday","LOKALNI_DAN":"localday","MJESNIDAN":"localday","LOKALNIDAN2":"localday2","LOCALDAY2":"localday2","LOKALNI_DAN2":"localday2","MJESNIDAN2":"localday2","LOKALNIDANIME":"localdayname","LOCALDAYNAME":"localdayname","IMELOKALNOGDANA":"localdayname","IME_LOKALNOG_DANA":"localdayname","MJESNIDANIME":"localdayname","LOKALNAGODINA":"localyear","LOCALYEAR":"localyear","LOKALNA_GODINA":"localyear","MJESNAGODINA":"localyear","LOKALNOVRIJEME":"localtime","LOKALNOVREME":"localtime","LOCALTIME":"localtime","LOKALNO_VREME":"localtime","MJESNOVRIJEME":"localtime","LOKALNISAT":"localhour","LOCALHOUR":"localhour","LOKALNI_SAT":"localhour","MJESNISAT":"localhour","IMESAJTA":"sitename","SITENAME":"sitename","IMEPROJEKTA":"sitename","TRENUTNASEDMICA": +"currentweek","TRENUTAČNITJEDAN":"currentweek","TRENUTNANEDELJA":"currentweek","TRENUTNITJEDAN":"currentweek","CURRENTWEEK":"currentweek","TRENUTNA_NEDELJA":"currentweek","TEKUĆANEDELJA":"currentweek","TEKUĆA_NEDELJA":"currentweek","TRENUTNIDANSEDMICE":"currentdow","TRENUTAČNIDANTJEDNA":"currentdow","TRENUTNIDANNEDELJE":"currentdow","TRENUTNIDANTJEDNA":"currentdow","CURRENTDOW":"currentdow","TRENUTNIDOV":"currentdow","TEKUĆIDUN":"currentdow","LOKALNASEDMICA":"localweek","LOKALNITJEDAN":"localweek","LOKALNANEDELJA":"localweek","LOCALWEEK":"localweek","LOKALNA_NEDELJA":"localweek","MJESNITJEDAN":"localweek","LOKALNIDANSEDMICE":"localdow","LOKALNIDANTJEDNA":"localdow","LOKALNIDANNEDELJE":"localdow","LOCALDOW":"localdow","LOKALNIDUN":"localdow","MJESNIDANTJEDNA":"localdow","REVISIONSIZE":"revisionsize","SADASNJAVERZIJA":"currentversion","CURRENTVERSION":"currentversion","TEKUĆEIZDANJE":"currentversion","TEKUĆE_IZDANJE":"currentversion","TRENUTAČNAIZMJENA":"currentversion", +"TRENUTNAOZNAKAVREMENA":"currenttimestamp","TRENUTAČNAOZNAKAVREMENA":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","SADASNJIVREMENSKIPECAT":"currenttimestamp","TEKUĆIOTISAKVREMENA":"currenttimestamp","TEKUĆI_OTISAK_VREMENA":"currenttimestamp","LOKALNAOZNAKAVREMENA":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","LOKALNIVREMENSKIPECAT":"localtimestamp","OTISAKVREMENA":"localtimestamp","OTISAK_VREMENA":"localtimestamp","MJESNAOZNAKAVREMENA":"localtimestamp","SMEROZNAKE":"directionmark","SMER _OZNAKE":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","JEZIKSADRŽAJA":"contentlanguage","JEZIK_SADRŽAJA":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","JEZIKPROJEKTA":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zčćđžš]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-shi.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-shi.json new file mode 100644 index 0000000..d9e9342 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-shi.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode", +"initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int", +"#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property", +"#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGE":"pagename","PAGENAME":"pagename", +"NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid", +"REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee", +"ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL": +"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([ⴰ-ⵯa-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙḍḥɛṛɣṣṭẓḌḤƐṚƔṢṬẒʷ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-shn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-shn.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-shn.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-shy.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-shy.json new file mode 100644 index 0000000..db4cee7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-shy.json @@ -0,0 +1 @@ +{"wgPageParseReport":{"discussiontools":{"limitreport-timeusage":"0.006"},"limitreport":{"cputime":"0.129","walltime":"0.149","ppvisitednodes":{"value":13091,"limit":1000000},"postexpandincludesize":{"value":8514,"limit":2097152},"templateargumentsize":{"value":438,"limit":2097152},"expansiondepth":{"value":10,"limit":100},"expensivefunctioncount":{"value":1,"limit":500},"unstrip-depth":{"value":0,"limit":20},"unstrip-size":{"value":0,"limit":5000000},"entityaccesscount":{"value":0,"limit":400},"timingprofile":["100.00% 123.333 1 Template:Test_wiki","100.00% 123.333 1 -total"," 97.51% 120.257 1 Template:Test_wiki/core"," 69.68% 85.938 2 Template:Test_wiki/validcode"," 3.48% 4.286 2 Template:Test_wiki/639-3"]},"scribunto":{"limitreport-timeusage":{"value":"0.004","limit":"10.000"},"limitreport-memusage":{"value":584643,"limit":52428800}},"cachereport":{"origin":"mw1321","timestamp":"20221117013859","ttl":1814400,"transientcontent":false}}});mw.config.set({"wgBackendResponseTime":108,"wgHostname":"mw2315"} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-si.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-si.json new file mode 100644 index 0000000..d7f8211 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-si.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#විශේෂ":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#බාබෙල්": +"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE": +"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","පිටුනාමය":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","නාමඅවකාශය":"namespace","NAMESPACE":"namespace", +"නාමඅවකාශයන්":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ලිපිසංඛ්‍යාව":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ගොනුසංඛ්‍යාව":"numberoffiles","NUMBEROFFILES":"numberoffiles","පරිශීලකයන්සංඛ්‍යාව":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","පිටුසංඛ්‍යාව":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","සංස්කරණසංඛ්‍යාව":"numberofedits","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","වත්මන්මාසය":"currentmonth","වත්මන්මාසය2":"currentmonth","CURRENTMONTH": +"currentmonth","CURRENTMONTH2":"currentmonth","වත්මන්මාසය1":"currentmonth1","CURRENTMONTH1":"currentmonth1","වත්මන්මාසනාමය":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","වත්මන්මාසනාමයපොදු":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","වත්මන්මාසයකෙටි":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","වත්මන්දිනය":"currentday","CURRENTDAY":"currentday","වත්මන්දිනය2":"currentday2","CURRENTDAY2":"currentday2","වත්මන්දිනනාමය":"currentdayname","CURRENTDAYNAME":"currentdayname","වත්මන්වසර":"currentyear","CURRENTYEAR":"currentyear","වත්මන්වේලාව":"currenttime","CURRENTTIME":"currenttime","වත්මන්පැය":"currenthour","CURRENTHOUR":"currenthour","දේශීයමාසය":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2" +:"localmonth","LOCALMONTH1":"localmonth1","දේශීයමාසනාමය":"localmonthname","LOCALMONTHNAME":"localmonthname","දේශීයමාසනාමයපොදු":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","දේශීයමාසයකෙටි":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","දේශීයදිනය":"localday","LOCALDAY":"localday","දේශීයදිනය2":"localday2","LOCALDAY2":"localday2","දේශීයදිනනාමය":"localdayname","LOCALDAYNAME":"localdayname","දේශීයවසර":"localyear","LOCALYEAR":"localyear","දේශීයවේලාව":"localtime","LOCALTIME":"localtime","දේශීයපැය":"localhour","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP": +"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-simple.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-simple.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-simple.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sk.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sk.json new file mode 100644 index 0000000..f21966f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sk.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__bezobsahu__":"notoc","__notoc__":"notoc","__bezgalérie__":"nogallery","__nogallery__":"nogallery","__bezgalerie__":"nogallery","__vynútiťobsah__":"forcetoc","__forcetoc__":"forcetoc","__vždyobsah__":"forcetoc","__obsah__":"toc","__toc__":"toc","__neupravovaťsekcie__":"noeditsection","__noeditsection__":"noeditsection" +,"__bezeditovatčást__":"noeditsection","__bezkonverzenadpisu__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__bezkonverzeobsahu__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LINKPŘIDATKOMENTÁŘ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__BEZLINKUPŘIDATKOMENTÁŘ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__SKRYTÁKATEGÓRIA__":"hiddencat","__SKRYTÁKAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__SKRÝTKAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXOVAT__":"index","__INDEX__":"index","__NEINDEXOVAT__":"noindex","__NOINDEX__":"noindex","__STATICKÉPŘESMĚROVÁNÍ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"mp":"ns","ns":"ns","jmennýprostor":"ns","jmennýprostore":"nse","nse":"nse","enkódovaturl":"urlencode","urlencode": +"urlencode","prvnímalé":"lcfirst","lcfirst":"lcfirst","prvnívelké":"ucfirst","ucfirst":"ucfirst","malá":"lc","lc":"lc","velká":"uc","uc":"uc","místníurl":"localurl","localurl":"localurl","místníurle":"localurle","localurle":"localurle","plnéurl":"fullurl","fullurl":"fullurl","plnéurle":"fullurle","fullurle":"fullurle","kanonickéurl":"canonicalurl","canonicalurl":"canonicalurl","kanonickéurle":"canonicalurle","canonicalurle":"canonicalurle","formátujčíslo":"formatnum","formatnum":"formatnum","gramatika":"grammar","grammar":"grammar","skloňuj":"grammar","pohlaví":"gender","gender":"gender","plurál":"plural","plural":"plural","obasměry":"bidi","bidi":"bidi","#jazyk":"language","#language":"language","zarovnatvlevo":"padleft","padleft":"padleft","zarovnatvpravo":"padright","padright":"padright","enkódovatnadpis":"anchorencode","anchorencode":"anchorencode","cestaksúboru":"filepath","filepath":"filepath","cestaksouboru":"filepath","idstránky":"pageid","pageid": +"pageid","hlášení":"int","int":"int","#speciální":"special","#special":"special","#speciale":"speciale","#značka":"tag","#tag":"tag","#formátujdatum":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#cíl":"target","#target":"target","#babylon":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#když":"if","#if":"if","#ifeq":"ifeq","#switch":"switch","#kdyžexist":"ifexist","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#čas":"time","#time":"time","#timel":"timel","#výraz":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#vlastnost":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","cestakčlánku":"articlepath","articlepath":"articlepath","server": +"server","názovservera":"servername","servername":"servername","názevserveru":"servername","cestakuskriptu":"scriptpath","scriptpath":"scriptpath","cestakeskriptům":"scriptpath","cestakestylům":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"POČETSTRÁNOK":"numberofpages","NUMBEROFPAGES":"numberofpages","POČETSTRAN":"numberofpages","POČETPOUŽÍVATEĽOV":"numberofusers","NUMBEROFUSERS":"numberofusers","POČETUŽIVATELŮ":"numberofusers","POČETAKTIVNÍCHUŽIVATELŮ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","POČETČLÁNKOV":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","POČETČLÁNKŮ":"numberofarticles","POČETSÚBOROV":"numberoffiles","NUMBEROFFILES":"numberoffiles","POČETSOUBORŮ":"numberoffiles","POČETSPRÁVCOV":"numberofadmins","NUMBEROFADMINS":"numberofadmins","POČETSPRÁVCŮ":"numberofadmins","POČETVESKUPINĚ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP": +"numberingroup","POČETÚPRAV":"numberofedits","NUMBEROFEDITS":"numberofedits","POČETEDITACÍ":"numberofedits","KLÍČŘAZENÍ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","STRÁNOKVKATEGÓRII":"pagesincategory","STRÁNOKVKAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","STRÁNEKVKATEGORII":"pagesincategory","STRÁNEKVKAT":"pagesincategory","VEĽKOSŤSTRÁNKY":"pagesize","PAGESIZE":"pagesize","VELIKOSTSTRÁNKY":"pagesize","ÚROVEŇZAMČENÍ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","VYPRŠENÍZAMČENÍ":"protectionexpiry","PROTECTIONEXPIRY":"protectionexpiry","MENNÝPRIESTORE":"namespacee","NAMESPACEE":"namespacee","JMENNÝPROSTORE":"namespacee","ČÍSLOJMENNÉHOPROSTORU":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSNÝPRIESTOR":"talkspace","TALKSPACE":"talkspace","DISKUSNÍPROSTOR":"talkspace","DISKUSNÝPRIESTORE":"talkspacee","TALKSPACEE": +"talkspacee","DISKUSNÍPROSTORE":"talkspacee","PRIESTORČLÁNKOV":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ČLÁNEKPROSTOR":"subjectspace","PRIESTORČLÁNKOVE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ČLÁNEKPROSTORE":"subjectspacee","NÁZOVSTRÁNKY":"pagename","PAGENAME":"pagename","NÁZEVSTRANY":"pagename","NÁZOVSTRÁNKYE":"pagenamee","PAGENAMEE":"pagenamee","NÁZEVSTRANYE":"pagenamee","PLNÝNÁZOVSTRÁNKY":"fullpagename","FULLPAGENAME":"fullpagename","PLNÝNÁZEVSTRANY":"fullpagename","PLNÝNÁZOVSTRÁNKYE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","PLNÝNÁZEVSTRANYE":"fullpagenamee","NÁZEVKOŘENOVÉSTRANY":"rootpagename","ROOTPAGENAME":"rootpagename","NÁZEVKOŘENOVÉSTRANYE":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NÁZOVZÁKLADNEJSTRÁNKY":"basepagename","BASEPAGENAME":"basepagename","NÁZEVNADSTRANY":"basepagename","NÁZOVZÁKLADNEJSTRÁNKYE":"basepagenamee","BASEPAGENAMEE": +"basepagenamee","NÁZEVNADSTRANYE":"basepagenamee","NÁZOVPODSTRÁNKY":"subpagename","SUBPAGENAME":"subpagename","NÁZEVPODSTRANY":"subpagename","NÁZOVPODSTRÁNKYE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NÁZEVPODSTRANYE":"subpagenamee","NÁZOVDISKUSNEJSTRÁNKY":"talkpagename","TALKPAGENAME":"talkpagename","NÁZEVDISKUSE":"talkpagename","NÁZOVDISKUSNEJSTRÁNKYE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NÁZEVDISKUSEE":"talkpagenamee","NÁZOVČLÁNKU":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NÁZEVČLÁNKU":"subjectpagename","NÁZOVČLÁNKUE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","NÁZEVČLÁNKUE":"subjectpagenamee","IDREVIZE":"revisionid","REVISIONID":"revisionid","DENREVIZE":"revisionday","REVISIONDAY":"revisionday","DENREVIZE2":"revisionday2","REVISIONDAY2":"revisionday2","MĚSÍCREVIZE":"revisionmonth","REVISIONMONTH":"revisionmonth","MĚSÍCREVIZE1": +"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ROKREVIZE":"revisionyear","REVISIONYEAR":"revisionyear","KÓDČASUREVIZE":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","AUTORREVIZE":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","MENNÝPRIESTOR":"namespace","NAMESPACE":"namespace","JMENNÝPROSTOR":"namespace","ZOBRAZOVANÝNADPIS":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","AKTUÁLNYMESIAC":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","AKTUÁLNÍMĚSÍC":"currentmonth","AKTUÁLNÍMĚSÍC2":"currentmonth","AKTUÁLNÍMĚSÍC1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NÁZOVAKTUÁLNEHOMESIACA":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","AKTUÁLNÍMĚSÍCJMÉNO":"currentmonthname","NÁZOVAKTUÁLNEHOMESIACAGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","AKTUÁLNÍMĚSÍCGEN":"currentmonthnamegen","NÁZOVAKTUÁLNEHOMESIACASKRATKA":"currentmonthabbrev", +"CURRENTMONTHABBREV":"currentmonthabbrev","AKTUÁLNÍMĚSÍCZKR":"currentmonthabbrev","AKTUÁLNYDEŇ":"currentday","CURRENTDAY":"currentday","AKTUÁLNÍDEN":"currentday","AKTUÁLNYDEŇ2":"currentday2","CURRENTDAY2":"currentday2","AKTUÁLNÍDEN2":"currentday2","NÁZOVAKTUÁLNEHODŇA":"currentdayname","CURRENTDAYNAME":"currentdayname","AKTUÁLNÍDENJMÉNO":"currentdayname","AKTUÁLNYROK":"currentyear","CURRENTYEAR":"currentyear","AKTUÁLNÍROK":"currentyear","AKTUÁLNYČAS":"currenttime","CURRENTTIME":"currenttime","AKTUÁLNÍČAS":"currenttime","AKTUÁLNAHODINA":"currenthour","CURRENTHOUR":"currenthour","AKTUÁLNÍHODINA":"currenthour","MÍSTNÍMĚSÍC":"localmonth","MÍSTNÍMĚSÍC2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MÍSTNÍMĚSÍC1":"localmonth1","LOCALMONTH1":"localmonth1","MÍSTNÍMĚSÍCJMÉNO":"localmonthname","LOCALMONTHNAME":"localmonthname","MÍSTNÍMĚSÍCGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MÍSTNÍMĚSÍCZKR": +"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","MÍSTNÍDEN":"localday","LOCALDAY":"localday","MÍSTNÍDEN2":"localday2","LOCALDAY2":"localday2","MÍSTNÍDENJMÉNO":"localdayname","LOCALDAYNAME":"localdayname","MÍSTNÍROK":"localyear","LOCALYEAR":"localyear","MÍSTNÍČAS":"localtime","LOCALTIME":"localtime","MÍSTNÍHODINA":"localhour","LOCALHOUR":"localhour","NÁZOVLOKALITY":"sitename","SITENAME":"sitename","NÁZEVWEBU":"sitename","AKTUÁLNYTÝŽDEŇ":"currentweek","CURRENTWEEK":"currentweek","AKTUÁLNÍTÝDEN":"currentweek","AKTUÁLNÍDENTÝDNE":"currentdow","CURRENTDOW":"currentdow","MÍSTNÍTÝDEN":"localweek","LOCALWEEK":"localweek","MÍSTNÍDENTÝDNE":"localdow","LOCALDOW":"localdow","VELIKOSTREVIZE":"revisionsize","REVISIONSIZE":"revisionsize","AKTUÁLNAVERZIA":"currentversion","CURRENTVERSION":"currentversion","VERZESOFTWARE":"currentversion","AKTUÁLNÍKÓDČASU":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MÍSTNÍKÓDČASU":"localtimestamp", +"LOCALTIMESTAMP":"localtimestamp","ZNAKSMĚRU":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","JAZYKOBSAHU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","JAZYKSTRÁNKY":"pagelanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-záäčďéíľĺňóôŕšťúýž]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-skr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-skr.json new file mode 100644 index 0000000..1a9a675 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-skr.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters": +"/^([آابٻپتٹثجچڄحخدڈݙذرڑزژسشصضطظعغفقکگڳلمنݨوہھیےئأءۃڋڰںؤ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sl.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sl.json new file mode 100644 index 0000000..b1a4d97 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sl.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__brezkazalavsebine__":"notoc","__notoc__":"notoc","__brezgalerije__":"nogallery","__nogallery__":"nogallery","__vsilikazalovsebine__":"forcetoc","__forcetoc__":"forcetoc","__poglavje__":"toc","__toc__":"toc","__brezurejanjarazdelkov__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert" +,"__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__SKRITAKATEGORIJA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__KAZALO__":"index","__INDEX__":"index","__BREZKAZALA__":"noindex","__NOINDEX__":"noindex","__STATICNAPREUSMERITEV__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","sklon":"grammar","grammar":"grammar","spol":"gender","gender":"gender","mnozina":"plural","plural":"plural","bidi":"bidi","#jezik":"language","#language":"language", +"padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#oznaka":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","streznik":"server","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis", +"wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","STOPNJAZASCITE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename", +"ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME": +"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","IMESTRANI":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zčćđžš]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sm.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sm.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sm.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-smn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-smn.json new file mode 100644 index 0000000..4359ac4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-smn.json @@ -0,0 +1,9 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__eisisluett__":"notoc","__notoc__":"notoc","__nogallery__":"nogallery","__sisluettpakotus__":"forcetoc","__forcetoc__":"forcetoc","__sisällysluettelo__":"toc","__toc__":"toc","__eiosiomuokkausta__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert", +"__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__PIILOLUOKKA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__HAKUKONEKIELTO__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"na":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","paikallinenosoite":"localurl","localurl":"localurl","paikallinenosoitee":"localurle","localurle":"localurle","täysiosoite":"fullurl","fullurl":"fullurl","täysiosoitee":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","muotoileluku":"formatnum","formatnum":"formatnum","taivutus":"grammar","grammar":"grammar","sukupuoli":"gender","gender":"gender","monikko":"plural","plural": +"plural","bidi":"bidi","#kieli":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","tiedostopolku":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#ominaisuus":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","palvelin":"server","server":"server", +"palvelinnimi":"servername","servername":"servername","skriptipolku":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"SIVUMÄÄRÄ":"numberofpages","NUMBEROFPAGES":"numberofpages","KÄYTTÄJÄMÄÄRÄ":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ARTIKKELIMÄÄRÄ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","TIEDOSTOMÄÄRÄ":"numberoffiles","NUMBEROFFILES":"numberoffiles","YLLÄPITÄJÄMÄÄRÄ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","MUOKKAUSMÄÄRÄ":"numberofedits","NUMBEROFEDITS":"numberofedits","AAKKOSTUS":"defaultsort","OLETUSAAKKOSTUS":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SIVUKOKO":"pagesize","PAGESIZE":"pagesize","SUOJAUSTASO": +"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NIMIAVARUUSE":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","KESKUSTELUAVARUUS":"talkspace","TALKSPACE":"talkspace","KESKUSTELUAVARUUSE":"talkspacee","TALKSPACEE":"talkspacee","AIHEAVARUUS":"subjectspace","ARTIKKELIAVARUUS":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","AIHEAVARUUSE":"subjectspacee","ARTIKKELIAVARUUSE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","SIVUNIMI":"pagename","PAGENAME":"pagename","SIVUNIMIE":"pagenamee","PAGENAMEE":"pagenamee","KOKOSIVUNIMI":"fullpagename","FULLPAGENAME":"fullpagename","KOKOSIVUNIMIE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","KANTASIVUNIMI":"basepagename","BASEPAGENAME":"basepagename","KANTASIVUNIMIE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ALASIVUNIMI":"subpagename","SUBPAGENAME": +"subpagename","ALASIVUNIMIE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","KESKUSTELUSIVUNIMI":"talkpagename","TALKPAGENAME":"talkpagename","KESKUSTELUSIVUNIMIE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","AIHESIVUNIMI":"subjectpagename","ARTIKKELISIVUNIMI":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","AIHESIVUNIMIE":"subjectpagenamee","ARTIKKELISIVUNIMIE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","VERSIOID":"revisionid","REVISIONID":"revisionid","VERSIOPÄIVÄ":"revisionday","REVISIONDAY":"revisionday","VERSIOPÄIVÄ2":"revisionday2","REVISIONDAY2":"revisionday2","VERSIOKUUKAUSI":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","VERSIOVUOSI":"revisionyear","REVISIONYEAR":"revisionyear","VERSIOAIKALEIMA":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NIMIAVARUUS": +"namespace","NAMESPACE":"namespace","NÄKYVÄOTSIKKO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","KULUVAKUU":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","KULUVAKUUNIMI":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","KULUVAKUUNIMIGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","KULUVAKUUNIMILYHYT":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","KULUVAPÄIVÄ":"currentday","CURRENTDAY":"currentday","KULUVAPÄIVÄ2":"currentday2","CURRENTDAY2":"currentday2","KULUVAPÄIVÄNIMI":"currentdayname","CURRENTDAYNAME":"currentdayname","KULUVAVUOSI":"currentyear","CURRENTYEAR":"currentyear","KULUVAAIKA":"currenttime","CURRENTTIME":"currenttime","KULUVATUNTI":"currenthour","CURRENTHOUR":"currenthour","PAIKALLINENKUU":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","PAIKALLINENKUUNIMI":"localmonthname","LOCALMONTHNAME": +"localmonthname","PAIKALLINENKUUNIMIGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","PAIKALLINENKUUNIMILYHYT":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","PAIKALLINENPÄIVÄ":"localday","LOCALDAY":"localday","PAIKALLINENPÄIVÄ2":"localday2","LOCALDAY2":"localday2","PAIKALLINENPÄIVÄNIMI":"localdayname","LOCALDAYNAME":"localdayname","PAIKALLINENVUOSI":"localyear","LOCALYEAR":"localyear","PAIKALLINENAIKA":"localtime","LOCALTIME":"localtime","PAIKALLINENTUNTI":"localhour","LOCALHOUR":"localhour","SIVUSTONIMI":"sitename","SITENAME":"sitename","KULUVAVIIKKO":"currentweek","CURRENTWEEK":"currentweek","KULUVAVIIKONPÄIVÄ":"currentdow","CURRENTDOW":"currentdow","PAIKALLINENVIIKKO":"localweek","LOCALWEEK":"localweek","PAIKALLINENVIIKONPÄIVÄ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","NYKYINENVERSIO":"currentversion","CURRENTVERSION":"currentversion","KULUVAAIKALEIMA":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp", +"PAIKALLINENAIKALEIMA":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zâčđŋšžäá]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sn.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sn.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-so.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-so.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-so.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sq.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sq.json new file mode 100644 index 0000000..af33d24 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sq.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__patp__":"notoc","__jotp__":"notoc","__notoc__":"notoc","__pagaleri__":"nogallery","__jogaleri__":"nogallery","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__tp__":"toc","__toc__":"toc","__paredaktimpjese__":"noeditsection","__joredaktimseksioni__":"noeditsection","__noeditsection__":"noeditsection", +"__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORIEFSHEHUR__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","urllokale":"localurl","localurl":"localurl","localurle":"localurle","urleplotë":"fullurl","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","gramatika":"grammar","grammar":"grammar","gjinia":"gender","gender":"gender","shumës":"plural","plural":"plural", +"bidi":"bidi","#gjuha":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","serveri":"server","server":"server","emriiserverit":"servername","servername": +"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","MADHËSIAEFAQES":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","EMRIFAQES":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","EMRIIPLOTËIFAQES":"fullpagename","FULLPAGENAME":"fullpagename","EMRIIPLOTËIFAQESE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","EMRIINËNFAQES":"subpagename","SUBPAGENAME":"subpagename","EMRIINËNFAQESE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","EMRIIFAQESBAZË":"basepagename","BASEPAGENAME":"basepagename","EMRIIFAQESBAZËE":"basepagenamee","BASEPAGENAMEE":"basepagenamee", +"EMRIIFAQESSËDISKUTIMIT":"talkpagename","TALKPAGENAME":"talkpagename","EMRIIFAQESSËDISKUTIMITE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","HAPËSIRA":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMRIIARTIKUJVE":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMRIISKEDAVE":"numberoffiles","NUMBEROFFILES": +"numberoffiles","NUMRIIPËRDORUESVE":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMRIIPËRDORUESVEAKTIVË":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMRIFAQEVE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMRIIADMINISTRATORËVE":"numberofadmins","NUMRIIADMINISTRUESVE":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMRIREDAKTIMEVE":"numberofedits","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MUAJIAKTUAL":"currentmonth","MUAJIAKTUAL2":"currentmonth","MUAJIMOMENTAL":"currentmonth","MUAJIMOMENTAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MUAJIAKTUAL1":"currentmonth1","MUAJIMOMENTAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","EMRIIMUAJITAKTUAL":"currentmonthname","EMRIIMUAJITMOMENTAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","DITASOT":"currentday","CURRENTDAY": +"currentday","DITASOT2":"currentday2","CURRENTDAY2":"currentday2","EMRIIDITËSOT":"currentdayname","CURRENTDAYNAME":"currentdayname","SIVJET":"currentyear","CURRENTYEAR":"currentyear","KOHATANI":"currenttime","CURRENTTIME":"currenttime","ORATANI":"currenthour","CURRENTHOUR":"currenthour","MUAJILOKAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","EMRIIMUAJITLOKAL":"localmonthname","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","DITALOKALE":"localday","LOCALDAY":"localday","DITALOKALE2":"localday2","LOCALDAY2":"localday2","EMRIIDITËSLOKALE":"localdayname","LOCALDAYNAME":"localdayname","VITILOKAL":"localyear","LOCALYEAR":"localyear","KOHALOKALE":"localtime","LOCALTIME":"localtime","ORALOKALE":"localhour","LOCALHOUR":"localhour","EMRIISITIT":"sitename","EMRIISAJTIT":"sitename","SITENAME":"sitename","JAVAAKTUALE":"currentweek","JAVAMOMENTALE":"currentweek","CURRENTWEEK": +"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sr.json new file mode 100644 index 0000000..9d3fad8 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sr.json @@ -0,0 +1,17 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"dynamicpagelist":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__безсадржаја__":"notoc","__без_садржаја__":"notoc","__notoc__":"notoc","__безгалерије__":"nogallery","__без_галерије__":"nogallery","__nogallery__":"nogallery","__форсиранисадржај__":"forcetoc","__форсирани_садржај__": +"forcetoc","__приморанисадржај__":"forcetoc","__приморани_садржај__":"forcetoc","__forcetoc__":"forcetoc","__садржај__":"toc","__toc__":"toc","__безизмена__":"noeditsection","__без_измена__":"noeditsection","__безизмјена__":"noeditsection","__без_измјена__":"noeditsection","__noeditsection__":"noeditsection","__безкн__":"notitleconvert","__bezkn__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__безкс__":"nocontentconvert","__безцц__":"nocontentconvert","__bezks__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__НОВАВЕЗАОДЕЉКА__":"newsectionlink","__НОВА_ВЕЗА_ОДЕЉКА__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗНОВЕВЕЗЕОДЕЉКА__":"nonewsectionlink","__БЕЗ_НОВЕ_ВЕЗЕ_ОДЕЉКА__":"nonewsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__САКРИВЕНАКАТ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗИНДЕКСА__":"noindex","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧКОПРЕУСМЕРЕЊЕ__":"staticredirect","СТАТИЧКО_ПРЕУСМЕРЕЊЕ":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ип":"ns","ns":"ns","nse":"nse","кодирањеадресе":"urlencode","кодирање_адресе":"urlencode","urlencode":"urlencode","лцпрви":"lcfirst","lcfirst":"lcfirst","уцпрви":"ucfirst","ucfirst":"ucfirst","лц":"lc","lc":"lc","уц":"uc","uc":"uc","локалнаадреса":"localurl","локална_адреса":"localurl","localurl":"localurl","локалнеадресе": +"localurle","локалне_адресе":"localurle","localurle":"localurle","пунурл":"fullurl","целаадреса":"fullurl","цела_адреса":"fullurl","fullurl":"fullurl","пунурле":"fullurle","целеадресе":"fullurle","целе_адресе":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","граматика":"grammar","grammar":"grammar","род":"gender","лице":"gender","gender":"gender","множина":"plural","plural":"plural","bidi":"bidi","#language":"language","поравнајлево":"padleft","поравнај_лево":"padleft","padleft":"padleft","поравнајдесно":"padright","поравнај_десно":"padright","padright":"padright","кодирањевезе":"anchorencode","кодирање_везе":"anchorencode","anchorencode":"anchorencode","путањадатотеке":"filepath","путања_датотеке":"filepath","filepath": +"filepath","бројстранице":"pageid","pageid":"pageid","инт":"int","int":"int","#посебно":"special","#special":"special","#speciale":"speciale","#ознака":"tag","#tag":"tag","#форматдатума":"formatdate","#формат_датума":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#бабел":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#време":"time","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#стаблокатегорије":"categorytree","#стабло_категорије":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements": +"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","путањачланка":"articlepath","путања_чланка":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","имесервера":"servername","име_сервера":"servername","servername":"servername","скрипта":"scriptpath","scriptpath":"scriptpath","путањастила":"stylepath","путања_стила":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"БРОЈУГРУПИ":"numberingroup","БРОЈ_У_ГРУПИ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ПОДРАЗУМЕВАНОСОРТИРАЊЕ":"defaultsort","ПОДРАЗУМЕВАНИКЉУЧЗАСОРТИРАЊЕ":"defaultsort","ПОДРАЗУМЕВАНОСОРТИРАЊЕКАТЕГОРИЈЕ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort", +"СТРАНИЦАУКАТЕГОРИЈИ":"pagesincategory","СТРАНАУКАТЕГОРИЈИ":"pagesincategory","СТРАНИЦА_У_КАТЕГОРИЈИ":"pagesincategory","СТРАНА_У_КАТЕГОРИЈИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ВЕЛИЧИНАСТРАНИЦЕ":"pagesize","ВЕЛИЧИНАСТРАНЕ":"pagesize","ВЕЛИЧИНА_СТРАНИЦЕ":"pagesize","ВЕЛИЧИНА_СТРАНЕ":"pagesize","PAGESIZE":"pagesize","НИВОЗАШТИТЕ":"protectionlevel","НИВО_ЗАШТИТЕ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ИМЕСТРАНИЦЕ":"pagename","ИМЕ_СТРАНИЦЕ":"pagename","СТРАНИЦА":"pagename","PAGENAME":"pagename","ИМЕНАСТРАНИЦА":"pagenamee","ИМЕНА_СТРАНИЦА":"pagenamee","СТРАНИЦЕ":"pagenamee","PAGENAMEE":"pagenamee","ПУНОИМЕСТРАНИЦЕ":"fullpagename","ПУНОИМЕСТРАНЕ": +"fullpagename","ПУНО_ИМЕ_СТРАНИЦЕ":"fullpagename","ПУНО_ИМЕ_СТРАНЕ":"fullpagename","FULLPAGENAME":"fullpagename","ПУНАИМЕНАСТРАНИЦА":"fullpagenamee","ПУНАИМЕНАСТРАНА":"fullpagenamee","ПУНА_ИМЕНА_СТРАНИЦА":"fullpagenamee","ПУНА_ИМЕНА_СТРАНА":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ИМЕПОДСТРАНИЦЕ":"subpagename","ИМЕПОДСТРАНЕ":"subpagename","ИМЕ_ПОДСТРАНИЦЕ":"subpagename","ИМЕ_ПОДСТРАНЕ":"subpagename","SUBPAGENAME":"subpagename","ИМЕНАПОДСТРАНИЦА":"subpagenamee","ИМЕНАПОДСТРАНА":"subpagenamee","ИМЕНА_ПОДСТРАНИЦА":"subpagenamee","ИМЕНА_ПОДСТРАНА":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ИМЕОСНОВЕ":"basepagename","ИМЕ_ОСНОВЕ":"basepagename","BASEPAGENAME":"basepagename","ИМЕНАОСНОВА": +"basepagenamee","ИМЕНА_ОСНОВА":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ИМЕРАЗГОВОРА":"talkpagename","ИМЕ_РАЗГОВОРА":"talkpagename","TALKPAGENAME":"talkpagename","ИМЕНАРАЗГОВОРА":"talkpagenamee","ИМЕНА_РАЗГОВОРА":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","ИМЕЧЛАНКА":"subjectpagename","ИМЕ_ЧЛАНКА":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ИМЕНАЧЛАНАКА":"subjectpagenamee","ИМЕНА_ЧЛАНАКА":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИДРЕВИЗИЈЕ":"revisionid","ИД_РЕВИЗИЈЕ":"revisionid","REVISIONID":"revisionid","ДАНИЗМЕНЕ":"revisionday","ДАН_ИЗМЕНЕ":"revisionday","REVISIONDAY":"revisionday","ДАНИЗМЕНЕ2":"revisionday2","ДАН_ИЗМЕНЕ2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЕЦИЗМЕНЕ":"revisionmonth", +"МЕСЕЦ_ИЗМЕНЕ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЕЦИЗМЕНЕ1":"revisionmonth1","МЕСЕЦ_ИЗМЕНЕ1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОДИНАИЗМЕНЕ":"revisionyear","ГОДИНА_ИЗМЕНЕ":"revisionyear","REVISIONYEAR":"revisionyear","ВРЕМЕИЗМЕНЕ":"revisiontimestamp","ВРЕМЕ_ИЗМЕНЕ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","КОРИСНИКИЗМЕНЕ":"revisionuser","КОРИСНИК_ИЗМЕНЕ":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ИМЕНСКИПРОСТОР":"namespace","ИМЕНСКИ_ПРОСТОР":"namespace","NAMESPACE":"namespace","ИМЕНСКИПРОСТОРИ":"namespacee","ИМЕНСКИ_ПРОСТОРИ":"namespacee","NAMESPACEE":"namespacee","БРОЈИМЕНСКОГПРОСТОРА":"namespacenumber","NAMESPACENUMBER":"namespacenumber","РАЗГОВОР":"talkspace","TALKSPACE":"talkspace", +"РАЗГОВОРИ":"talkspacee","TALKSPACEE":"talkspacee","ИМЕНСКИПРОСТОРЧЛАНКА":"subjectspace","ИМЕНСКИ_ПРОСТОР_ЧЛАНКА":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ИМЕНСКИПРОСТОРЧЛАНАКА":"subjectspacee","ИМЕНСКИ_ПРОСТОР_ЧЛАНАКА":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","БРОЈЧЛАНАКА":"numberofarticles","БРОЈ_ЧЛАНАКА":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","БРОЈДАТОТЕКА":"numberoffiles","БРОЈ_ДАТОТЕКА":"numberoffiles","БРОЈФАЈЛОВА":"numberoffiles","БРОЈ_ФАЈЛОВА":"numberoffiles","NUMBEROFFILES":"numberoffiles","БРОЈКОРИСНИКА":"numberofusers","БРОЈ_КОРИСНИКА":"numberofusers","NUMBEROFUSERS":"numberofusers","БРОЈАКТИВНИХКОРИСНИКА":"numberofactiveusers","БРОЈ_АКТИВНИХ_КОРИСНИКА": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","БРОЈСТРАНИЦА":"numberofpages","БРОЈ_СТРАНИЦА":"numberofpages","NUMBEROFPAGES":"numberofpages","БРОЈАДМИНА":"numberofadmins","БРОЈ_АДМИНА":"numberofadmins","NUMBEROFADMINS":"numberofadmins","БРОЈИЗМЕНА":"numberofedits","БРОЈ_ИЗМЕНА":"numberofedits","NUMBEROFEDITS":"numberofedits","НАЗИВПРИКАЗА":"displaytitle","НАЗИВ_ПРИКАЗА":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТРЕНУТНИМЕСЕЦ":"currentmonth","ТРЕНУТНИ_МЕСЕЦ":"currentmonth","ТЕКУЋИМЕСЕЦ":"currentmonth","ТЕКУЋИ_МЕСЕЦ":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТРЕНУТНИМЕСЕЦ1":"currentmonth1","ТРЕНУТНИ_МЕСЕЦ1":"currentmonth1","ТЕКУЋИМЕСЕЦ1":"currentmonth1","ТЕКУЋИ_МЕСЕЦ1":"currentmonth1","CURRENTMONTH1":"currentmonth1", +"ТРЕНУТНИМЕСЕЦИМЕ":"currentmonthname","ИМЕТЕКУЋЕГМЕСЕЦА":"currentmonthname","ИМЕ_ТЕКУЋЕГ_МЕСЕЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","ТРЕНУТНИМЕСЕЦГЕН":"currentmonthnamegen","ТЕКУЋИМЕСЕЦГЕН":"currentmonthnamegen","ТЕКУЋИ_МЕСЕЦ_ГЕН":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ТРЕНУТНИМЕСЕЦСКР":"currentmonthabbrev","ТЕКУЋИМЕСЕЦСКР":"currentmonthabbrev","ТЕКУЋИ_МЕСЕЦ_СКР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТРЕНУТНИДАН":"currentday","ТЕКУЋИДАН":"currentday","ТЕКУЋИ_ДАН":"currentday","CURRENTDAY":"currentday","ТЕКУЋИДАН2":"currentday2","ТЕКУЋИ_ДАН_2":"currentday2","CURRENTDAY2":"currentday2","ТРЕНУТНИДАНИМЕ":"currentdayname","ИМЕТЕКУЋЕГДАНА":"currentdayname","ИМЕ_ТЕКУЋЕГ_ДАНА":"currentdayname", +"CURRENTDAYNAME":"currentdayname","ТРЕНУТНАГОДИНА":"currentyear","ТЕКУЋАГОДИНА":"currentyear","ТЕКУЋА_ГОДИНА":"currentyear","CURRENTYEAR":"currentyear","ТРЕНУТНОВРЕМЕ":"currenttime","ТЕКУЋЕВРЕМЕ":"currenttime","ТЕКУЋЕ_ВРЕМЕ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЋИСАТ":"currenthour","ТЕКУЋИ_САТ":"currenthour","CURRENTHOUR":"currenthour","ЛОКАЛНИМЕСЕЦ":"localmonth","ЛОКАЛНИ_МЕСЕЦ":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","ЛОКАЛНИМЕСЕЦ2":"localmonth1","ЛОКАЛНИ_МЕСЕЦ2":"localmonth1","LOCALMONTH1":"localmonth1","ИМЕЛОКАЛНОГМЕСЕЦА":"localmonthname","ИМЕ_ЛОКАЛНОГ_МЕСЕЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","ЛОКАЛНИМЕСЕЦГЕН":"localmonthnamegen","ЛОКАЛНИ_МЕСЕЦ_ГЕН":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen", +"ЛОКАЛНИМЕСЕЦСКР":"localmonthabbrev","ЛОКАЛНИ_МЕСЕЦ_СКР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ЛОКАЛНИДАН":"localday","ЛОКАЛНИ_ДАН":"localday","LOCALDAY":"localday","ЛОКАЛНИДАН2":"localday2","ЛОКАЛНИ_ДАН2":"localday2","LOCALDAY2":"localday2","ИМЕЛОКАЛНОГДАНА":"localdayname","ИМЕ_ЛОКАЛНОГ_ДАНА":"localdayname","LOCALDAYNAME":"localdayname","ЛОКАЛНАГОДИНА":"localyear","ЛОКАЛНА_ГОДИНА":"localyear","LOCALYEAR":"localyear","ЛОКАЛНОВРЕМЕ":"localtime","ЛОКАЛНО_ВРЕМЕ":"localtime","LOCALTIME":"localtime","ЛОКАЛНИСАТ":"localhour","ЛОКАЛНИ_САТ":"localhour","LOCALHOUR":"localhour","ИМЕСАЈТА":"sitename","SITENAME":"sitename","ТРЕНУТНАНЕДЕЉА":"currentweek","ТРЕНУТНА_НЕДЕЉА":"currentweek","ТЕКУЋАНЕДЕЉА":"currentweek","ТЕКУЋА_НЕДЕЉА":"currentweek", +"CURRENTWEEK":"currentweek","ТРЕНУТНИДОВ":"currentdow","ТЕКУЋИДУН":"currentdow","CURRENTDOW":"currentdow","ЛОКАЛНАНЕДЕЉА":"localweek","ЛОКАЛНА_НЕДЕЉА":"localweek","LOCALWEEK":"localweek","ЛОКАЛНИДУН":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЋЕИЗДАЊЕ":"currentversion","ТЕКУЋЕ_ИЗДАЊЕ":"currentversion","CURRENTVERSION":"currentversion","ТЕКУЋИОТИСАКВРЕМЕНА":"currenttimestamp","ТЕКУЋИ_ОТИСАК_ВРЕМЕНА":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТИСАКВРЕМЕНА":"localtimestamp","ОТИСАК_ВРЕМЕНА":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","СМЕРОЗНАКЕ":"directionmark","СМЕР _ОЗНАКЕ":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЈЕЗИКСАДРЖАЈА":"contentlanguage","ЈЕЗИК_САДРЖАЈА":"contentlanguage","CONTENTLANGUAGE":"contentlanguage", +"CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([abvgdđežzijklljmnnjoprstćufhcčdžšабвгдђежзијклљмнњопрстћуфхцчџш]+)(.*)$/usD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-srn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-srn.json new file mode 100644 index 0000000..846d701 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-srn.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__noinot__":"notoc","__geeninhoud__":"notoc","__notoc__":"notoc","__nopikturama__":"nogallery","__geen_galerij__":"nogallery","__nogallery__":"nogallery","__inotdwengi__":"forcetoc","__inhoud_dwingen__":"forcetoc","__forceerinhoud__":"forcetoc","__forcetoc__":"forcetoc","__inot__":"toc","__inhoud__":"toc","__toc__":"toc", +"__nokenkiskaki__":"noeditsection","__nietbewerkbaresectie__":"noeditsection","__noeditsection__":"noeditsection","__geenpaginanaamconversie__":"notitleconvert","__geentitelconversie__":"notitleconvert","__geentc__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__geeninhoudconversie__":"nocontentconvert","__geenic__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NIEUWESECTIELINK__":"newsectionlink","__NIEUWESECTIEKOPPELING__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__GEENNIEUWKOPJEKOPPELING__":"nonewsectionlink","__GEENNIEUWESECTIELINK__":"nonewsectionlink","__GEENNIEUWKOPJEVERWIJZING__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERBORGENCAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__GEENINDEX__":"noindex","__NOINDEX__":"noindex","__STATISCHEDOORVERWIJZING__":"staticredirect", +"__STATISCHEREDIRECT__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nre":"nse","nse":"nse","urlcoderen":"urlencode","codeerurl":"urlencode","urlencode":"urlencode","kleerste":"lcfirst","lcfirst":"lcfirst","gleerste":"ucfirst","hleerste":"ucfirst","ucfirst":"ucfirst","kl":"lc","lc":"lc","hl":"uc","uc":"uc","lokaleurl":"localurl","localurl":"localurl","lokaleurle":"localurle","localurle":"localurle","volledigeurl":"fullurl","fullurl":"fullurl","volledigeurle":"fullurle","fullurle":"fullurle","canoiekeurl":"canonicalurl","canonicalurl":"canonicalurl","canoniekeurle":"canonicalurle","canonicalurle":"canonicalurle","formatteernum":"formatnum","numformatteren":"formatnum","formatnum":"formatnum","grammatica":"grammar","grammar":"grammar","geslacht":"gender","gender":"gender","meervoud":"plural","plural":"plural","bidi":"bidi","#taal":"language","#language":"language", +"linksopvullen":"padleft","padleft":"padleft","rechtsopvullen":"padright","padright":"padright","ankercoderen":"anchorencode","codeeranker":"anchorencode","anchorencode":"anchorencode","bestandspad":"filepath","filepath":"filepath","paginaid":"pageid","pageid":"pageid","int":"int","#spesyal":"special","#speciaal":"special","#special":"special","#speciaale":"speciale","#speciale":"speciale","#label":"tag","#tag":"tag","#datumopmaak":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#categorieboom":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#aanroepen":"invoke","#invoke":"invoke","#related":"related","#als":"if","#if":"if","#alsgelijk":"ifeq","#ifeq":"ifeq","#schakelen":"switch","#switch":"switch","#alsbestaat":"ifexist","#ifexist":"ifexist","#alsexpressie":"ifexpr","#ifexpr":"ifexpr","#alsfout":"iferror","#iferror":"iferror","#tijd":"time","#time":"time","#tijdl":"timel","#timel":"timel","#expressie": +"expr","#expr":"expr","#relatiefnaarabsoluut":"rel2abs","#rel2abs":"rel2abs","#paginanaamdelen":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","geenexternetaalkoppelingen":"noexternallanglinks","geenexternetaalverwijzingen":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschap":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","artikelpad":"articlepath","articlepath":"articlepath","server":"server","servernaam":"servername","servername":"servername","scriptpad":"scriptpath","scriptpath":"scriptpath","stijlpad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"PAPIRANUMRO":"numberofpages","AANTALPAGINAS":"numberofpages","AANTALPAGINA'S":"numberofpages","AANTALPAGINA’S":"numberofpages","NUMBEROFPAGES":"numberofpages","MASYINNUMRO":"numberofusers","AANTALGEBRUIKERS": +"numberofusers","NUMBEROFUSERS":"numberofusers","AANTALACTIEVEGEBRUIKERS":"numberofactiveusers","ACTIEVEGEBRUIKERS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","PAPIRALEGIMNUMRO":"numberofarticles","AANTALARTIKELEN":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","GEFRENUMRO":"numberoffiles","AANTALBESTANDEN":"numberoffiles","NUMBEROFFILES":"numberoffiles","AANTALBEHEERDERS":"numberofadmins","AANTALADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","AANTALINGROEP":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","KENKINUMRO":"numberofedits","AANTALBEWERKINGEN":"numberofedits","NUMBEROFEDITS":"numberofedits","STANDAARDSORTERING":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINASINCATEGORIE":"pagesincategory","PAGINASINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGINAGROOTTE":"pagesize","PAGESIZE":"pagesize", +"BEVEILIGINGSNIVEAU":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NENPREKIE":"namespacee","NAAMRUIMTEE":"namespacee","NAMESPACEE":"namespacee","NAAMRUIMTENUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","TAKIPREKI":"talkspace","OVERLEGRUIMTE":"talkspace","TALKSPACE":"talkspace","TAKIPREKIE":"talkspacee","OVERLEGRUIMTEE":"talkspacee","TALKSPACEE":"talkspacee","ONDERWERPRUIMTE":"subjectspace","ARTIKELRUIMTE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ONDERWERPRUIMTEE":"subjectspacee","ARTIKELRUIMTEE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAPIRANEN":"pagename","PAGINANAAM":"pagename","PAGENAME":"pagename","PAPIRANENE":"pagenamee","PAGINANAAME":"pagenamee","PAGENAMEE":"pagenamee","VOLLEDIGEPAGINANAAM":"fullpagename","FULLPAGENAME":"fullpagename","VOLLEDIGEPAGINANAAME":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGINANAAM":"rootpagename", +"ROOTPAGENAME":"rootpagename","ROOTPAGINANAAME":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","BASISPAGINANAAM":"basepagename","BASEPAGENAME":"basepagename","BASISPAGINANAAME":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DEELPAGINANAAM":"subpagename","SUBPAGENAME":"subpagename","DEELPAGINANAAME":"subpagenamee","SUBPAGENAMEE":"subpagenamee","OVERLEGPAGINANAAM":"talkpagename","TALKPAGENAME":"talkpagename","OVERLEGPAGINANAAME":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","ONDERWERPPAGINANAAM":"subjectpagename","ARTIKELPAGINANAAM":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ONDERWERPPAGINANAAME":"subjectpagenamee","ARTIKELPAGINANAAME":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","VERSIEID":"revisionid","REVISIONID":"revisionid","VERSIEDAG":"revisionday","REVISIONDAY":"revisionday","VERSIEDAG2":"revisionday2","REVISIONDAY2":"revisionday2","VERSIEMAAND":"revisionmonth","REVISIONMONTH": +"revisionmonth","VERSIEMAAND1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","VERSIEJAAR":"revisionyear","REVISIONYEAR":"revisionyear","VERSIETIJD":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","VERSIEGEBRUIKER":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NENPREKI":"namespace","NAAMRUIMTE":"namespace","NAMESPACE":"namespace","WEERGEGEVENTITEL":"displaytitle","TOONTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMOTNH":"currentmonth","DISIMUN":"currentmonth","HUIDIGEMAAND":"currentmonth","HUIDIGEMAAND2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","HUIDIGEMAAND1":"currentmonth1","CURRENTMONTH1":"currentmonth1","DISIMUNNEN":"currentmonthname","HUIDIGEMAANDNAAM":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","DISIMUNNENGEN":"currentmonthnamegen","HUIDIGEMAANDGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","DISIMUNSH":"currentmonthabbrev", +"HUIDIGEMAANDAFK":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DISIDEY":"currentday","HUIDIGEDAG":"currentday","CURRENTDAY":"currentday","DISIDEY2":"currentday2","HUIDIGEDAG2":"currentday2","CURRENTDAY2":"currentday2","DISIDEYNEN":"currentdayname","HUIDIGEDAGNAAM":"currentdayname","CURRENTDAYNAME":"currentdayname","DISIYARI":"currentyear","HUIDIGJAAR":"currentyear","CURRENTYEAR":"currentyear","DISITEN":"currenttime","HUIDIGETIJD":"currenttime","CURRENTTIME":"currenttime","DISIYURU":"currenthour","HUIDIGUUR":"currenthour","CURRENTHOUR":"currenthour","PRESIMUN":"localmonth","PLAATSELIJKEMAAND":"localmonth","LOKALEMAAND":"localmonth","LOKALEMAAND2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALEMAAND1":"localmonth1","LOCALMONTH1":"localmonth1","PRESIMUNNEN":"localmonthname","PLAATSELIJKEMAANDNAAM":"localmonthname","LOKALEMAANDNAAM":"localmonthname","LOCALMONTHNAME":"localmonthname","PRESIMUNNENGEN":"localmonthnamegen","PLAATSELIJKEMAANDNAAMGEN" +:"localmonthnamegen","LOKALEMAANDNAAMGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","PRESIMUNSH":"localmonthabbrev","PLAATSELIJKEMAANDAFK":"localmonthabbrev","LOKALEMAANDAFK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","PRESIDEY":"localday","PLAATSELIJKEDAG":"localday","LOKALEDAG":"localday","LOCALDAY":"localday","PRESIDEY2":"localday2","PLAATSELIJKEDAG2":"localday2","LOKALEDAG2":"localday2","LOCALDAY2":"localday2","PRESIDEYNEN":"localdayname","PLAATSELIJKEDAGNAAM":"localdayname","LOKALEDAGNAAM":"localdayname","LOCALDAYNAME":"localdayname","PRESIYARI":"localyear","PLAATSELIJKJAAR":"localyear","LOKAALJAAR":"localyear","LOCALYEAR":"localyear","PRESITEN":"localtime","PLAATSELIJKETIJD":"localtime","LOKALETIJD":"localtime","LOCALTIME":"localtime","PRESIYURU":"localhour","PLAATSELIJKUUR":"localhour","LOKAALUUR":"localhour","LOCALHOUR":"localhour","SITENAAM":"sitename","SITENAME":"sitename","HUIDIGEWEEK":"currentweek","CURRENTWEEK":"currentweek","HUIDIGEDVDW": +"currentdow","CURRENTDOW":"currentdow","PLAATSELIJKEWEEK":"localweek","LOKALEWEEK":"localweek","LOCALWEEK":"localweek","PLAATSELIJKEDVDW":"localdow","LOKALEDVDW":"localdow","LOCALDOW":"localdow","VERSIEGROOTTE":"revisionsize","REVISIONSIZE":"revisionsize","HUIDIGEVERSIE":"currentversion","CURRENTVERSION":"currentversion","HUIDIGETIJDSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","PLAATSELIJKETIJDSTEMPEL":"localtimestamp","LOKALETIJDSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","RICHTINGMARKERING":"directionmark","RICHTINGSMARKERING":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHOUDSTAAL":"contentlanguage","INHOUDTAAL":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zäöüïëéèà]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ss.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ss.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ss.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-st.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-st.json new file mode 100644 index 0000000..8f6dc2f --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-st.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([A-Za-zŠÒŌÈĒšòōèē]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-stq.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-stq.json new file mode 100644 index 0000000..d777619 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-stq.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__kein_inhaltsverzeichnis__":"notoc","__keininhaltsverzeichnis__":"notoc","__notoc__":"notoc","__keine_galerie__":"nogallery","__keinegalerie__":"nogallery","__nogallery__":"nogallery","__inhaltsverzeichnis_erzwingen__":"forcetoc","__forcetoc__":"forcetoc","__inhaltsverzeichnis__":"toc","__toc__":"toc", +"__abschnitte_nicht_bearbeiten__":"noeditsection","__noeditsection__":"noeditsection","__keine_titelkonvertierung__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__keine_inhaltskonvertierung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEUER_ABSCHNITTSLINK__":"newsectionlink","__PLUS_LINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KEIN_NEUER_ABSCHNITTSLINK__":"nonewsectionlink","__KEIN_PLUS_LINK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERSTECKTE_KATEGORIE__":"hiddencat","__WARTUNGSKATEGORIE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXIEREN__":"index","__INDIZIEREN__":"index","__INDEX__":"index","__NICHT_INDEXIEREN__":"noindex","__KEIN_INDEX__":"noindex","__NICHT_INDIZIEREN__":"noindex","__NOINDEX__":"noindex","__PERMANENTE_WEITERLEITUNG__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr_url":"nse","nse":"nse","urlenkodiert":"urlencode","urlencode":"urlencode","initial_klein":"lcfirst","lcfirst":"lcfirst","initial_gross":"ucfirst","ucfirst":"ucfirst","klein":"lc","lc":"lc","gross":"uc","uc":"uc","lokale_url":"localurl","localurl":"localurl","lokale_url_c":"localurle","localurle":"localurle","vollständige_url":"fullurl","fullurl":"fullurl","vollständige_url_c":"fullurle","fullurle":"fullurle","kanonische_url":"canonicalurl","canonicalurl":"canonicalurl","kanonische_url_c":"canonicalurle","canonicalurle":"canonicalurle","zahlenformat":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","geschlecht":"gender","gender":"gender","plural":"plural","bidi":"bidi","#sprache":"language","#language":"language","füllenlinks":"padleft","padleft":"padleft","füllenrechts":"padright","padright": +"padright","ankerenkodiert":"anchorencode","sprungmarkeenkodiert":"anchorencode","anchorencode":"anchorencode","dateipfad":"filepath","filepath":"filepath","seitenid":"pageid","seitenkennung":"pageid","pageid":"pageid","nachricht":"int","int":"int","#spezial":"special","#special":"special","#speziale":"speciale","#speciale":"speciale","#erweiterung":"tag","#tag":"tag","#datumsformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#ziel":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#aufrufen":"invoke","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#wechsle":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorienbaum":"categorytree","#kategoriebaum":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#abschnitt":"lst","#lstx":"lstx","#section-x":"lstx", +"#abschnitt-x":"lstx","#lsth":"lsth","#section-h":"lsth","keineexternensprachlinks":"noexternallanglinks","keine_externen_sprachlinks":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschaft":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpfad":"articlepath","articlepath":"articlepath","server":"server","servername":"servername","skriptpfad":"scriptpath","scriptpath":"scriptpath","stilpfad":"stylepath","stylepfad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbrepositoriumsname":"wbreponame","wbreponame":"wbreponame"},{"BENUTZER_IN_GRUPPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTIERUNG":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SEITEN_IN_KATEGORIE":"pagesincategory","SEITEN_KAT":"pagesincategory","SEITENINKAT":"pagesincategory","PAGESINCATEGORY": +"pagesincategory","PAGESINCAT":"pagesincategory","SEITENGRÖSSE":"pagesize","PAGESIZE":"pagesize","SCHUTZSTATUS":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SEITENNAME":"pagename","PAGENAME":"pagename","SEITENNAME_URL":"pagenamee","PAGENAMEE":"pagenamee","VOLLER_SEITENNAME":"fullpagename","FULLPAGENAME":"fullpagename","VOLLER_SEITENNAME_URL":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNTERSEITE":"subpagename","SUBPAGENAME":"subpagename","UNTERSEITE_URL":"subpagenamee","SUBPAGENAMEE":"subpagenamee","STAMMSEITE":"rootpagename","ROOTPAGENAME":"rootpagename","STAMMSEITE_URL":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","OBERSEITE":"basepagename","BASEPAGENAME":"basepagename","OBERSEITE_URL":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSSIONSSEITE":"talkpagename","DISK":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSEITE_URL":"talkpagenamee","DISK_URL":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"HAUPTSEITENNAME":"subjectpagename","VORDERSEITE":"subjectpagename","HAUPTSEITE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","HAUPTSEITENNAME_URL":"subjectpagenamee","VORDERSEITE_URL":"subjectpagenamee","HAUPTSEITE_URL":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid","VERSIONSID":"revisionid","REVISIONID":"revisionid","REVISIONSTAG":"revisionday","VERSIONSTAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSTAG2":"revisionday2","VERSIONSTAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMONAT":"revisionmonth","VERSIONSMONAT":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONSMONAT1":"revisionmonth1","VERSIONSMONAT1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","REVISIONSJAHR":"revisionyear","VERSIONSJAHR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSZEITSTEMPEL":"revisiontimestamp","VERSIONSZEITSTEMPEL":"revisiontimestamp", +"REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSBENUTZER":"revisionuser","VERSIONSBENUTZER":"revisionuser","REVISIONUSER":"revisionuser","KASKADENQUELLEN":"cascadingsources","CASCADINGSOURCES":"cascadingsources","NAMENSRAUM":"namespace","NAMESPACE":"namespace","NAMENSRAUM_URL":"namespacee","NAMESPACEE":"namespacee","NAMENSRAUMNUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSNAMENSRAUM":"talkspace","DISK_NR":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSNAMENSRAUM_URL":"talkspacee","DISK_NR_URL":"talkspacee","TALKSPACEE":"talkspacee","HAUPTNAMENSRAUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","HAUPTNAMENSRAUM_URL":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKELANZAHL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DATEIANZAHL":"numberoffiles","NUMBEROFFILES":"numberoffiles","BENUTZERANZAHL":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTIVE_BENUTZER": +"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SEITENANZAHL":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINANZAHL":"numberofadmins","NUMBEROFADMINS":"numberofadmins","BEARBEITUNGSANZAHL":"numberofedits","NUMBEROFEDITS":"numberofedits","SEITENTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JETZIGER_MONAT":"currentmonth","JETZIGER_MONAT_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JETZIGER_MONAT_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JETZIGER_MONATSNAME":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","JETZIGER_MONATSNAME_GENITIV":"currentmonthnamegen","JETZIGER_MONATSNAME_GEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JETZIGER_MONATSNAME_KURZ":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JETZIGER_KALENDERTAG":"currentday","JETZIGER_TAG":"currentday","CURRENTDAY":"currentday","JETZIGER_KALENDERTAG_2":"currentday2","JETZIGER_TAG_2": +"currentday2","CURRENTDAY2":"currentday2","JETZIGER_WOCHENTAG":"currentdayname","CURRENTDAYNAME":"currentdayname","JETZIGES_JAHR":"currentyear","CURRENTYEAR":"currentyear","JETZIGE_UHRZEIT":"currenttime","CURRENTTIME":"currenttime","JETZIGE_STUNDE":"currenthour","CURRENTHOUR":"currenthour","LOKALER_MONAT":"localmonth","LOKALER_MONAT_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALER_MONAT_1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALER_MONATSNAME":"localmonthname","LOCALMONTHNAME":"localmonthname","LOKALER_MONATSNAME_GENITIV":"localmonthnamegen","LOKALER_MONATSNAME_GEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALER_MONATSNAME_KURZ":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALER_KALENDERTAG":"localday","LOKALER_TAG":"localday","LOCALDAY":"localday","LOKALER_KALENDERTAG_2":"localday2","LOKALER_TAG_2":"localday2","LOCALDAY2":"localday2","LOKALER_WOCHENTAG":"localdayname","LOCALDAYNAME":"localdayname","LOKALES_JAHR" +:"localyear","LOCALYEAR":"localyear","LOKALE_UHRZEIT":"localtime","LOCALTIME":"localtime","LOKALE_STUNDE":"localhour","LOCALHOUR":"localhour","PROJEKTNAME":"sitename","SITENAME":"sitename","JETZIGE_KALENDERWOCHE":"currentweek","JETZIGE_WOCHE":"currentweek","CURRENTWEEK":"currentweek","JETZIGER_WOCHENTAG_ZAHL":"currentdow","CURRENTDOW":"currentdow","LOKALE_KALENDERWOCHE":"localweek","LOKALE_WOCHE":"localweek","LOCALWEEK":"localweek","LOKALER_WOCHENTAG_ZAHL":"localdow","LOCALDOW":"localdow","VERSIONSGRÖSSE":"revisionsize","REVISIONSIZE":"revisionsize","JETZIGE_VERSION":"currentversion","CURRENTVERSION":"currentversion","JETZIGER_ZEITSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALER_ZEITSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","TEXTAUSRICHTUNG":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHALTSSPRACHE":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE": +"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöüßa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-su.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-su.json new file mode 100644 index 0000000..04dbf3d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-su.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__tanpadaftarisi__":"notoc","__nirdasi__":"notoc","__notoc__":"notoc","__tanpagaleri__":"nogallery","__nirgal__":"nogallery","__nogallery__":"nogallery","__paksadaftarisi__":"forcetoc","__paksadasi__":"forcetoc","__forcetoc__":"forcetoc","__daftarisi__":"toc","__dasi__":"toc","__toc__":"toc","__tanpasuntinganbagian__": +"noeditsection","__nirsuba__":"noeditsection","__noeditsection__":"noeditsection","__tanpakonversijudul__":"notitleconvert","__nirkodul__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__tanpakonversiisi__":"nocontentconvert","__nirkosi__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__PRANALABAGIANBARU__":"newsectionlink","__PRABABA__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","_TANPAPRANALABAGIANBARU__":"nonewsectionlink","__NIRPRABABA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORITERSEMBUNYI__":"hiddencat","__KATSEM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__TANPAINDEKS__":"noindex","__NIRDEKS__":"noindex","__NOINDEX__":"noindex","__PENGALIHANSTATIK__":"staticredirect","__PENGALIHANSTATIS__":"staticredirect","__PETIK__":"staticredirect","__PETIS__": +"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"rn":"ns","runam":"ns","ns":"ns","nse":"nse","kodeurl":"urlencode","kodu":"urlencode","urlencode":"urlencode","akc":"lcfirst","awalkecil":"lcfirst","lcfirst":"lcfirst","abs":"ucfirst","awalbesar":"ucfirst","ucfirst":"ucfirst","kc":"lc","kecil":"lc","hurufkecil":"lc","lc":"lc","bs":"uc","besar":"uc","hurufbesar":"uc","uc":"uc","urllokal":"localurl","localurl":"localurl","urllokale":"localurle","localurle":"localurle","urllengkap":"fullurl","fullurl":"fullurl","urllengkape":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatangka":"formatnum","forang":"formatnum","formatnum":"formatnum","tatabahasa":"grammar","tasa":"grammar","grammar":"grammar","jantina":"gender","gender":"gender","jamak":"plural","plural":"plural","bidi":"bidi","#bahasa": +"language","#bhs":"language","#language":"language","isikiri":"padleft","iki":"padleft","padleft":"padleft","isikanan":"padright","ika":"padright","padright":"padright","kodejangkar":"anchorencode","kojang":"anchorencode","anchorencode":"anchorencode","lokasiberkas":"filepath","lober":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#istimewa":"special","#spesial":"special","#special":"special","#speciale":"speciale","#kata_kunci":"tag","#takun":"tag","#tag":"tag","#formattanggal":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#pinta":"invoke","#invoke":"invoke","#related":"related","#jika":"if","#if":"if","#jikasama":"ifeq","#ifeq":"ifeq","#pilih":"switch","#switch":"switch","#jikaada":"ifexist","#ifexist":"ifexist","#jikahitung":"ifexpr","#ifexpr":"ifexpr","#jikasalah":"iferror","#iferror":"iferror","#waktu":"time","#time":"time","#waktu1":"timel","#timel":"timel","#hitung":"expr", +"#expr":"expr","#rel2abs":"rel2abs","#bagianjudul":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","peladen":"server","server":"server","namapeladen":"servername","namaserver":"servername","nampel":"servername","servername":"servername","lokasiskrip":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"JUMLAHDIKELOMPOK":"numberingroup","JULDIPOK":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","URUTANBAKU":"defaultsort","UBUR":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","HALAMANDIKATEGORI":"pagesincategory","HALDIKAT": +"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","BESARHALAMAN":"pagesize","BESMAN":"pagesize","PAGESIZE":"pagesize","TINGKATPERLINDUNGAN":"protectionlevel","TIPER":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMAHALAMAN":"pagename","NAMMAN":"pagename","PAGENAME":"pagename","NAMAHALAMANE":"pagenamee","NAMMANE":"pagenamee","PAGENAMEE":"pagenamee","NAMAHALAMANLENGKAP":"fullpagename","NAMALENGKAPHALAMAN":"fullpagename","NAMMANKAP":"fullpagename","FULLPAGENAME":"fullpagename","AMAHALAMANLENGKAPE":"fullpagenamee","NAMALENGKAPHALAMANE":"fullpagenamee","NAMMANKAPE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NAMASUBHALAMAN":"subpagename","NAMAUPAHALAMAN":"subpagename","NAMUMAN":"subpagename","SUBPAGENAME":"subpagename","NAMASUBHALAMANE":"subpagenamee","NAMAUPAHALAMANE":"subpagenamee","NAMUMANE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee", +"NAMAHALAMANDASAR":"basepagename","NAMADASARHALAMAN":"basepagename","NAMMANSAR":"basepagename","BASEPAGENAME":"basepagename","NAMAHALAMANDASARE":"basepagenamee","NAMADASARHALAMANE":"basepagenamee","NAMMANSARE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NAMAHALAMANBICARA":"talkpagename","NAMMANBIR":"talkpagename","TALKPAGENAME":"talkpagename","NAMAHALAMANBICARAE":"talkpagenamee","NAMMANBIRE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NAMAHALAMANUTAMA":"subjectpagename","NAMAHALAMANARTIKEL":"subjectpagename","NAMMANTAMA":"subjectpagename","NAMMANTIKEL":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NAMAHALAMANUTAMAE":"subjectpagenamee","NAMAHALAMANARTIKELE":"subjectpagenamee","NAMMANTAMAE":"subjectpagenamee","NAMMANTIKELE":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDREVISI":"revisionid","IREV":"revisionid","REVISIONID":"revisionid","HARIREVISI":"revisionday","HAREV":"revisionday" +,"REVISIONDAY":"revisionday","HARIREVISI2":"revisionday2","HAREV2":"revisionday2","REVISIONDAY2":"revisionday2","BULANREVISI":"revisionmonth","BUREV":"revisionmonth","REVISIONMONTH":"revisionmonth","BULANREVISI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","TAHUNREVISI":"revisionyear","TAREV":"revisionyear","REVISIONYEAR":"revisionyear","STEMPELWAKTUREVISI":"revisiontimestamp","REKAMWAKTUREVISI":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","PENGGUNAREVISI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","RUANGNAMA":"namespace","RUNAM":"namespace","NAMESPACE":"namespace","RUANGNAMAE":"namespacee","RUNAME":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","RUANGBICARA":"talkspace","RUBIR":"talkspace","TALKSPACE":"talkspace","RUANGBICARAE":"talkspacee","RUBIRE":"talkspacee","TALKSPACEE":"talkspacee","RUANGUTAMA":"subjectspace","RUANGARTIKEL":"subjectspace","RUTAMA":"subjectspace","RUTIKEL":"subjectspace", +"SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","RUANGUTAMAE":"subjectspacee","RUANGARTIKELE":"subjectspacee","RUTAMAE":"subjectspacee","RUKELE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","JUMLAHARTIKEL":"numberofarticles","JUMKEL":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","JUMLAHBERKAS":"numberoffiles","JUMKAS":"numberoffiles","NUMBEROFFILES":"numberoffiles","JUMLAHPENGGUNA":"numberofusers","JUMPENG":"numberofusers","NUMBEROFUSERS":"numberofusers","JUMLAHPENGGUNAAKTIF":"numberofactiveusers","JUMPENGTIF":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","JUMLAHHALAMAN":"numberofpages","JUMMAN":"numberofpages","NUMBEROFPAGES":"numberofpages","JUMLAHADMIN":"numberofadmins","JUMLAHPENGURUS":"numberofadmins","JUMAD":"numberofadmins","JURUS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","JUMLAHSUNTINGAN":"numberofedits","JUMTING":"numberofedits","NUMBEROFEDITS":"numberofedits","JUDULTAMPILAN":"displaytitle" +,"JUTAM":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","BULANKINI":"currentmonth","BULANKINI2":"currentmonth","BUKIN":"currentmonth","BUKIN2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","BULANKINI1":"currentmonth1","BUKIN1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NAMABULANKINI":"currentmonthname","NAMBUKIN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NAMAJENDERBULANKINI":"currentmonthnamegen","NAMJENBUKIN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NAMASINGKATBULANKINI":"currentmonthabbrev","BULANINISINGKAT":"currentmonthabbrev","NAMSINGBUKIN":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HARIKINI":"currentday","HARKIN":"currentday","CURRENTDAY":"currentday","HARIKINI2":"currentday2","HARKIN2":"currentday2","CURRENTDAY2":"currentday2","NAMAHARIKINI":"currentdayname","NAMHARKIN":"currentdayname","CURRENTDAYNAME":"currentdayname","TAHUNKINI":"currentyear","TAKIN":"currentyear", +"CURRENTYEAR":"currentyear","WAKTUKINI":"currenttime","WAKIN":"currenttime","CURRENTTIME":"currenttime","JAMKINI":"currenthour","JAKIN":"currenthour","CURRENTHOUR":"currenthour","BULANLOKAL":"localmonth","BULANLOKAL2":"localmonth","BULOK":"localmonth","BULOK2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","BULANLOKAL1":"localmonth1","BULOK1":"localmonth1","LOCALMONTH1":"localmonth1","NAMABULANLOKAL":"localmonthname","NAMBULOK":"localmonthname","LOCALMONTHNAME":"localmonthname","NAMAJENDERBULANLOKAL":"localmonthnamegen","NAMJENBULOK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","NAMASINGKATBULANLOKAL":"localmonthabbrev","NAMSINGBULOK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","HARILOKAL":"localday","HALOK":"localday","LOCALDAY":"localday","HARILOKAL2":"localday2","HALOK2":"localday2","LOCALDAY2":"localday2","NAMAHARILOKAL":"localdayname","NAMHALOK":"localdayname","LOCALDAYNAME":"localdayname","TAHUNLOKAL":"localyear","TALOK":"localyear", +"LOCALYEAR":"localyear","WAKTULOKAL":"localtime","WALOK":"localtime","LOCALTIME":"localtime","JAMLOKAL":"localhour","JALOK":"localhour","LOCALHOUR":"localhour","NAMASITUS":"sitename","NAMSIT":"sitename","SITENAME":"sitename","MINGGUKINI":"currentweek","MIKIN":"currentweek","CURRENTWEEK":"currentweek","HARIDALAMMINGGU":"currentdow","HADAMI":"currentdow","CURRENTDOW":"currentdow","MINGGULOKAL":"localweek","MIKAL":"localweek","LOCALWEEK":"localweek","HARIDALAMMINGGULOKAL":"localdow","HADAMIKAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIKINI":"currentversion","VERKIN":"currentversion","CURRENTVERSION":"currentversion","STEMPELWAKTUKINI":"currenttimestamp","STEMWAKIN":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","STEMPELWAKTULOKAL":"localtimestamp","STEMWAKAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARKAARAH":"directionmark","MARRAH":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","BAHASAISI":"contentlanguage", +"BHSISI":"contentlanguage","BASI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sv.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sv.json new file mode 100644 index 0000000..d3a1bc7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sv.json @@ -0,0 +1,10 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"quiz":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__ingeninnehållsförteckning__":"notoc","__notoc__":"notoc","__ingetgalleri__":"nogallery","__nogallery__":"nogallery","__alltidinnehållsförteckning__":"forcetoc","__forcetoc__":"forcetoc","__innehållsförteckning__":"toc","__toc__":"toc","__interedigerasektion__":"noeditsection","__noeditsection__": +"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NYTTAVSNITTLÄNK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__DOLDKAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXERA__":"index","__INDEX__":"index","__INTEINDEXERA_":"noindex","__NOINDEX__":"noindex","__STATISKOMDIRIGERING__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","lbförst":"lcfirst","lcfirst":"lcfirst","ucfirst":"ucfirst","sbförst":"ucfirst","lb":"lc","lc":"lc","sb":"uc","uc":"uc","lokalurl":"localurl","localurl":"localurl","lokalurle":"localurle","localurle":"localurle","fullturl":"fullurl","fullurl": +"fullurl","fullturle":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formateranum":"formatnum","formateratal":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","kön":"gender","gender":"gender","plural":"plural","bidi":"bidi","#språk":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filsökväg":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tagg":"tag","#tag":"tag","#formateradatum":"formatdate","#datumformat":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#om":"if","#if":"if","#omlika":"ifeq","#ifeq":"ifeq","#växel":"switch","#switch":"switch","#omfinns":"ifexist","#ifexist":"ifexist","#omutr":"ifexpr","#ifexpr":"ifexpr","#omfel":"iferror","#iferror":"iferror", +"#tid":"time","#time":"time","#tidl":"timel","#timel":"timel","#utr":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategoriträd":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servernamn":"servername","servername":"servername","skriptsökväg":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ANTALIGRUPP":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","STANDARDSORTERING":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","SIDORIKATEGORI":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT": +"pagesincategory","SIDSTORLEK":"pagesize","PAGESIZE":"pagesize","SKYDDSNIVÅ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SIDNAMN":"pagename","PAGENAME":"pagename","SIDNAMNE":"pagenamee","PAGENAMEE":"pagenamee","HELASIDNAMNET":"fullpagename","FULLPAGENAME":"fullpagename","HELASIDNAMNETE":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","UNDERSIDNAMN":"subpagename","SUBPAGENAME":"subpagename","UNDERSIDNAMNE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","GRUNDSIDNAMN":"basepagename","BASEPAGENAME":"basepagename","GRUNDSIDNAMNE":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DISKUSSIONSSIDNAMN":"talkpagename","TALKPAGENAME":"talkpagename","DISKUSSIONSSIDNAMNE":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONSID":"revisionid" +,"REVISIONID":"revisionid","REVISIONSDAG":"revisionday","REVISIONDAY":"revisionday","REVISIONSDAG2":"revisionday2","REVISIONDAY2":"revisionday2","REVISIONSMÅNAD":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONSÅR":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONSTIDSSTÄMPEL":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONSANVÄNDARE":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMNRYMD":"namespace","NAMESPACE":"namespace","NAMNRYMDE":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","DISKUSSIONSRYMD":"talkspace","TALKSPACE":"talkspace","DISKUSSIONSRYMDE":"talkspacee","TALKSPACEE":"talkspacee","ARTIKELRYMD":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ARTIKELRYMDE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ANTALARTIKLAR":"numberofarticles","NUMBEROFARTICLES":"numberofarticles", +"ANTALFILER":"numberoffiles","NUMBEROFFILES":"numberoffiles","ANTALANVÄNDARE":"numberofusers","NUMBEROFUSERS":"numberofusers","ANTALAKTIVAANVÄNDARE":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ANTALSIDOR":"numberofpages","NUMBEROFPAGES":"numberofpages","ANTALADMINS":"numberofadmins","ANTALADMINISTRATÖRER":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ANTALREDIGERINGAR":"numberofedits","NUMBEROFEDITS":"numberofedits","VISATITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","NUVARANDEMÅNAD":"currentmonth","NUMÅNAD":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","NUVARANDEMÅNAD1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NUVARANDEMÅNADSNAMN":"currentmonthname","NUMÅNADSNAMN":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","NUVARANDEMÅNADKORT":"currentmonthabbrev","NUMÅNADKORT":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev", +"NUVARANDEDAG":"currentday","NUDAG":"currentday","CURRENTDAY":"currentday","NUVARANDEDAG2":"currentday2","NUDAG2":"currentday2","CURRENTDAY2":"currentday2","NUVARANDEDAGSNAMN":"currentdayname","NUDAGSNAMN":"currentdayname","CURRENTDAYNAME":"currentdayname","NUVARANDEÅR":"currentyear","NUÅR":"currentyear","CURRENTYEAR":"currentyear","NUVARANDETID":"currenttime","NUTID":"currenttime","CURRENTTIME":"currenttime","NUVARANDETIMME":"currenthour","NUTIMME":"currenthour","CURRENTHOUR":"currenthour","LOKALMÅNAD":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALMÅNAD1":"localmonth1","LOCALMONTH1":"localmonth1","LOKALMÅNADSNAMN":"localmonthname","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOKALMÅNADKORT":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","LOKALDAG":"localday","LOCALDAY":"localday","LOKALDAG2":"localday2","LOCALDAY2":"localday2","LOKALDAGSNAMN":"localdayname","LOCALDAYNAME":"localdayname","LOKALTÅR":"localyear", +"LOCALYEAR":"localyear","LOKALTID":"localtime","LOCALTIME":"localtime","LOKALTIMME":"localhour","LOCALHOUR":"localhour","SAJTNAMN":"sitename","SITENAMN":"sitename","SITENAME":"sitename","NUVARANDEVECKA":"currentweek","NUVECKA":"currentweek","CURRENTWEEK":"currentweek","NUVARANDEVECKODAG":"currentdow","CURRENTDOW":"currentdow","LOKALVECKA":"localweek","LOCALWEEK":"localweek","LOKALVECKODAG":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","NUVARANDEVERSION":"currentversion","NUVERSION":"currentversion","CURRENTVERSION":"currentversion","NUTIDSTÄMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","LOKALTIDSTÄMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INNEHÅLLSSPRÅK":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zåäöéÅÄÖÉ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sw.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sw.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-sw.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-szl.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-szl.json new file mode 100644 index 0000000..5777c0e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-szl.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__bezspisu__":"notoc","__notoc__":"notoc","__bezgalerii__":"nogallery","__nogallery__":"nogallery","__zespisem__":"forcetoc","__wymuśspis__":"forcetoc","__forcetoc__":"forcetoc","__spis__":"toc","__toc__":"toc","__bezedycjisekcji__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert", +"__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LINKNOWEJSEKCJI__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__KATEGORIAUKRYTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKSUJ__":"index","__INDEX__":"index","__NIEINDEKSUJ__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"pn":"ns","ns":"ns","nse":"nse","urlencode":"urlencode","zmałej":"lcfirst","odmałej":"lcfirst","lcfirst":"lcfirst","zwielkiej":"ucfirst","zdużej":"ucfirst","odwielkiej":"ucfirst","oddużej":"ucfirst","ucfirst":"ucfirst","małe":"lc","lc":"lc","wielkie":"uc","duże":"uc","uc":"uc","localurl":"localurl","localurle":"localurle","pełnyurl":"fullurl","fullurl":"fullurl","fullurle":"fullurle", +"canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","odmiana":"grammar","grammar":"grammar","płeć":"gender","gender":"gender","mnoga":"plural","plural":"plural","bidi":"bidi","#język":"language","#language":"language","dolewej":"padleft","padleft":"padleft","doprawej":"padright","padright":"padright","anchorencode":"anchorencode","ścieżkapliku":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#specjalna":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#drzewokategorii":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx", +"#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#właściwość":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","ścieżkaartykułów":"articlepath","articlepath":"articlepath","serwer":"server","server":"server","nazwaserwera":"servername","servername":"servername","ścieżkaskryptu":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","SORTUJ":"defaultsort","DOMYŚLNIESORTUJ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","STRONYWKATEGORII":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","ROZMIARSTRONY":"pagesize","PAGESIZE":"pagesize","__POZIOMZABEZPIECZEŃ__":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","NAZWASTRONY":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","PELNANAZWASTRONY":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","NAZWAPODSTRONY":"subpagename","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BAZOWANAZWASTRONY":"basepagename","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","NAZWASTRONYDYSKUSJI":"talkpagename","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAZWAPRZESTRZENI":"namespace" +,"NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","DYSKUSJA":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTYKUŁÓW":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","PLIKÓW":"numberoffiles","NUMBEROFFILES":"numberoffiles","UŻYTKOWNIKÓW":"numberofusers","NUMBEROFUSERS":"numberofusers","LICZBAAKTYWNYCHUŻYTKOWNIKÓW":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","STRON":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINISTRATORÓW":"numberofadmins","NUMBEROFADMINS":"numberofadmins","EDYCJI":"numberofedits","NUMBEROFEDITS":"numberofedits","WYŚWIETLANYTYTUŁ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","AKTUALNYDZIEŃ":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","NAZWADNIA":"currentdayname","CURRENTDAYNAME":"currentdayname","AKTUALNYROK":"currentyear","CURRENTYEAR":"currentyear","AKTUALNYCZAS":"currenttime","CURRENTTIME":"currenttime","AKTUALNAGODZINA":"currenthour","CURRENTHOUR":"currenthour","MIESIĄC":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","MIESIĄCNAZWA":"localmonthname","LOCALMONTHNAME":"localmonthname","MIESIĄCNAZWAD":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MIESIĄCNAZWASKR":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DZIEŃ":"localday","LOCALDAY":"localday","DZIEŃ2":"localday2","LOCALDAY2":"localday2","DZIEŃTYGODNIA":"localdayname","LOCALDAYNAME":"localdayname","ROK":"localyear","LOCALYEAR":"localyear","CZAS":"localtime","LOCALTIME":"localtime","GODZINA":"localhour","LOCALHOUR":"localhour","PROJEKT": +"sitename","SITENAME":"sitename","AKTUALNYTYDZIEŃ":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","TYDZIEŃROKU":"localweek","LOCALWEEK":"localweek","DZIEŃTYGODNIANR":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","AKTUALNAWERSJA":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zęóąśłżźćńĘÓĄŚŁŻŹĆŃ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-szy.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-szy.json new file mode 100644 index 0000000..8f21bcd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-szy.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__無目錄__":"notoc","__无目录__":"notoc","__notoc__":"notoc","__無圖庫__":"nogallery","__无图库__":"nogallery","__nogallery__":"nogallery","__強制目錄__":"forcetoc","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__目錄__":"toc","__目录__":"toc","__toc__":"toc","__無段落編輯__": +"noeditsection","__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__不轉換標題__":"notitleconvert","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換內容__":"nocontentconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__新段落链接__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__无新段落链接__":"nonewsectionlink","__隱藏分類__":"hiddencat","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__索引__":"index","__NOINDEX__":"noindex","__无索引__":"noindex","__靜態重新導向__":"staticredirect","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__消除歧义__": +"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"命名空間":"ns","名字空间":"ns","ns":"ns","名称空间":"ns","命名空間e":"nse","名字空间e":"nse","nse":"nse","名称空间e":"nse","urlencode":"urlencode","url编码":"urlencode","lcfirst":"lcfirst","小写首字":"lcfirst","ucfirst":"ucfirst","大写首字":"ucfirst","lc":"lc","小写":"lc","uc":"uc","大写":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","fullurl":"fullurl","完整url":"fullurl","fullurle":"fullurle","完整url等同":"fullurle","canonicalurl":"canonicalurl","规范url":"canonicalurl","canonicalurle":"canonicalurle","规范url等同":"canonicalurle","formatnum":"formatnum","格式化数字":"formatnum","grammar":"grammar","语法":"grammar","性別":"gender","性":"gender","性别":"gender","gender":"gender","plural":"plural","复数":"plural","bidi":"bidi","#語言": +"language","#语言":"language","#language":"language","padleft":"padleft","左填充":"padleft","padright":"padright","右填充":"padright","anchorencode":"anchorencode","锚编码":"anchorencode","filepath":"filepath","文件路径":"filepath","頁面id":"pageid","页面id":"pageid","pageid":"pageid","int":"int","界面":"int","#special":"special","#特殊":"special","#speciale":"speciale","#特殊等同":"speciale","#tag":"tag","#标记":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#格式化日期":"formatdate","#日期格式化":"formatdate","#目標":"target","#目标":"target","#target":"target","#巴別":"babel","#巴别":"babel","#babel":"babel","#coordinates":"coordinates","#調動":"invoke","#调用":"invoke","#invoke":"invoke","#related":"related","#若":"if","#非空式":"if","#如果":"if","#if":"if","#相同式":"ifeq","#匹配式":"ifeq","#若相等":"ifeq","#如果相等":"ifeq","#ifeq":"ifeq","#轉換":"switch","#多选式":"switch","#多条件式": +"switch","#双射式":"switch","#开关":"switch","#转换":"switch","#switch":"switch","#存在式":"ifexist","#若有":"ifexist","#如有":"ifexist","#ifexist":"ifexist","#若表達式":"ifexpr","#若表达式":"ifexpr","#ifexpr":"ifexpr","#如果錯誤":"iferror","#错误式":"iferror","#如果错误":"iferror","#iferror":"iferror","#時間":"time","#时间":"time","#time":"time","#時間l":"timel","#时间l":"timel","#timel":"timel","#表達式":"expr","#计算式":"expr","#表达式":"expr","#expr":"expr","#rel2abs":"rel2abs","#标题组成部分":"titleparts","#titleparts":"titleparts","#分類樹":"categorytree","#分类树":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","隱藏跨語言連結":"noexternallanglinks","无外部语言连接":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#屬性":"property","#属性":"property","#property":"property","#statements" +:"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","条目路径":"articlepath","伺服器":"server","服务器":"server","server":"server","伺服器名稱":"servername","服务器名":"servername","servername":"servername","scriptpath":"scriptpath","脚本路径":"scriptpath","stylepath":"stylepath","样式路径":"stylepath","numberofwikis":"numberofwikis","wb報表名稱":"wbreponame","wb报告名":"wbreponame","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","组中用户数":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","默认排序":"defaultsort","默认排序关键字":"defaultsort","默认分类排序":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","分类中页面数":"pagesincategory","PAGESIZE":"pagesize","页面大小":"pagesize","PROTECTIONLEVEL":"protectionlevel","保护级别": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","頁面名稱":"pagename","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","页面名等同":"pagenamee","页面名称等同":"pagenamee","FULLPAGENAME":"fullpagename","页面全称":"fullpagename","完整页面名称":"fullpagename","FULLPAGENAMEE":"fullpagenamee","完整页面名称等同":"fullpagenamee","SUBPAGENAME":"subpagename","子页面名称":"subpagename","SUBPAGENAMEE":"subpagenamee","子页面名称等同":"subpagenamee","根頁面名稱":"rootpagename","ROOTPAGENAME":"rootpagename","根页面名称":"rootpagename","根頁面名稱E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","根页面名称等同":"rootpagenamee","BASEPAGENAME":"basepagename","基础页面名称":"basepagename","BASEPAGENAMEE":"basepagenamee","基础页面名称等同":"basepagenamee","TALKPAGENAME":"talkpagename","讨论页面名称":"talkpagename","对话页面名称": +"talkpagename","TALKPAGENAMEE":"talkpagenamee","讨论页面名称等同":"talkpagenamee","对话页面名称等同":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主名字空间页面名称":"subjectpagename","条目页面名称":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","主名字空间页面名称等同":"subjectpagenamee","条目页面名称等同":"subjectpagenamee","REVISIONID":"revisionid","修订ID":"revisionid","REVISIONDAY":"revisionday","修订日":"revisionday","REVISIONDAY2":"revisionday2","修订日2":"revisionday2","REVISIONMONTH":"revisionmonth","修订月":"revisionmonth","REVISIONMONTH1":"revisionmonth1","修订月1":"revisionmonth1","REVISIONYEAR":"revisionyear","修订年":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","修订时间戳":"revisiontimestamp","修訂使用者":"revisionuser","REVISIONUSER":"revisionuser","修订用户":"revisionuser", +"CASCADINGSOURCES":"cascadingsources","级联来源":"cascadingsources","命名空間":"namespace","名字空间":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","名字空间等同":"namespacee","命名空間數":"namespacenumber","名字空间编号":"namespacenumber","NAMESPACENUMBER":"namespacenumber","對話空間":"talkspace","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","讨论空间等同":"talkspacee","讨论名字空间等同":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主名字空间":"subjectspace","条目名字空间":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","主名字空间等同":"subjectspacee","条目名字空间等同":"subjectspacee","文章數":"numberofarticles","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","檔案數":"numberoffiles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles", +"使用者人數量":"numberofusers","用户数":"numberofusers","NUMBEROFUSERS":"numberofusers","活躍使用者人數":"numberofactiveusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","頁面數":"numberofpages","页面数":"numberofpages","NUMBEROFPAGES":"numberofpages","管理員數":"numberofadmins","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","编辑数":"numberofedits","顯示標題":"displaytitle","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","本月1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","本月名":"currentmonthname","本月名称":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月名属格":"currentmonthnamegen","本月名称属格":"currentmonthnamegen","本月縮寫": +"currentmonthabbrev","本月简称":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","今天2":"currentday2","CURRENTDAYNAME":"currentdayname","星期":"currentdayname","今天名":"currentdayname","今天名称":"currentdayname","CURRENTYEAR":"currentyear","今年":"currentyear","目前時間":"currenttime","当前时间":"currenttime","此时":"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","当前小时":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","本地月":"localmonth","本地月2":"localmonth","LOCALMONTH1":"localmonth1","本地月1":"localmonth1","LOCALMONTHNAME":"localmonthname","本地月份名":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","本地月历":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","本地月缩写":"localmonthabbrev","LOCALDAY":"localday","本地日":"localday","LOCALDAY2":"localday2","本地日2": +"localday2","LOCALDAYNAME":"localdayname","本地日名":"localdayname","LOCALYEAR":"localyear","本地年":"localyear","LOCALTIME":"localtime","本地时间":"localtime","LOCALHOUR":"localhour","本地小时":"localhour","網站名稱":"sitename","站点名称":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","本周":"currentweek","CURRENTDOW":"currentdow","当前DOW":"currentdow","LOCALWEEK":"localweek","本地周":"localweek","LOCALDOW":"localdow","本地DOW":"localdow","REVISIONSIZE":"revisionsize","修订大小":"revisionsize","目前版本":"currentversion","当前版本":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","当前时间戳":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","本地时间戳":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","方向标记":"directionmark","內容語言":"contentlanguage","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^()(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ta.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ta.json new file mode 100644 index 0000000..f626bd6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ta.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","பன்மை":"plural","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#சிறப்பு":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel", +"#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS": +"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","பக்க_அளவு":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE": +"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY": +"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([஀-௿]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tay.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tay.json new file mode 100644 index 0000000..8f21bcd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tay.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__無目錄__":"notoc","__无目录__":"notoc","__notoc__":"notoc","__無圖庫__":"nogallery","__无图库__":"nogallery","__nogallery__":"nogallery","__強制目錄__":"forcetoc","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__目錄__":"toc","__目录__":"toc","__toc__":"toc","__無段落編輯__": +"noeditsection","__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__不轉換標題__":"notitleconvert","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換內容__":"nocontentconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__新段落链接__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__无新段落链接__":"nonewsectionlink","__隱藏分類__":"hiddencat","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__索引__":"index","__NOINDEX__":"noindex","__无索引__":"noindex","__靜態重新導向__":"staticredirect","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__消除歧义__": +"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"命名空間":"ns","名字空间":"ns","ns":"ns","名称空间":"ns","命名空間e":"nse","名字空间e":"nse","nse":"nse","名称空间e":"nse","urlencode":"urlencode","url编码":"urlencode","lcfirst":"lcfirst","小写首字":"lcfirst","ucfirst":"ucfirst","大写首字":"ucfirst","lc":"lc","小写":"lc","uc":"uc","大写":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","fullurl":"fullurl","完整url":"fullurl","fullurle":"fullurle","完整url等同":"fullurle","canonicalurl":"canonicalurl","规范url":"canonicalurl","canonicalurle":"canonicalurle","规范url等同":"canonicalurle","formatnum":"formatnum","格式化数字":"formatnum","grammar":"grammar","语法":"grammar","性別":"gender","性":"gender","性别":"gender","gender":"gender","plural":"plural","复数":"plural","bidi":"bidi","#語言": +"language","#语言":"language","#language":"language","padleft":"padleft","左填充":"padleft","padright":"padright","右填充":"padright","anchorencode":"anchorencode","锚编码":"anchorencode","filepath":"filepath","文件路径":"filepath","頁面id":"pageid","页面id":"pageid","pageid":"pageid","int":"int","界面":"int","#special":"special","#特殊":"special","#speciale":"speciale","#特殊等同":"speciale","#tag":"tag","#标记":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#格式化日期":"formatdate","#日期格式化":"formatdate","#目標":"target","#目标":"target","#target":"target","#巴別":"babel","#巴别":"babel","#babel":"babel","#coordinates":"coordinates","#調動":"invoke","#调用":"invoke","#invoke":"invoke","#related":"related","#若":"if","#非空式":"if","#如果":"if","#if":"if","#相同式":"ifeq","#匹配式":"ifeq","#若相等":"ifeq","#如果相等":"ifeq","#ifeq":"ifeq","#轉換":"switch","#多选式":"switch","#多条件式": +"switch","#双射式":"switch","#开关":"switch","#转换":"switch","#switch":"switch","#存在式":"ifexist","#若有":"ifexist","#如有":"ifexist","#ifexist":"ifexist","#若表達式":"ifexpr","#若表达式":"ifexpr","#ifexpr":"ifexpr","#如果錯誤":"iferror","#错误式":"iferror","#如果错误":"iferror","#iferror":"iferror","#時間":"time","#时间":"time","#time":"time","#時間l":"timel","#时间l":"timel","#timel":"timel","#表達式":"expr","#计算式":"expr","#表达式":"expr","#expr":"expr","#rel2abs":"rel2abs","#标题组成部分":"titleparts","#titleparts":"titleparts","#分類樹":"categorytree","#分类树":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","隱藏跨語言連結":"noexternallanglinks","无外部语言连接":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#屬性":"property","#属性":"property","#property":"property","#statements" +:"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","条目路径":"articlepath","伺服器":"server","服务器":"server","server":"server","伺服器名稱":"servername","服务器名":"servername","servername":"servername","scriptpath":"scriptpath","脚本路径":"scriptpath","stylepath":"stylepath","样式路径":"stylepath","numberofwikis":"numberofwikis","wb報表名稱":"wbreponame","wb报告名":"wbreponame","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","组中用户数":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","默认排序":"defaultsort","默认排序关键字":"defaultsort","默认分类排序":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","分类中页面数":"pagesincategory","PAGESIZE":"pagesize","页面大小":"pagesize","PROTECTIONLEVEL":"protectionlevel","保护级别": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","頁面名稱":"pagename","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","页面名等同":"pagenamee","页面名称等同":"pagenamee","FULLPAGENAME":"fullpagename","页面全称":"fullpagename","完整页面名称":"fullpagename","FULLPAGENAMEE":"fullpagenamee","完整页面名称等同":"fullpagenamee","SUBPAGENAME":"subpagename","子页面名称":"subpagename","SUBPAGENAMEE":"subpagenamee","子页面名称等同":"subpagenamee","根頁面名稱":"rootpagename","ROOTPAGENAME":"rootpagename","根页面名称":"rootpagename","根頁面名稱E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","根页面名称等同":"rootpagenamee","BASEPAGENAME":"basepagename","基础页面名称":"basepagename","BASEPAGENAMEE":"basepagenamee","基础页面名称等同":"basepagenamee","TALKPAGENAME":"talkpagename","讨论页面名称":"talkpagename","对话页面名称": +"talkpagename","TALKPAGENAMEE":"talkpagenamee","讨论页面名称等同":"talkpagenamee","对话页面名称等同":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主名字空间页面名称":"subjectpagename","条目页面名称":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","主名字空间页面名称等同":"subjectpagenamee","条目页面名称等同":"subjectpagenamee","REVISIONID":"revisionid","修订ID":"revisionid","REVISIONDAY":"revisionday","修订日":"revisionday","REVISIONDAY2":"revisionday2","修订日2":"revisionday2","REVISIONMONTH":"revisionmonth","修订月":"revisionmonth","REVISIONMONTH1":"revisionmonth1","修订月1":"revisionmonth1","REVISIONYEAR":"revisionyear","修订年":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","修订时间戳":"revisiontimestamp","修訂使用者":"revisionuser","REVISIONUSER":"revisionuser","修订用户":"revisionuser", +"CASCADINGSOURCES":"cascadingsources","级联来源":"cascadingsources","命名空間":"namespace","名字空间":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","名字空间等同":"namespacee","命名空間數":"namespacenumber","名字空间编号":"namespacenumber","NAMESPACENUMBER":"namespacenumber","對話空間":"talkspace","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","讨论空间等同":"talkspacee","讨论名字空间等同":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主名字空间":"subjectspace","条目名字空间":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","主名字空间等同":"subjectspacee","条目名字空间等同":"subjectspacee","文章數":"numberofarticles","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","檔案數":"numberoffiles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles", +"使用者人數量":"numberofusers","用户数":"numberofusers","NUMBEROFUSERS":"numberofusers","活躍使用者人數":"numberofactiveusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","頁面數":"numberofpages","页面数":"numberofpages","NUMBEROFPAGES":"numberofpages","管理員數":"numberofadmins","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","编辑数":"numberofedits","顯示標題":"displaytitle","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","本月1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","本月名":"currentmonthname","本月名称":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月名属格":"currentmonthnamegen","本月名称属格":"currentmonthnamegen","本月縮寫": +"currentmonthabbrev","本月简称":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","今天2":"currentday2","CURRENTDAYNAME":"currentdayname","星期":"currentdayname","今天名":"currentdayname","今天名称":"currentdayname","CURRENTYEAR":"currentyear","今年":"currentyear","目前時間":"currenttime","当前时间":"currenttime","此时":"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","当前小时":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","本地月":"localmonth","本地月2":"localmonth","LOCALMONTH1":"localmonth1","本地月1":"localmonth1","LOCALMONTHNAME":"localmonthname","本地月份名":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","本地月历":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","本地月缩写":"localmonthabbrev","LOCALDAY":"localday","本地日":"localday","LOCALDAY2":"localday2","本地日2": +"localday2","LOCALDAYNAME":"localdayname","本地日名":"localdayname","LOCALYEAR":"localyear","本地年":"localyear","LOCALTIME":"localtime","本地时间":"localtime","LOCALHOUR":"localhour","本地小时":"localhour","網站名稱":"sitename","站点名称":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","本周":"currentweek","CURRENTDOW":"currentdow","当前DOW":"currentdow","LOCALWEEK":"localweek","本地周":"localweek","LOCALDOW":"localdow","本地DOW":"localdow","REVISIONSIZE":"revisionsize","修订大小":"revisionsize","目前版本":"currentversion","当前版本":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","当前时间戳":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","本地时间戳":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","方向标记":"directionmark","內容語言":"contentlanguage","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^()(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tcy.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tcy.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tcy.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-te.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-te.json new file mode 100644 index 0000000..87aaa3c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-te.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__విషయసూచికవద్దు__":"notoc","__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__విషయసూచిక__":"toc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__": +"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#ప్రత్యేక":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate", +"#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS": +"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","పేజీపేరు":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME": +"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([ఁ-౯]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-test.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-test.json new file mode 100644 index 0000000..a541ecc --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-test.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"splist":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"languages":true,"charinsert":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__": +"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#useliquidthreads":"useliquidthreads","#lqtpagelimit": +"lqtpagelimit","#target":"target","#babel":"babel","#translation":"translation","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#assessment":"assessment","pagebanner":"PAGEBANNER","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles", +"NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE": +"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","SHORTDESC":"shortdesc","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1": +"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tet.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tet.json new file mode 100644 index 0000000..414ab6e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tet.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__semtdc__":"notoc","__semsumário__":"notoc","__notoc__":"notoc","__semgaleria__":"nogallery","__nogallery__":"nogallery","__forcartdc__":"forcetoc","__forcarsumario__":"forcetoc","__forçartdc__":"forcetoc","__forçarsumário__":"forcetoc","__forcetoc__":"forcetoc","__tdc__":"toc","__sumário__":"toc","__sumario__":"toc", +"__toc__":"toc","__nãoeditarseção__":"noeditsection","__semeditarseção__":"noeditsection","__naoeditarsecao__":"noeditsection","__semeditarsecao__":"noeditsection","__noeditsection__":"noeditsection","__semconvertertitulo__":"notitleconvert","__semconvertertítulo__":"notitleconvert","__semct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__semconverterconteudo__":"nocontentconvert","__semconverterconteúdo__":"nocontentconvert","__semcc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LINKDENOVASECAO__":"newsectionlink","__LINKDENOVASEÇÃO__":"newsectionlink","__LIGACAODENOVASECAO__":"newsectionlink","__LIGAÇÃODENOVASEÇÃO__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__SEMLINKDENOVASECAO__":"nonewsectionlink","__SEMLINKDENOVASEÇÃO__":"nonewsectionlink","__SEMLIGACAODENOVASECAO__":"nonewsectionlink","__SEMLIGAÇÃODENOVASEÇÃO__":"nonewsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__CATEGORIAOCULTA__":"hiddencat","__CATOCULTA__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEXAR__":"index","__INDEX__":"index","__NAOINDEXAR__":"noindex","__NÃOINDEXAR__":"noindex","__NOINDEX__":"noindex","__REDIRECIONAMENTOESTATICO__":"staticredirect","__REDIRECIONAMENTOESTÁTICO__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","codificaurl":"urlencode","urlencode":"urlencode","primeiraminuscula":"lcfirst","primeiraminúscula":"lcfirst","lcfirst":"lcfirst","primeiramaiuscula":"ucfirst","primeiramaiúscula":"ucfirst","ucfirst":"ucfirst","minuscula":"lc","minúscula":"lc","minusculas":"lc","minúsculas":"lc","lc":"lc","maiuscula":"uc","maiúscula":"uc","maiusculas":"uc","maiúsculas":"uc","uc":"uc","localurl":"localurl","localurle":"localurle", +"urlcompleto":"fullurl","fullurl":"fullurl","urlcompletoc":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genero":"gender","gênero":"gender","gender":"gender","plural":"plural","bidi":"bidi","#idioma":"language","#language":"language","padleft":"padleft","padright":"padright","codificaancora":"anchorencode","codificaâncora":"anchorencode","anchorencode":"anchorencode","caminhodoarquivo":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seigual":"ifeq","#ifeq":"ifeq","#switch":"switch","#seexiste":"ifexist","#ifexist":"ifexist","#seexpr":"ifexpr","#ifexpr":"ifexpr","#seerro":"iferror","#iferror":"iferror","#time":"time","#timel":"timel","#expr": +"expr","#rel2abs":"rel2abs","#partesdotítulo":"titleparts","#partesdotitulo":"titleparts","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#trecho":"lst","#lstx":"lstx","#section-x":"lstx","#trecho-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#propriedade":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","servidor":"server","server":"server","nomedoservidor":"servername","servername":"servername","caminhodoscript":"scriptpath","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMERONOGRUPO":"numberingroup","NÚMERONOGRUPO":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","ORDENACAOPADRAO":"defaultsort","ORDENAÇÃOPADRÃO":"defaultsort","ORDEMPADRAO":"defaultsort","ORDEMPADRÃO":"defaultsort","DEFAULTSORT": +"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINASNACATEGORIA":"pagesincategory","PÁGINASNACATEGORIA":"pagesincategory","PAGINASNACAT":"pagesincategory","PÁGINASNACAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAMANHODAPAGINA":"pagesize","TAMANHODAPÁGINA":"pagesize","PAGESIZE":"pagesize","NIVELDEPROTECAO":"protectionlevel","NÍVELDEPROTEÇÃO":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMEDAPAGINA":"pagename","NOMEDAPÁGINA":"pagename","PAGENAME":"pagename","NOMEDAPAGINAC":"pagenamee","NOMEDAPÁGINAC":"pagenamee","PAGENAMEE":"pagenamee","NOMECOMPLETODAPAGINA":"fullpagename","NOMECOMPLETODAPÁGINA":"fullpagename","FULLPAGENAME":"fullpagename","NOMECOMPLETODAPAGINAC":"fullpagenamee","NOMECOMPLETODAPÁGINAC":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMEDASUBPAGINA":"subpagename","NOMEDASUBPÁGINA":"subpagename","SUBPAGENAME":"subpagename", +"NOMEDASUBPAGINAC":"subpagenamee","NOMEDASUBPÁGINAC":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","NOMEDAPAGINABASE":"basepagename","NOMEDAPÁGINABASE":"basepagename","BASEPAGENAME":"basepagename","NOMEDAPAGINABASEC":"basepagenamee","NOMEDAPÁGINABASEC":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMEDAPAGINADEDISCUSSAO":"talkpagename","NOMEDAPÁGINADEDISCUSSÃO":"talkpagename","TALKPAGENAME":"talkpagename","NOMEDAPAGINADEDISCUSSAOC":"talkpagenamee","NOMEDAPÁGINADEDISCUSSÃOC":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMEDAPAGINADECONTEUDO":"subjectpagename","NOMEDAPÁGINADECONTEÚDO":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMEDAPAGINADECONTEUDOC":"subjectpagenamee","NOMEDAPÁGINADECONTEÚDOC":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDDAREVISAO":"revisionid","IDDAREVISÃO":"revisionid","REVISIONID": +"revisionid","DIADAREVISAO":"revisionday","DIADAREVISÃO":"revisionday","REVISIONDAY":"revisionday","DIADAREVISAO2":"revisionday2","DIADAREVISÃO2":"revisionday2","REVISIONDAY2":"revisionday2","MESDAREVISAO":"revisionmonth","MÊSDAREVISÃO":"revisionmonth","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","ANODAREVISAO":"revisionyear","ANODAREVISÃO":"revisionyear","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","USUARIODAREVISAO":"revisionuser","USUÁRIODAREVISÃO":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","DOMINIO":"namespace","DOMÍNIO":"namespace","ESPACONOMINAL":"namespace","ESPAÇONOMINAL":"namespace","NAMESPACE":"namespace","DOMINIOC":"namespacee","DOMÍNIOC":"namespacee","ESPACONOMINALC":"namespacee","ESPAÇONOMINALC":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","PAGINADEDISCUSSAO":"talkspace","PÁGINADEDISCUSSÃO":"talkspace","TALKSPACE":"talkspace","PAGINADEDISCUSSAOC" +:"talkspacee","PÁGINADEDISCUSSÃOC":"talkspacee","TALKSPACEE":"talkspacee","PAGINADECONTEUDO":"subjectspace","PAGINADECONTEÚDO":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","PAGINADECONTEUDOC":"subjectspacee","PAGINADECONTEÚDOC":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMERODEARTIGOS":"numberofarticles","NÚMERODEARTIGOS":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMERODEARQUIVOS":"numberoffiles","NÚMERODEARQUIVOS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMERODEUSUARIOS":"numberofusers","NÚMERODEUSUÁRIOS":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMERODEUSUARIOSATIVOS":"numberofactiveusers","NÚMERODEUSUÁRIOSATIVOS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMERODEPAGINAS":"numberofpages","NÚMERODEPÁGINAS":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMERODEADMINISTRADORES":"numberofadmins","NÚMERODEADMINISTRADORES":"numberofadmins", +"NUMBEROFADMINS":"numberofadmins","NUMERODEEDICOES":"numberofedits","NÚMERODEEDIÇÕES":"numberofedits","NUMBEROFEDITS":"numberofedits","EXIBETITULO":"displaytitle","EXIBETÍTULO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MESATUAL":"currentmonth","MESATUAL2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MESATUAL1":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMEDOMESATUAL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESATUALABREV":"currentmonthabbrev","MESATUALABREVIADO":"currentmonthabbrev","ABREVIATURADOMESATUAL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","DIAATUAL":"currentday","CURRENTDAY":"currentday","DIAATUAL2":"currentday2","CURRENTDAY2":"currentday2","NOMEDODIAATUAL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANOATUAL":"currentyear","CURRENTYEAR":"currentyear","HORARIOATUAL":"currenttime","CURRENTTIME":"currenttime","HORAATUAL": +"currenthour","CURRENTHOUR":"currenthour","MESLOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESLOCAL1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEDOMESLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","MESLOCALABREV":"localmonthabbrev","MESLOCALABREVIADO":"localmonthabbrev","ABREVIATURADOMESLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","DIALOCAL":"localday","LOCALDAY":"localday","DIALOCAL2":"localday2","LOCALDAY2":"localday2","NOMEDODIALOCAL":"localdayname","LOCALDAYNAME":"localdayname","ANOLOCAL":"localyear","LOCALYEAR":"localyear","HORARIOLOCAL":"localtime","LOCALTIME":"localtime","HORALOCAL":"localhour","LOCALHOUR":"localhour","NOMEDOSITE":"sitename","NOMEDOSÍTIO":"sitename","NOMEDOSITIO":"sitename","SITENAME":"sitename","SEMANAATUAL":"currentweek","CURRENTWEEK":"currentweek","DIADASEMANAATUAL":"currentdow","CURRENTDOW":"currentdow","SEMANALOCAL":"localweek","LOCALWEEK":"localweek", +"DIADASEMANALOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","REVISAOATUAL":"currentversion","REVISÃOATUAL":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","IDIOMADOCONTEUDO":"contentlanguage","IDIOMADOCONTEÚDO":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([áâãàéêẽçíòóôõq̃úüűũa-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tg.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tg.json new file mode 100644 index 0000000..1db41e2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tg.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__бе_феҳрист__":"notoc","__notoc__":"notoc","__бе_нигорхона__":"nogallery","__nogallery__":"nogallery","__бо_феҳрист__":"forcetoc","__forcetoc__":"forcetoc","__феҳрист__":"toc","__toc__":"toc","__бе_вироиши_ҷузъӣ__":"noeditsection","__noeditsection__":"noeditsection", +"__бе_тағйири_сарлавҳа__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__бе_тағйири_матн__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ПАЙВАНД_БА_ҚИСМАТИ_НАВ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕ_ПАЙВАНД_БА_ҚИСМАТИ_НАВ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__ГУРӮҲИ_ПИНҲОН__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕ_ИНДЕКС__":"noindex","__NOINDEX__":"noindex","__РАВОНАИ_СТАТИСТИКӢ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","суроғаи_рамз":"urlencode","urlencode":"urlencode", +"ҳарфи_аввал_хурд":"lcfirst","lcfirst":"lcfirst","ҳарфи_аввал_калон":"ucfirst","ucfirst":"ucfirst","бо_ҳарфҳои_хурд":"lc","lc":"lc","бо_ҳарфҳои_калон":"uc","uc":"uc","суроғаи_локалӣ":"localurl","localurl":"localurl","суроғаи_локалӣ_2":"localurle","localurle":"localurle","суроғаи_пурра":"fullurl","fullurl":"fullurl","суроғаи_пурра_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","формати_рақам":"formatnum","formatnum":"formatnum","тасриф":"grammar","grammar":"grammar","ҷинс":"gender","gender":"gender","шакли_ҷамъ":"plural","plural":"plural","bidi":"bidi","#забон":"language","#language":"language","аз_тарафи_чап":"padleft","padleft":"padleft","аз_тарафи_рост":"padright","padright":"padright","рамзкунии_барчасб":"anchorencode","anchorencode":"anchorencode", +"масири_парванда":"filepath","filepath":"filepath","идентификатори_саҳифа":"pageid","pageid":"pageid","дарун":"int","int":"int","#вижа":"special","#special":"special","#speciale":"speciale","#барчасб":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматисана":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","масири_саҳифа": +"articlepath","articlepath":"articlepath","сервер":"server","server":"server","номи_сервер":"servername","servername":"servername","масири_скрипт":"scriptpath","scriptpath":"scriptpath","масири_услуб":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ШУМОРАИ_САҲИФАҲО":"numberofpages","NUMBEROFPAGES":"numberofpages","ШУМОРАИ_КОРБАРОН":"numberofusers","NUMBEROFUSERS":"numberofusers","ШУМОРАИ_КОРБАРОНИ_ФАЪОЛ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","ШУМОРАИ_МАҚОЛАҲО":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","ШУМОРАИ_ПАРВАНДАҲО":"numberoffiles","NUMBEROFFILES":"numberoffiles","ШУМОРАИ_МУДИРОН":"numberofadmins","NUMBEROFADMINS":"numberofadmins","РАҚАМ_ДАР_ГУРӮҲ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup", +"ШУМОРАИ_ВИРОИШОТ":"numberofedits","NUMBEROFEDITS":"numberofedits","ТАРТИБ_БА_ТАВРИ_ПЕШФАРЗ":"defaultsort","КАЛИДИ_ТАРТИБ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","САҲИФА_ДАР_ГУРӮҲ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","АНДОЗАИ_САҲИФА":"pagesize","PAGESIZE":"pagesize","ДАРАҶАИ_МУҲОФИЗАТ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ФАЗОИ_НОМ_2":"namespacee","NAMESPACEE":"namespacee","РАҚАМИ_ФАЗОИ_НОМ":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ФАЗОИ_БАҲСҲО":"talkspace","TALKSPACE":"talkspace","ФАЗОИ_БАҲСҲО_2":"talkspacee","TALKSPACEE":"talkspacee","ФАЗОИ_МАҚОЛАҲО":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace", +"ФАЗОИ_МАҚОЛАҲО_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","НОМИ_САҲИФА":"pagename","PAGENAME":"pagename","НОМИ_САҲИФА_2":"pagenamee","PAGENAMEE":"pagenamee","НОМИ_ПУРРАИ_САҲИФА":"fullpagename","FULLPAGENAME":"fullpagename","НОМИ_ПУРРАИ_САҲИФА_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","АСОСИИ_НОМИ_САҲИФА":"basepagename","BASEPAGENAME":"basepagename","АСОСИИ_НОМИ_САҲИФА_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НОМИ_ЗЕРГУРӮҲ":"subpagename","SUBPAGENAME":"subpagename","НОМИ_ЗЕРГУРӮҲ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","НОМИ_САҲИФАИ_БАҲС":"talkpagename","TALKPAGENAME":"talkpagename","НОМИ_САҲИФАИ_БАҲС_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НОМИ_САҲИФА_МАҚОЛА":"subjectpagename", +"SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НОМИ_САҲИФА_МАҚОЛА_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_НУСХА":"revisionid","REVISIONID":"revisionid","РӮЗИ_НУСХА":"revisionday","REVISIONDAY":"revisionday","РӮЗИ_НУСХА_2":"revisionday2","REVISIONDAY2":"revisionday2","МОҲИ_НУСХА":"revisionmonth","REVISIONMONTH":"revisionmonth","МОҲИ_НУСХА_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","СОЛИ_НУСХА":"revisionyear","REVISIONYEAR":"revisionyear","НИШОНИ_ЗАМОНИ_НУСХА":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","НУСХАИ_КОРБАР":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ФАЗОИ_НОМ":"namespace","NAMESPACE":"namespace","НАМОИШИ_САРЛАВҲА":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","МОҲИ_КУНУНӢ":"currentmonth", +"МОҲИ_КУНУНӢ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","МОҲИ_КУНУНӢ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НОМИ_МОҲИ_КУНУНӢ":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НОМИ_МОҲИ_КУНУНӢ_ТАСРИФ":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НОМИ_МОҲИ_КУНУНӢ_ИХТИСОР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","РӮЗИ_КУНУНӢ":"currentday","CURRENTDAY":"currentday","РӮЗИ_КУНУНИ_2":"currentday2","CURRENTDAY2":"currentday2","НОМИ_РӮЗИ_КУНУНӢ":"currentdayname","CURRENTDAYNAME":"currentdayname","СОЛИ_КУНУНӢ":"currentyear","CURRENTYEAR":"currentyear","ЗАМОНИ_КУНУНӢ":"currenttime","CURRENTTIME":"currenttime","СОАТИ_КУНУНӢ":"currenthour","CURRENTHOUR":"currenthour","МОҲИ_МАҲАЛЛӢ":"localmonth","МОҲИ_МАҲАЛЛӢ_2":"localmonth","LOCALMONTH" +:"localmonth","LOCALMONTH2":"localmonth","МОҲИ_МАҲАЛЛӢ_1":"localmonth1","LOCALMONTH1":"localmonth1","НОМИ_МОҲИ_МАҲАЛЛӢ":"localmonthname","LOCALMONTHNAME":"localmonthname","НОМИ_МОҲИ_МАҲАЛЛӢ_ТАСРИФ":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НОМИ_МОҲИ_МАҲАЛЛӢ_ИХТИСОР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","РӮЗИ_МАҲАЛЛӢ":"localday","LOCALDAY":"localday","РӮЗИ_МАҲАЛЛӢ_2":"localday2","LOCALDAY2":"localday2","НОМИ_РӮЗИ_МАҲАЛЛӢ":"localdayname","LOCALDAYNAME":"localdayname","СОЛИ_МАҲАЛЛӢ":"localyear","LOCALYEAR":"localyear","ЗАМОНИ_МАҲАЛЛӢ":"localtime","LOCALTIME":"localtime","СОАТИ_МАҲАЛЛӢ":"localhour","LOCALHOUR":"localhour","НОМИ_СОМОНА":"sitename","НОМИ_САЙТ":"sitename","SITENAME":"sitename","ХАФТАИ_КУНУНӢ":"currentweek","CURRENTWEEK":"currentweek", +"РӮЗИ_КУНУНИИ_ҲАФТА":"currentdow","CURRENTDOW":"currentdow","ҲАФТАИ_МАҲАЛЛӢ":"localweek","LOCALWEEK":"localweek","РУЗИ_ҲАФТАИ_МАҲАЛЛӢ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","НУСХАИ_КУНУНӢ":"currentversion","CURRENTVERSION":"currentversion","БАРЧАСБИ_ЗАМОНИ_КУНУНӢ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","БАРЧАСБИ_ЗАМОНИ_МАҲАЛЛӢ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","МАСИРИ_ПАЁМ":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЗАБОНИ_МӮҲТАВО":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхчшъэюяғӣқўҳҷцщыь]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-th.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-th.json new file mode 100644 index 0000000..8817c6d --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-th.json @@ -0,0 +1,7 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__ไม่มีสารบัญ__":"notoc","__notoc__":"notoc","__ไม่มีแกลเลอรี่__":"nogallery","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__ไม่มีแก้เฉพาะส่วน__":"noeditsection","__noeditsection__":"noeditsection", +"__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid": +"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort", +"DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES": +"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","เดือนปัจจุบัน":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","ชื่อเดือนปัจจุบัน":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME": +"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ti.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ti.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ti.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tk.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tk.json new file mode 100644 index 0000000..c0e1cab --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tk.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zÄäÇçĞğŇňÖöŞşÜüÝýŽž]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tl.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tl.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tl.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tn.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tn.json new file mode 100644 index 0000000..d26d123 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tn.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zêšô]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-to.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-to.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-to.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tpi.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tpi.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tpi.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tr.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tr.json new file mode 100644 index 0000000..ffe5c79 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tr.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__i̇çi̇ndeki̇leryok__":"notoc","__notoc__":"notoc","__galeri̇yok__":"nogallery","__nogallery__":"nogallery","__i̇çi̇ndeki̇lerzorunlu__":"forcetoc","__forcetoc__":"forcetoc","__i̇çi̇ndeki̇ler__":"toc","__toc__":"toc","__deği̇şti̇ryok__":"noeditsection","__düzenlemeyok__":"noeditsection","__noeditsection__": +"noeditsection","__başlikdönüşümüyok__":"notitleconvert","__bdy__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__i̇çeri̇kdönüşümüyok__":"nocontentconvert","__i̇dy__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__YENİBAŞLIKBAĞLANTISI__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__YENİBAŞLIKBAĞLANTISIYOK__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__GİZLİKAT__":"hiddencat","__GİZLİKATEGORİ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__DİZİN__":"index","__ENDEKS__":"index","__INDEX__":"index","__DİZİNYOK__":"noindex","__ENDEKSYOK__":"noindex","__NOINDEX__":"noindex","__STATİKYÖNLENDİRME__":"staticredirect","__SABİTYÖNLENDİRME__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__": +"expectedUnconnectedPage"}],"functionSynonyms":[{"aa":"ns","ab":"ns","ns":"ns","aau":"nse","abu":"nse","nse":"nse","urlkodlama":"urlencode","urlencode":"urlencode","khi̇lk":"lcfirst","lcfirst":"lcfirst","bhi̇lk":"ucfirst","ucfirst":"ucfirst","kh":"lc","lc":"lc","bh":"uc","uc":"uc","yerelurl":"localurl","localurl":"localurl","yerelurlu":"localurle","localurle":"localurle","tamurl":"fullurl","fullurl":"fullurl","tamurlu":"fullurle","fullurle":"fullurle","kuralliurl":"canonicalurl","canonicalurl":"canonicalurl","kuralliurlu":"canonicalurle","canonicalurle":"canonicalurle","bi̇çi̇mnum":"formatnum","formatnum":"formatnum","di̇lbi̇lgi̇si̇":"grammar","gramer":"grammar","grammar":"grammar","ci̇nsi̇yet":"gender","gender":"gender","çoğul":"plural","plural":"plural","bidi":"bidi","#di̇l":"language","#li̇san":"language","#language":"language","dolsol":"padleft","padleft":"padleft","dolsağ":"padright","padright":"padright","çengelkodlama":"anchorencode","anchorencode": +"anchorencode","dosyayolu":"filepath","dosya_yolu":"filepath","filepath":"filepath","sayfano":"pageid","pageid":"pageid","i̇nt":"int","int":"int","#özel":"special","#special":"special","#özelu":"speciale","#speciale":"speciale","#etiket":"tag","#tag":"tag","#biçimtarih":"formatdate","#tarihbiçimi":"formatdate","#formattarihi":"formatdate","#tarihformatı":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babil":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#assessment":"assessment","pagebanner":"PAGEBANNER","#eğer":"if","#eger":"if","#if":"if","#ifeq":"ifeq","#değiştir":"switch","#degistir":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#işlem":"expr","#islem":"expr","#ifade":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategorihiyerarşisi":"categorytree","#kategoriağacı":"categorytree", +"#ulamhiyerarşisi":"categorytree","#ulamağacı":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","maddeyolu":"articlepath","articlepath":"articlepath","sunucu":"server","server":"server","sunucuadi":"servername","servername":"servername","beti̇kyolu":"scriptpath","scriptpath":"scriptpath","bi̇çemyolu":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"GRUPTAKİSAYI":"numberingroup","GRUBUNSAYISI":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","VARSAYILANSIRALA":"defaultsort","VARSAYILANSIRALAMAANAHTARI":"defaultsort","VARSAYILANKATEGORİSIRALA":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort", +"KATEGORİDEKİSAYFALAR":"pagesincategory","KATTAKİSAYFALAR":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SAYFABOYUTU":"pagesize","PAGESIZE":"pagesize","KORUMASEVİYESİ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SAYFAADI":"pagename","PAGENAME":"pagename","SAYFAADIU":"pagenamee","PAGENAMEE":"pagenamee","TAMSAYFAADI":"fullpagename","FULLPAGENAME":"fullpagename","TAMSAYFAADIU":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ALTSAYFAADI":"subpagename","SUBPAGENAME":"subpagename","ALTSAYFAADIU":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ÜSTSAYFAADI":"basepagename","BASEPAGENAME":"basepagename","ÜSTSAYFAADIU":"basepagenamee","BASEPAGENAMEE":"basepagenamee","TARTIŞMASAYFASIADI":"talkpagename","TALKPAGENAME":"talkpagename","TARTIŞMASAYFASIADIU":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","KONUSAYFASIADI":"subjectpagename", +"MADDESAYFASIADI":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","KONUSAYFASIADIU":"subjectpagenamee","MADDESAYFASIADIU":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","SÜRÜMNU":"revisionid","SÜRÜMNO":"revisionid","REVİZYONNU":"revisionid","REVİZYONNO":"revisionid","REVISIONID":"revisionid","SÜRÜMGÜNÜ":"revisionday","REVISIONDAY":"revisionday","SÜRÜMGÜNÜ2":"revisionday2","REVISIONDAY2":"revisionday2","SÜRÜMAYI":"revisionmonth","REVISIONMONTH":"revisionmonth","SÜRÜMAYI1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","SÜRÜMYILI":"revisionyear","REVISIONYEAR":"revisionyear","SÜRÜMZAMANBİLGİSİ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","SÜRÜMKULLANICI":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ADALANI":"namespace","İSİMALANI":"namespace","NAMESPACE":"namespace","ADALANIU":"namespacee","İSİMALANIU": +"namespacee","NAMESPACEE":"namespacee","ADALANINUMARASI":"namespacenumber","NAMESPACENUMBER":"namespacenumber","TARTIŞMAALANI":"talkspace","TARTIŞMABOŞLUĞU":"talkspace","TALKSPACE":"talkspace","TARTIŞMAALANIU":"talkspacee","TARTIŞMABOŞLUĞUU":"talkspacee","TALKSPACEE":"talkspacee","KONUALANI":"subjectspace","MADDEALANI":"subjectspace","KONUBOŞLUĞU":"subjectspace","MADDEBOŞLUĞU":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","KONUALANIU":"subjectspacee","MADDEALANIU":"subjectspacee","KONUBOŞLUĞUU":"subjectspacee","MADDEBOŞLUĞUU":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","MADDESAYISI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","DOSYASAYISI":"numberoffiles","NUMBEROFFILES":"numberoffiles","KULLANICISAYISI":"numberofusers","NUMBEROFUSERS":"numberofusers","AKTİFKULLANICISAYISI":"numberofactiveusers","ETKİNKULLANICISAYISI":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers", +"SAYFASAYISI":"numberofpages","NUMBEROFPAGES":"numberofpages","HİZMETLİSAYISI":"numberofadmins","NUMBEROFADMINS":"numberofadmins","DEĞİŞİKLİKSAYISI":"numberofedits","NUMBEROFEDITS":"numberofedits","BAŞLIKGÖSTER":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MEVCUTAY":"currentmonth","MEVCUTAY2":"currentmonth","GÜNCELAY":"currentmonth","GÜNCELAY2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MEVCUTAY1":"currentmonth1","GÜNCELAY1":"currentmonth1","CURRENTMONTH1":"currentmonth1","MEVCUTAYADI":"currentmonthname","GÜNCELAYADI":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","MEVCUTAYADIİYELİK":"currentmonthnamegen","GÜNCELAYADIİYELİK":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MEVCUTAYKISALTMASI":"currentmonthabbrev","GÜNCELAYKISALTMASI":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","MEVCUTGÜN":"currentday","GÜNCELGÜN":"currentday","CURRENTDAY":"currentday","MEVCUTGÜN2": +"currentday2","GÜNCELGÜN2":"currentday2","CURRENTDAY2":"currentday2","MEVCUTGÜNADI":"currentdayname","GÜNCELGÜNADI":"currentdayname","CURRENTDAYNAME":"currentdayname","MEVCUTYIL":"currentyear","GÜNCELYIL":"currentyear","CURRENTYEAR":"currentyear","MEVCUTZAMAN":"currenttime","GÜNCELZAMAN":"currenttime","CURRENTTIME":"currenttime","MEVCUTSAAT":"currenthour","GÜNCELSAAT":"currenthour","CURRENTHOUR":"currenthour","YERELAY":"localmonth","YERELAY2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","YERELAY1":"localmonth1","LOCALMONTH1":"localmonth1","YERELAYADI":"localmonthname","LOCALMONTHNAME":"localmonthname","YERELAYADIİYELİK":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","YERELAYKISALTMASI":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","YERELGÜN":"localday","LOCALDAY":"localday","YERELGÜN2":"localday2","LOCALDAY2":"localday2","YERELGÜNADI":"localdayname","LOCALDAYNAME":"localdayname","YERELYIL":"localyear","LOCALYEAR":"localyear", +"YERELZAMAN":"localtime","LOCALTIME":"localtime","YERELSAAT":"localhour","LOCALHOUR":"localhour","SİTEADI":"sitename","SITENAME":"sitename","MEVCUTHAFTA":"currentweek","GÜNCELHAFTA":"currentweek","CURRENTWEEK":"currentweek","MEVCUTHAFTANINGÜNÜ":"currentdow","GÜNCELHAFTANINGÜNÜ":"currentdow","CURRENTDOW":"currentdow","YERELHAFTA":"localweek","LOCALWEEK":"localweek","YERELHAFTANINGÜNÜ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","MEVCUTSÜRÜM":"currentversion","GÜNCELSÜRÜM":"currentversion","CURRENTVERSION":"currentversion","MEVCUTZAMANBİLGİSİ":"currenttimestamp","GÜNCELZAMANBİLGİSİ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","YERELZAMANBİLGİSİ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","YÖNİŞARETİ:":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","İÇERİKDİLİ":"contentlanguage","İÇERİKLİSANI":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage", +"PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zÇĞçğİıÖöŞşÜüÂâÎîÛû]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-trv.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-trv.json new file mode 100644 index 0000000..8f21bcd --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-trv.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__無目錄__":"notoc","__无目录__":"notoc","__notoc__":"notoc","__無圖庫__":"nogallery","__无图库__":"nogallery","__nogallery__":"nogallery","__強制目錄__":"forcetoc","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__目錄__":"toc","__目录__":"toc","__toc__":"toc","__無段落編輯__": +"noeditsection","__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__不轉換標題__":"notitleconvert","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換內容__":"nocontentconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__新段落链接__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__无新段落链接__":"nonewsectionlink","__隱藏分類__":"hiddencat","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__索引__":"index","__NOINDEX__":"noindex","__无索引__":"noindex","__靜態重新導向__":"staticredirect","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__消除歧义__": +"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"命名空間":"ns","名字空间":"ns","ns":"ns","名称空间":"ns","命名空間e":"nse","名字空间e":"nse","nse":"nse","名称空间e":"nse","urlencode":"urlencode","url编码":"urlencode","lcfirst":"lcfirst","小写首字":"lcfirst","ucfirst":"ucfirst","大写首字":"ucfirst","lc":"lc","小写":"lc","uc":"uc","大写":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","fullurl":"fullurl","完整url":"fullurl","fullurle":"fullurle","完整url等同":"fullurle","canonicalurl":"canonicalurl","规范url":"canonicalurl","canonicalurle":"canonicalurle","规范url等同":"canonicalurle","formatnum":"formatnum","格式化数字":"formatnum","grammar":"grammar","语法":"grammar","性別":"gender","性":"gender","性别":"gender","gender":"gender","plural":"plural","复数":"plural","bidi":"bidi","#語言": +"language","#语言":"language","#language":"language","padleft":"padleft","左填充":"padleft","padright":"padright","右填充":"padright","anchorencode":"anchorencode","锚编码":"anchorencode","filepath":"filepath","文件路径":"filepath","頁面id":"pageid","页面id":"pageid","pageid":"pageid","int":"int","界面":"int","#special":"special","#特殊":"special","#speciale":"speciale","#特殊等同":"speciale","#tag":"tag","#标记":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#格式化日期":"formatdate","#日期格式化":"formatdate","#目標":"target","#目标":"target","#target":"target","#巴別":"babel","#巴别":"babel","#babel":"babel","#coordinates":"coordinates","#調動":"invoke","#调用":"invoke","#invoke":"invoke","#related":"related","#若":"if","#非空式":"if","#如果":"if","#if":"if","#相同式":"ifeq","#匹配式":"ifeq","#若相等":"ifeq","#如果相等":"ifeq","#ifeq":"ifeq","#轉換":"switch","#多选式":"switch","#多条件式": +"switch","#双射式":"switch","#开关":"switch","#转换":"switch","#switch":"switch","#存在式":"ifexist","#若有":"ifexist","#如有":"ifexist","#ifexist":"ifexist","#若表達式":"ifexpr","#若表达式":"ifexpr","#ifexpr":"ifexpr","#如果錯誤":"iferror","#错误式":"iferror","#如果错误":"iferror","#iferror":"iferror","#時間":"time","#时间":"time","#time":"time","#時間l":"timel","#时间l":"timel","#timel":"timel","#表達式":"expr","#计算式":"expr","#表达式":"expr","#expr":"expr","#rel2abs":"rel2abs","#标题组成部分":"titleparts","#titleparts":"titleparts","#分類樹":"categorytree","#分类树":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","隱藏跨語言連結":"noexternallanglinks","无外部语言连接":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#屬性":"property","#属性":"property","#property":"property","#statements" +:"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","条目路径":"articlepath","伺服器":"server","服务器":"server","server":"server","伺服器名稱":"servername","服务器名":"servername","servername":"servername","scriptpath":"scriptpath","脚本路径":"scriptpath","stylepath":"stylepath","样式路径":"stylepath","numberofwikis":"numberofwikis","wb報表名稱":"wbreponame","wb报告名":"wbreponame","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","组中用户数":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","默认排序":"defaultsort","默认排序关键字":"defaultsort","默认分类排序":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","分类中页面数":"pagesincategory","PAGESIZE":"pagesize","页面大小":"pagesize","PROTECTIONLEVEL":"protectionlevel","保护级别": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","頁面名稱":"pagename","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","页面名等同":"pagenamee","页面名称等同":"pagenamee","FULLPAGENAME":"fullpagename","页面全称":"fullpagename","完整页面名称":"fullpagename","FULLPAGENAMEE":"fullpagenamee","完整页面名称等同":"fullpagenamee","SUBPAGENAME":"subpagename","子页面名称":"subpagename","SUBPAGENAMEE":"subpagenamee","子页面名称等同":"subpagenamee","根頁面名稱":"rootpagename","ROOTPAGENAME":"rootpagename","根页面名称":"rootpagename","根頁面名稱E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","根页面名称等同":"rootpagenamee","BASEPAGENAME":"basepagename","基础页面名称":"basepagename","BASEPAGENAMEE":"basepagenamee","基础页面名称等同":"basepagenamee","TALKPAGENAME":"talkpagename","讨论页面名称":"talkpagename","对话页面名称": +"talkpagename","TALKPAGENAMEE":"talkpagenamee","讨论页面名称等同":"talkpagenamee","对话页面名称等同":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主名字空间页面名称":"subjectpagename","条目页面名称":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","主名字空间页面名称等同":"subjectpagenamee","条目页面名称等同":"subjectpagenamee","REVISIONID":"revisionid","修订ID":"revisionid","REVISIONDAY":"revisionday","修订日":"revisionday","REVISIONDAY2":"revisionday2","修订日2":"revisionday2","REVISIONMONTH":"revisionmonth","修订月":"revisionmonth","REVISIONMONTH1":"revisionmonth1","修订月1":"revisionmonth1","REVISIONYEAR":"revisionyear","修订年":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","修订时间戳":"revisiontimestamp","修訂使用者":"revisionuser","REVISIONUSER":"revisionuser","修订用户":"revisionuser", +"CASCADINGSOURCES":"cascadingsources","级联来源":"cascadingsources","命名空間":"namespace","名字空间":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","名字空间等同":"namespacee","命名空間數":"namespacenumber","名字空间编号":"namespacenumber","NAMESPACENUMBER":"namespacenumber","對話空間":"talkspace","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","讨论空间等同":"talkspacee","讨论名字空间等同":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主名字空间":"subjectspace","条目名字空间":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","主名字空间等同":"subjectspacee","条目名字空间等同":"subjectspacee","文章數":"numberofarticles","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","檔案數":"numberoffiles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles", +"使用者人數量":"numberofusers","用户数":"numberofusers","NUMBEROFUSERS":"numberofusers","活躍使用者人數":"numberofactiveusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","頁面數":"numberofpages","页面数":"numberofpages","NUMBEROFPAGES":"numberofpages","管理員數":"numberofadmins","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","编辑数":"numberofedits","顯示標題":"displaytitle","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","本月1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","本月名":"currentmonthname","本月名称":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月名属格":"currentmonthnamegen","本月名称属格":"currentmonthnamegen","本月縮寫": +"currentmonthabbrev","本月简称":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","今天2":"currentday2","CURRENTDAYNAME":"currentdayname","星期":"currentdayname","今天名":"currentdayname","今天名称":"currentdayname","CURRENTYEAR":"currentyear","今年":"currentyear","目前時間":"currenttime","当前时间":"currenttime","此时":"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","当前小时":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","本地月":"localmonth","本地月2":"localmonth","LOCALMONTH1":"localmonth1","本地月1":"localmonth1","LOCALMONTHNAME":"localmonthname","本地月份名":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","本地月历":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","本地月缩写":"localmonthabbrev","LOCALDAY":"localday","本地日":"localday","LOCALDAY2":"localday2","本地日2": +"localday2","LOCALDAYNAME":"localdayname","本地日名":"localdayname","LOCALYEAR":"localyear","本地年":"localyear","LOCALTIME":"localtime","本地时间":"localtime","LOCALHOUR":"localhour","本地小时":"localhour","網站名稱":"sitename","站点名称":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","本周":"currentweek","CURRENTDOW":"currentdow","当前DOW":"currentdow","LOCALWEEK":"localweek","本地周":"localweek","LOCALDOW":"localdow","本地DOW":"localdow","REVISIONSIZE":"revisionsize","修订大小":"revisionsize","目前版本":"currentversion","当前版本":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","当前时间戳":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","本地时间戳":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","方向标记":"directionmark","內容語言":"contentlanguage","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^()(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ts.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ts.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ts.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tt.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tt.json new file mode 100644 index 0000000..b080df7 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tt.json @@ -0,0 +1,14 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__башлыкюк__":"notoc","__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__ettiq__":"forcetoc","__обяз_огл__":"forcetoc","__обязательное_оглавление__":"forcetoc","__forcetoc__": +"forcetoc","__эчтелек__":"toc","__оглавление__":"toc","__огл__":"toc","__toc__":"toc","__бүлекүзгәртүюк__":"noeditsection","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__ИНДЕКССЫЗ__":"noindex", +"__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"i̇a":"ns","пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","urinliurl":"localurl","локальный_адрес":"localurl","localurl":"localurl","urinliurle":"localurle","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle": +"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender","множественное_число":"plural","plural":"plural","bidi":"bidi","#тел":"language","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","эчке":"int","внутр":"int","int":"int","#махсус":"special","#служебная":"special","#special":"special","#speciale":"speciale","#тамга":"tag","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate" +:"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#төркемнәр_шәҗәрәсе":"categorytree","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor", +"путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry", +"БИТ_ИСЕМЕ":"pagename","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE": +"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ИСЕМНӘР_МӘЙДАНЫ":"namespace", +"ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","МӘКАЛӘ_САНЫ":"numberofarticles","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers", +"КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","АГЫМДАГЫ_АЙ":"currentmonth","АГЫМДАГЫ_АЙ2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","АГЫМДАГЫ_АЙ_ИСЕМЕ":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname", +"АГЫМДАГЫ_АЙ_ИСЕМЕ_GEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","АГЫМДАГЫ_КӨН":"currentday","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","АГЫМДАГЫ_КӨН2":"currentday2","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","АГЫМДАГЫ_КӨН_ИСЕМЕ":"currentdayname","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","АГЫМДАГЫ_ЕЛ":"currentyear","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","АГЫМДАГЫ_ВАКЫТ":"currenttime","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","МЕСТНЫЙ_МЕСЯЦ":"localmonth", +"МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","СӘХИФӘ_ИСЕМЕ":"sitename","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename", +"ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюяӘәӨөҮүҖҗҢңҺһ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tum.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tum.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tum.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tw.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tw.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tw.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ty.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ty.json new file mode 100644 index 0000000..30830cb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ty.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode", +"initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int", +"#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property", +"#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGE":"pagename","PAGENAME":"pagename", +"NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid", +"REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee", +"ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL": +"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tyv.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tyv.json new file mode 100644 index 0000000..f5ea70a --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-tyv.json @@ -0,0 +1,14 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__эге_чок__":"notoc","__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__галерея_чок__":"nogallery","__без_галереи__":"nogallery","__nogallery__":"nogallery","__албан_эге__":"forcetoc","__обязательное_оглавление__":"forcetoc", +"__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__допчу__":"toc","__оглавление__":"toc","__огл__":"toc","__toc__":"toc","__үлег_эдилгези_чок__":"noeditsection","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__ЧАЖЫТ_АҢГЫЛАЛ__":"hiddencat","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory", +"__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ад":"ns","пи":"ns","ns":"ns","ад2":"nse","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle", +"fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender","множественное_число":"plural","plural":"plural","bidi":"bidi","#дыл":"language","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#тускай":"special","#служебная":"special","#special":"special","#speciale":"speciale","#демдек":"tag","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate": +"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#аңгылал_ыяжы":"categorytree","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье" +:"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","__АҢГЫЛАЛ_АРЫННАРЫ__":"pagesincategory","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel", +"PROTECTIONEXPIRY":"protectionexpiry","АРЫННЫҢАДЫ":"pagename","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2": +"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","АТТАРДЕЛГЕМИ": +"namespace","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","АТТАРДЕЛГЕМИ2":"namespacee","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","АТТАРДЕЛГЕМИНИҢСАНЫ":"namespacenumber","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ЧУГААДЕЛГЕМИ":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ЧУГААДЕЛГЕМИ2":"talkspacee","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ЧҮҮЛДЕРНИҢСАНЫ":"numberofarticles","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles", +"ФАЙЛДАРНЫҢСАНЫ":"numberoffiles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","АЖЫГЛАКЧЫЛАРНЫҢСАНЫ":"numberofusers","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","АРЫННАРНЫҢСАНЫ":"numberofpages","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ӨСКЕРЛИИШКИННЕРНИҢСАНЫ":"numberofedits","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","__АМГЫ_АЙ":"currentmonth","__АМГЫ_АЙ_2__":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","__АМГЫ_АЙ_1__":"currentmonth1","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","АМГЫАЙНЫҢАДЫ":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","АМГЫХҮН":"currentday","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","АМГЫХҮН2":"currentday2","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","АМГЫХҮННҮҢАДЫ":"currentdayname","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","АМГЫЧЫЛ":"currentyear", +"ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","АМГЫҮЕ":"currenttime","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","АМГЫШАК":"currenthour","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME": +"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","САЙТТЫҢАДЫ":"sitename","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename","АМГЫЧЕДИХОНУК":"currentweek","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","АМГЫЧЕДИХОНУКТУҢХҮНҮ":"currentdow","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp", +"НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-udm.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-udm.json new file mode 100644 index 0000000..ae1fbf9 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-udm.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender", +"множественное_число":"plural","plural":"plural","bidi":"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch","#ifexist": +"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#деревокатегорий":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#наставник":"mentor","#mentor":"mentor","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ": +"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY": +"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ": +"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","ТЕКУЩИЙ_МЕСЯЦ": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour", +"МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename", +"ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zа-яёӝӟӥӧӵ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ug.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ug.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ug.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-uk.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-uk.json new file mode 100644 index 0000000..ee88865 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-uk.json @@ -0,0 +1,20 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__без_змісту__":"notoc","__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереї__":"nogallery","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обов_зміст__":"forcetoc","__обязательное_оглавление__": +"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__зміст__":"toc","__оглавление__":"toc","__огл__":"toc","__toc__":"toc","__без_редагув_розділу__":"noeditsection","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_перетворення_заголовку__":"notitleconvert","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_перетворення_тексту__":"nocontentconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ПОСИЛАННЯ_НА_НОВИЙ_РОЗДІЛ__":"newsectionlink","__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ПОСИЛАННЯ_НА_НОВИЙ_РОЗДІЛ__": +"nonewsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__ПРИХОВ_КАТ__":"hiddencat","__ПРИХОВАНА_КАТЕГОРІЯ__":"hiddencat","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ІНДЕКС__":"index","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ІНДЕКСУ__":"noindex","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧНЕ_ПЕРЕНАПРАВЛЕННЯ__":"staticredirect","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"пн":"ns","пи":"ns","ns":"ns","пн_2":"nse","пик":"nse","nse":"nse","закодована_адреса":"urlencode","закодированный_адрес":"urlencode","urlencode": +"urlencode","нр_перша":"lcfirst","перша_буква_мала":"lcfirst","перша_літера_мала":"lcfirst","мала_перша_літера":"lcfirst","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","вр_перша":"ucfirst","перша_буква_велика":"ucfirst","перша_літера_велика":"ucfirst","велика_перша_літера":"ucfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","нр":"lc","нижній_регістр":"lc","малими_буквами":"lc","малими_літерами":"lc","маленькими_буквами":"lc","lc":"lc","вр":"uc","верхній_регістр":"uc","великими_буквами":"uc","великими_літерами":"uc","большими_буквами":"uc","uc":"uc","локальна_адреса":"localurl","локальный_адрес":"localurl","localurl":"localurl","локальна_адреса_2":"localurle", +"локальный_адрес_2":"localurle","localurle":"localurle","повна_адреса":"fullurl","полный_адрес":"fullurl","fullurl":"fullurl","повна_адреса_2":"fullurle","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматувати_число":"formatnum","форматировать_число":"formatnum","formatnum":"formatnum","відмінок":"grammar","падеж":"grammar","grammar":"grammar","стать":"gender","пол":"gender","gender":"gender","множина":"plural","множественное_число":"plural","plural":"plural","bidi":"bidi","#мова":"language","#язык":"language","#language":"language","заповнити_ліворуч":"padleft","заполнить_слева":"padleft","padleft":"padleft","заповнити_праворуч":"padright","заполнить_справа":"padright","padright":"padright", +"кодувати_мітку":"anchorencode","кодировать_метку":"anchorencode","anchorencode":"anchorencode","шлях_до_файлу":"filepath","путь_к_файлу":"filepath","filepath":"filepath","ідентифікатор_сторінки":"pageid","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#спеціальна":"special","#служебная":"special","#special":"special","#speciale":"speciale","#тег":"tag","#мітка":"tag","#теґ":"tag","#метка":"tag","#тэг":"tag","#tag":"tag","#форматдати":"formatdate","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#деревокатегорій":"categorytree","#деревокатегорий":"categorytree","#categorytree":"categorytree","#target":"target","#вавилон":"babel","#вавілон":"babel","#babel":"babel","#coordinates":"coordinates","#викликати":"invoke", +"#вызвать":"invoke","#invoke":"invoke","#related":"related","#якщо":"if","#если":"if","#if":"if","#якщорівні":"ifeq","#рівні":"ifeq","#ifeq":"ifeq","#вибірка":"switch","#переключатель":"switch","#switch":"switch","#якщоіснує":"ifexist","#ifexist":"ifexist","#якщовираз":"ifexpr","#ifexpr":"ifexpr","#якщопомилка":"iferror","#еслиошибка":"iferror","#iferror":"iferror","#time":"time","#timel":"timel","#вираз":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","беззовнішніхпосилань":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#властивість":"property","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","шлях_до_статті": +"articlepath","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","назва_сервера":"servername","название_сервера":"servername","servername":"servername","шлях_до_скрипту":"scriptpath","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","шлях_до_стилю":"stylepath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"КІЛЬКІСТЬ_СТОРІНОК":"numberofpages","КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages","КІЛЬКІСТЬ_КОРИСТУВАЧІВ":"numberofusers","КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КІЛЬКІСТЬ_АКТИВНИХ_КОРИСТУВАЧІВ":"numberofactiveusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS": +"numberofactiveusers","КІЛЬКІСТЬ_СТАТЕЙ":"numberofarticles","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КІЛЬКІСТЬ_ФАЙЛІВ":"numberoffiles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КІЛЬКІСТЬ_АДМІНІСТРАТОРІВ":"numberofadmins","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","КІЛЬКІСТЬ_У_ГРУПІ":"numberingroup","ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","КІЛЬКІСТЬ_РЕДАГУВАНЬ":"numberofedits","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","СТАНДАРТНЕ_СОРТУВАННЯ:_КЛЮЧ_СОРТУВАННЯ":"defaultsort","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort", +"DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТОР_В_КАТ":"pagesincategory","СТОР_У_КАТ":"pagesincategory","СТОРІНОК_У_КАТЕГОРІЇ":"pagesincategory","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","РОЗМІР":"pagesize","РОЗМІР_СТОРІНКИ":"pagesize","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","РІВЕНЬ_ЗАХИСТУ":"protectionlevel","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ПРОСТІР_НАЗВ_2":"namespacee","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","ПРОСТІР_ОБГОВОРЕННЯ":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТІР_ОБГОВОРЕННЯ_2":"talkspacee", +"ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТІР_СТАТЕЙ":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТІР_СТАТЕЙ_2":"subjectspacee","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","НАЗВА_СТОРІНКИ":"pagename","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВА_СТОРІНКИ_2":"pagenamee","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОВНА_НАЗВА_СТОРІНКИ":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename","FULLPAGENAME":"fullpagename","ПОВНА_НАЗВА_СТОРІНКИ_2":"fullpagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE" +:"rootpagenamee","ОСНОВА_НАЗВИ_ПІДСТОРІНКИ":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВИ_ПІДСТОРІНКИ_2":"basepagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВА_ПІДСТОРІНКИ":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВА_ПІДСТОРІНКИ_2":"subpagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","НАЗВА_СТОРІНКИ_ОБГОВОРЕННЯ":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВА_СТОРІНКИ_ОБГОВОРЕННЯ_2":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВА_СТАТТІ": +"subjectpagename","НАЗВА_СТОРІНКИ_СТАТТІ":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВА_СТАТТІ_2":"subjectpagenamee","НАЗВА_СТОРІНКИ_СТАТТІ_2":"subjectpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ІД_ВЕРСІЇ":"revisionid","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСІЇ":"revisionday","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСІЇ_2":"revisionday2","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МІСЯЦЬ_ВЕРСІЇ":"revisionmonth","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МІСЯЦЬ_ВЕРСІЇ_1":"revisionmonth1","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1", +"REVISIONMONTH1":"revisionmonth1","РІК_ВЕРСІЇ":"revisionyear","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","МІТКА_ЧАСУ_ВЕРСІЇ":"revisiontimestamp","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСІЯ_КОРИСТУВАЧА":"revisionuser","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТІР_НАЗВ":"namespace","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПОКАЗАТИ_ЗАГОЛОВОК":"displaytitle","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","ПОТОЧНИЙ_МІСЯЦЬ":"currentmonth","ПОТОЧНИЙ_МІСЯЦЬ_2":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","ПОТОЧНИЙ_МІСЯЦЬ_1" +:"currentmonth1","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВА_ПОТОЧНОГО_МІСЯЦЯ":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВА_ПОТОЧНОГО_МІСЯЦЯ_РОД":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВА_ПОТОЧНОГО_МІСЯЦЯ_АБР":"currentmonthabbrev","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ПОТОЧНИЙ_ДЕНЬ":"currentday","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ПОТОЧНИЙ_ДЕНЬ_2":"currentday2","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВА_ПОТОЧНОГО_ДНЯ":"currentdayname","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname", +"CURRENTDAYNAME":"currentdayname","ПОТОЧНИЙ_РІК":"currentyear","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ПОТОЧНИЙ_ЧАС":"currenttime","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ПОТОЧНА_ГОДИНА":"currenthour","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","ЛОКАЛЬНИЙ_МІСЯЦЬ":"localmonth","ЛОКАЛЬНИЙ_МІСЯЦЬ_2":"localmonth","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","ЛОКАЛЬНИЙ_МІСЯЦЬ_1":"localmonth1","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВА_ЛОКАЛЬНОГО_МІСЯЦЯ":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВА_ЛОКАЛЬНОГО_МІСЯЦЯ_РОД":"localmonthnamegen", +"НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВА_ЛОКАЛЬНОГО_МІСЯЦЯ_АБР":"localmonthabbrev","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","ЛОКАЛЬНИЙ_ДЕНЬ":"localday","МІСЦЕВИЙ_ДЕНЬ":"localday","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","ЛОКАЛЬНИЙ_ДЕНЬ_2":"localday2","МІСЦЕВИЙ_ДЕНЬ_2":"localday2","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВА_ЛОКАЛЬНОГО_ДНЯ":"localdayname","НАЗВА_МІСЦЕВОГО_ДНЯ":"localdayname","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","ЛОКАЛЬНИЙ_РІК":"localyear","МІСЦЕВИЙ_РІК":"localyear","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","ЛОКАЛЬНИЙ_ЧАС":"localtime","МІСЦЕВИЙ_ЧАС": +"localtime","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","ЛОКАЛЬНА_ГОДИНА":"localhour","МІСЦЕВА_ГОДИНА":"localhour","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВА_САЙТУ":"sitename","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename","ПОТОЧНИЙ_ТИЖДЕНЬ":"currentweek","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ПОТОЧНИЙ_ДЕНЬ_ТИЖНЯ":"currentdow","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow","CURRENTDOW":"currentdow","ЛОКАЛЬНИЙ_ТИЖДЕНЬ":"localweek","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","ЛОКАЛЬНИЙ_ДЕНЬ_ТИЖНЯ":"localdow","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ПОТОЧНА_ВЕРСІЯ":"currentversion","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion", +"МІТКА_ПОТОЧНОГО_ЧАСУ":"currenttimestamp","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","МІТКА_ЛОКАЛЬНОГО_ЧАСУ":"localtimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРЯМОК_ПИСЬМА":"directionmark","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","МОВА_ВМІСТУ":"contentlanguage","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгґдеєжзиіїйклмнопрстуфхцчшщьєюяёъы“»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ur.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ur.json new file mode 100644 index 0000000..48ced3c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ur.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__فہرست_نہیں__":"notoc","__نافہرست__":"notoc","__notoc__":"notoc","__نگارخانہ_نہیں__":"nogallery","__nogallery__":"nogallery","__بافہرست__":"forcetoc","__forcetoc__":"forcetoc","__فہرست__":"toc","__toc__":"toc","__ناترمیم_قطعہ__":"noeditsection","__noeditsection__": +"noeditsection","__منتقلی_عنوان_نہیں__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__منتقلی_مواد_نہیں__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ربط_نیا_قطعہ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__ربط_نیا_قطعہ_نہیں__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__پوشیدہ_زمرہ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__اشاریہ__":"index","__INDEX__":"index","__اشاریہ_نہیں__":"noindex","__NOINDEX__":"noindex","__ساکن_رجوع_مکرر__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"نف":"ns","ns":"ns","نفک":"nse","nse":"nse","ربط_کوڈ":"urlencode","urlencode":"urlencode","چھوٹے_حروف_سے_شروع":"lcfirst", +"lcfirst":"lcfirst","بڑے_حروف_سے_شروع":"ucfirst","ucfirst":"ucfirst","چھوٹے_حروف":"lc","lc":"lc","بڑے_حروف":"uc","uc":"uc","مقامی_ربط":"localurl","localurl":"localurl","مقامی_ربط_کوڈ":"localurle","localurle":"localurle","مکمل_ربط":"fullurl","fullurl":"fullurl","مکمل_ربط_کوڈ":"fullurle","fullurle":"fullurle","معیاری_ربط":"canonicalurl","canonicalurl":"canonicalurl","معیاری_ربط_کوڈ":"canonicalurle","canonicalurle":"canonicalurle","صیغہ_عدد":"formatnum","formatnum":"formatnum","قواعد":"grammar","grammar":"grammar","صنف":"gender","gender":"gender","جمع":"plural","plural":"plural","بی_ڈی":"bidi","bidi":"bidi","#زبان":"language","#language":"language","بائیں_جوڑیں":"padleft","padleft":"padleft","دائیں_جوڑیں":"padright","padright":"padright","اینکر_کوڈ":"anchorencode","anchorencode":"anchorencode","راہ_فائل":"filepath","filepath":"filepath", +"شناخت_صفحہ":"pageid","pageid":"pageid","عالمی":"int","int":"int","#خاص":"special","#special":"special","#خاص_کوڈ":"speciale","#speciale":"speciale","#ٹیگ":"tag","#tag":"tag","#صیغہ_تاریخ":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#شجرہ_زمرہ":"categorytree","#شجر_زمرہ":"categorytree","#categorytree":"categorytree","#ہدف":"target","#target":"target","#بابل":"babel","#babel":"babel","#متناسقات":"coordinates","#coordinates":"coordinates","#استدعا":"invoke","#invoke":"invoke","#related":"related","#اگر":"if","#if":"if","#اگربرابر":"ifeq","#ifeq":"ifeq","#منتقل":"switch","#switch":"switch","#اگرموجود":"ifexist","#ifexist":"ifexist","#اگراظہار":"ifexpr","#ifexpr":"ifexpr","#اگرنقص":"iferror","#iferror":"iferror","#وقت":"time","#time":"time","#وقت_لمبائی":"timel","#timel":"timel","#اظہار":"expr","#expr":"expr","#اضافی_تا_مطلق": +"rel2abs","#rel2abs":"rel2abs","#اجزاء_عنوان":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","بیرونی_زبان_کے_روابط_نہیں":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#خاصیت":"property","#property":"property","#بیانات":"statements","#statements":"statements","#commaseparatedlist":"commaSeparatedList","راہ_مضمون":"articlepath","articlepath":"articlepath","سرور":"server","server":"server","نام_سرور":"servername","servername":"servername","راہ_اسکرپٹ":"scriptpath","scriptpath":"scriptpath","راہ_طرز":"stylepath","stylepath":"stylepath","ویکیوں_کی_تعداد":"numberofwikis","numberofwikis":"numberofwikis","ویب_مخزن_کا_نام":"wbreponame","wbreponame":"wbreponame"},{"تعداد_صفحات":"numberofpages","NUMBEROFPAGES":"numberofpages","تعداد_صارفین":"numberofusers", +"NUMBEROFUSERS":"numberofusers","تعداد_فعال_صارفین":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","تعداد_مضامین":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","تعداد_فائل":"numberoffiles","NUMBEROFFILES":"numberoffiles","تعداد_منتظمین":"numberofadmins","NUMBEROFADMINS":"numberofadmins","تعداد_در_گروہ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","تعداد_ترامیم":"numberofedits","NUMBEROFEDITS":"numberofedits","ابتدائی_ترتیب":"defaultsort","کلید_ابتدائی_ترتیب":"defaultsort","ابتدائی_ترتیب_زمرہ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","زمرے_میں_صفحات":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","حجم_صفحہ":"pagesize","PAGESIZE":"pagesize","درجہ_حفاظت":"protectionlevel", +"PROTECTIONLEVEL":"protectionlevel","اختتام_حفاظت":"protectionexpiry","PROTECTIONEXPIRY":"protectionexpiry","نام_فضا_کوڈ":"namespacee","NAMESPACEE":"namespacee","نام_فضا_کا_عدد":"namespacenumber","NAMESPACENUMBER":"namespacenumber","تبادلہ_خیال_نام_فضا":"talkspace","TALKSPACE":"talkspace","تبادلہ_خیال_کوڈ":"talkspacee","TALKSPACEE":"talkspacee","مضمون_نام_فضا":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","مضمون_نام_فضا_کوڈ":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","نام_صفحہ":"pagename","PAGENAME":"pagename","نام_صفحہ_کوڈ":"pagenamee","PAGENAMEE":"pagenamee","صفحہ_کا_مکمل_نام":"fullpagename","مکمل_نام":"fullpagename","FULLPAGENAME":"fullpagename","مکمل_نام_کوڈ":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","نام_اصل_صفحہ":"rootpagename","ROOTPAGENAME":"rootpagename", +"اصل_صفحہ_کوڈ":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","نام_بنیادی_صفحہ":"basepagename","BASEPAGENAME":"basepagename","بنیادی_صفحہ_کوڈ":"basepagenamee","BASEPAGENAMEE":"basepagenamee","نام_ذیلی_صفحہ":"subpagename","SUBPAGENAME":"subpagename","ذیلی_صفحہ_کوڈ":"subpagenamee","SUBPAGENAMEE":"subpagenamee","نام_تبادلہ_خیال_صفحہ":"talkpagename","TALKPAGENAME":"talkpagename","تبادلہ_خیال_صفحہ_کوڈ":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","نام_صفحہ_مضمون":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","نام_صفحہ_مضمون_کوڈ":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","شناخت_نسخہ":"revisionid","REVISIONID":"revisionid","یوم_نسخہ":"revisionday","REVISIONDAY":"revisionday","یوم_نسخہ2":"revisionday2","REVISIONDAY2":"revisionday2","ماہ_نسخہ": +"revisionmonth","REVISIONMONTH":"revisionmonth","ماہ_نسخہ1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","سال_نسخہ":"revisionyear","REVISIONYEAR":"revisionyear","مہر_وقت_نسخہ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","صارف_نسخہ":"revisionuser","REVISIONUSER":"revisionuser","آبشاری_مآخذ":"cascadingsources","CASCADINGSOURCES":"cascadingsources","نام_فضا":"namespace","NAMESPACE":"namespace","نمائش_عنوان":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","موجودہ_مہینہ":"currentmonth","موجودہ_مہینہ2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","موجودہ_مہینہ1":"currentmonth1","CURRENTMONTH1":"currentmonth1","موجودہ_مہینہ_کا_نام":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","موجودہ_مہینہ_کا_نام_اضافت":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen", +"موجودہ_مہینہ_کا_اختصار":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","موجودہ_دن":"currentday","CURRENTDAY":"currentday","موجودہ_دن2":"currentday2","CURRENTDAY2":"currentday2","موجودہ_دن_کا_نام":"currentdayname","CURRENTDAYNAME":"currentdayname","موجودہ_سال":"currentyear","CURRENTYEAR":"currentyear","موجودہ_وقت":"currenttime","CURRENTTIME":"currenttime","موجودہ_گھنٹہ":"currenthour","CURRENTHOUR":"currenthour","مقامی_مہینہ":"localmonth","مقامی_مہینہ2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","مقامی_مہینہ1":"localmonth1","LOCALMONTH1":"localmonth1","مقامی_مہینہ_کا_نام":"localmonthname","LOCALMONTHNAME":"localmonthname","مقامی_مہینہ_کا_نام_اضافت":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","مقامی_مہینہ_کا_اختصار":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev", +"مقامی_دن":"localday","LOCALDAY":"localday","مقامی_دن2":"localday2","LOCALDAY2":"localday2","مقامی_دن_کا_نام":"localdayname","LOCALDAYNAME":"localdayname","مقامی_سال":"localyear","LOCALYEAR":"localyear","مقامی_وقت":"localtime","LOCALTIME":"localtime","مقامی_گھنٹہ":"localhour","LOCALHOUR":"localhour","سائٹ_نام":"sitename","نام_سائٹ":"sitename","نام_موقع":"sitename","SITENAME":"sitename","موجودہ_ہفتہ":"currentweek","CURRENTWEEK":"currentweek","ہفتہ_کا_دن":"currentdow","CURRENTDOW":"currentdow","مقامی_ہفتہ":"localweek","LOCALWEEK":"localweek","مقامی_ہفتہ_کا_دن":"localdow","LOCALDOW":"localdow","حجم_نسخہ":"revisionsize","REVISIONSIZE":"revisionsize","موجودہ_نسخہ":"currentversion","CURRENTVERSION":"currentversion","موجودہ_مہر_وقت":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","مقامی_مہر_وقت":"localtimestamp","LOCALTIMESTAMP": +"localtimestamp","علامت_جہت":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","مواد_کی_زبان":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([ابپتٹثجچحخدڈذر​ڑ​زژسشصضطظعغفقکگل​م​نںوؤہھیئےآأءۃ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-uz.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-uz.json new file mode 100644 index 0000000..bca2754 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-uz.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__ichidagilaryoq__":"notoc","__notoc__":"notoc","__galereyayoq__":"nogallery","__nogallery__":"nogallery","__ichidagilarmajburiy__":"forcetoc","__forcetoc__":"forcetoc","__ichidagilari__":"toc","__ichidagilar__":"toc","__toc__":"toc","__tahriryoq__":"noeditsection","__tartiblashyoq__":"noeditsection","__noeditsection__": +"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__YASHIRINTURKUM__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","__INDEX__":"index","__INDEKSYOQ__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","jins":"gender","gender":"gender","plural":"plural","bidi":"bidi","#til":"language", +"#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#maxsus":"special","#special":"special","#speciale":"speciale","#yorliq":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#agar":"if","#if":"if","#agarteng":"ifeq","#ifeq":"ifeq","#tanlov":"switch","#switch":"switch","#agarbor":"ifexist","#ifexist":"ifexist","#agarifoda":"ifexpr","#ifexpr":"ifexpr","#agarxato":"iferror","#iferror":"iferror","#vaqt":"time","#time":"time","#timel":"timel","#ifoda":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#turkumiyerarxiyasi":"categorytree","#turkumdaraxti":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks", +"#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","TURKUMDAGISAHIFALAR":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","SAHIFAHAJMI":"pagesize","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","SAHIFANOMI":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee", +"TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NOMFAZO":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","MAQOLASONI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","FAYLSONI":"numberoffiles","NUMBEROFFILES":"numberoffiles","FOYDALANUVCHISONI":"numberofusers","NUMBEROFUSERS":"numberofusers","FAOLFOYDALANUVCHISONI" +:"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SAHIFASONI":"numberofpages","NUMBEROFPAGES":"numberofpages","ADMINISTRATORSONI":"numberofadmins","NUMBEROFADMINS":"numberofadmins","OZGARISHSONI":"numberofedits","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","JORIYOY":"currentmonth","JORIYOY2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","JORIYOY1":"currentmonth1","CURRENTMONTH1":"currentmonth1","JORIYOYNOMI":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","JORIYOYNOMIQARATQICH":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","JORIYOYQISQARTMASI":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JORIYKUN":"currentday","CURRENTDAY":"currentday","JORIYKUN2":"currentday2","CURRENTDAY2":"currentday2","JORIYKUNNOMI":"currentdayname","CURRENTDAYNAME":"currentdayname","JORIYYIL":"currentyear","CURRENTYEAR":"currentyear","JORIYVAQT":"currenttime","CURRENTTIME":"currenttime", +"JORIYSOAT":"currenthour","CURRENTHOUR":"currenthour","MAHALLIYOY":"localmonth","MAHALLIYOY2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MAHALLIYOY1":"localmonth1","LOCALMONTH1":"localmonth1","MAHALLIYOYNOMI":"localmonthname","LOCALMONTHNAME":"localmonthname","MAHALLIYOYQARATQICH":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MAHALLIYOYQISQARTMASI":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","MAHALLIYKUN":"localday","LOCALDAY":"localday","MAHALLIYKUN2":"localday2","LOCALDAY2":"localday2","MAHALLIYKUNNOMI":"localdayname","LOCALDAYNAME":"localdayname","MAHALLIYYIL":"localyear","LOCALYEAR":"localyear","MAHALLIYVAQT":"localtime","LOCALTIME":"localtime","MAHALLIYSOAT":"localhour","LOCALHOUR":"localhour","SITENAME":"sitename","JORIYHAFTA":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP": +"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zʻʼ“»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ve.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ve.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-ve.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vec.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vec.json new file mode 100644 index 0000000..e0a99a4 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vec.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDICE__":"index","__INDEX__":"index","__NOINDICE__":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","genere":"gender","gender":"gender","plurale":"plural","plural":"plural","bidi":"bidi","#lingua":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#etichetta":"tag","#tag":"tag","#formatodata":"formatdate","#formatdate":"formatdate", +"#dateformat":"formatdate","#alberocategorie":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#se":"if","#if":"if","#seeq":"ifeq","#ifeq":"ifeq","#switch":"switch","#seesiste":"ifexist","#ifexist":"ifexist","#seespr":"ifexpr","#ifexpr":"ifexpr","#seerrore":"iferror","#iferror":"iferror","#tempo":"time","#time":"time","#timel":"timel","#espr":"expr","#expr":"expr","#rel2abs":"rel2abs","#patititolo":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","nomeserver":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"NUMEROPAGINE":"numberofpages","NUMBEROFPAGES":"numberofpages","NUMEROUTENTI":"numberofusers","NUMBEROFUSERS":"numberofusers","NUMEROUTENTIATTIVI":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMEROVOCI":"numberofarticles","NUMEROARTICOLI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NUMEROFILE":"numberoffiles","NUMBEROFFILES":"numberoffiles","NUMEROADMIN":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMEROMODIFICHE":"numberofedits","NUMEROEDIT":"numberofedits","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINEINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","DIMENSIONEPAGINA":"pagesize","PESOPAGINA":"pagesize","PAGESIZE":"pagesize","LIVELLOPROTEZIONE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE": +"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","TITOLOPAGINA":"pagename","PAGENAME":"pagename","TITOLOPAGINAE":"pagenamee","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","NOMESOTTOPAGINA":"subpagename","SUBPAGENAME":"subpagename","NOMESOTTOPAGINAE":"subpagenamee","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1", +"REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","MOSTRATITOLO":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","MESEATTUALE":"currentmonth","MESECORRENTE":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","NOMEMESEATTUALE":"currentmonthname","NOMEMESECORRENTE":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMEMESEATTUALEGEN":"currentmonthnamegen","NOMEMESECORRENTEGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","MESEATTUALEABBREV":"currentmonthabbrev","MESECORRENTEABBREV":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","GIORNOATTUALE":"currentday","GIORNOCORRENTE":"currentday","CURRENTDAY":"currentday","GIORNOATTUALE2":"currentday2","GIORNOCORRENTE2":"currentday2","CURRENTDAY2":"currentday2","NOMEGIORNOATTUALE":"currentdayname","NOMEGIORNOCORRENTE": +"currentdayname","CURRENTDAYNAME":"currentdayname","ANNOATTUALE":"currentyear","ANNOCORRENTE":"currentyear","CURRENTYEAR":"currentyear","ORARIOATTUALE":"currenttime","CURRENTTIME":"currenttime","ORAATTUALE":"currenthour","ORACORRENTE":"currenthour","CURRENTHOUR":"currenthour","MESELOCALE":"localmonth","MESELOCALE2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MESELOCALE1":"localmonth1","LOCALMONTH1":"localmonth1","NOMEMESELOCALE":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMEMESELOCALEGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","MESELOCALEABBREV":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","GIORNOLOCALE":"localday","LOCALDAY":"localday","GIORNOLOCALE2":"localday2","LOCALDAY2":"localday2","NOMEGIORNOLOCALE":"localdayname","LOCALDAYNAME":"localdayname","ANNOLOCALE":"localyear","LOCALYEAR":"localyear","ORARIOLOCALE":"localtime","LOCALTIME":"localtime","ORALOCALE":"localhour","LOCALHOUR":"localhour","NOMESITO":"sitename", +"SITENAME":"sitename","SETTIMANACORRENTE":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","SETTIMANALOCALE":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàéèíîìóòúù]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vep.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vep.json new file mode 100644 index 0000000..b3c3bda --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vep.json @@ -0,0 +1,9 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__sisukorrata__":"notoc","__notoc__":"notoc","__galeriita__":"nogallery","__nogallery__":"nogallery","__sisukordees__":"forcetoc","__forcetoc__":"forcetoc","__sisukord__":"toc","__toc__":"toc","__alaosalingita__":"noeditsection","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__": +"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__UUEALAOSALINK__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__UUEALAOSALINGITA__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__PEIDETUDKAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEKS__":"index","INDEKSIGA":"index","__INDEX__":"index","INDEKSITA":"noindex","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nr1":"nse","nse":"nse","urlencode":"urlencode","esivt":"lcfirst","lcfirst":"lcfirst","esist":"ucfirst","ucfirst":"ucfirst","vt":"lc","lc":"lc","st":"uc","uc":"uc","kohalikurl":"localurl","localurl":"localurl","kohalikurl1":"localurle","localurle":"localurle","täuz'url":"fullurl","koguurl":"fullurl","fullurl":"fullurl","koguurl1": +"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","arvuvormindus":"formatnum","formatnum":"formatnum","grammatik":"grammar","grammar":"grammar","sugu":"gender","gender":"gender","äilugu":"plural","plural":"plural","bidi":"bidi","#keel":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","failitee":"filepath","filepath":"filepath","pageid":"pageid","int":"int","#eri":"special","#special":"special","#eri1":"speciale","#speciale":"speciale","#tag":"tag","#kuupäevavormindus":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#paabel":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#kategooriapuu":"categorytree", +"#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","välistekeelelinkideta":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#omadus":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","serverinimi":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"KASUTAJAIDRÜHMAS":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","JÄRJESTA":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","LEHEKÜLGIKATEGOORIAS":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","KAITSETASE":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY": +"protectionexpiry","LEHEKÜLJENIMI":"pagename","PAGENAME":"pagename","LEHEKÜLJENIMI1":"pagenamee","PAGENAMEE":"pagenamee","KOGULEHEKÜLJENIMI":"fullpagename","FULLPAGENAME":"fullpagename","KOGULEHEKÜLJENIMI1":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ALAMLEHEKÜLJENIMI":"subpagename","SUBPAGENAME":"subpagename","ALAMLEHEKÜLJENIMI1":"subpagenamee","SUBPAGENAMEE":"subpagenamee","JUURLEHEKÜLJENIMI":"rootpagename","ROOTPAGENAME":"rootpagename","JUURLEHEKÜLJENIMI1":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NIMERUUMITANIMI":"basepagename","BASEPAGENAME":"basepagename","NIMERUUMITANIMI1":"basepagenamee","BASEPAGENAMEE":"basepagenamee","ARUTELUNIMI":"talkpagename","TALKPAGENAME":"talkpagename","ARUTELUNIMI1":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2": +"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NIMERUUM":"namespace","NAMESPACE":"namespace","NIMERUUM1":"namespacee","NAMESPACEE":"namespacee","NIMERUUMINUMBER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ARUTELUNIMERUUM":"talkspace","TALKSPACE":"talkspace","ARUTELUNIMERUUM1":"talkspacee","TALKSPACEE":"talkspacee","SISUNIMERUUM":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SISUNIMERUUM1":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","ARTIKLIMÄÄR":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","FAILIMÄÄR":"numberoffiles","NUMBEROFFILES":"numberoffiles","KASUTAJAMÄÄR":"numberofusers","NUMBEROFUSERS":"numberofusers","TEGUSKASUTAJAMÄÄR":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","LEHEMÄÄR": +"numberofpages","NUMBEROFPAGES":"numberofpages","ÜLEMAMÄÄR":"numberofadmins","NUMBEROFADMINS":"numberofadmins","REDIGEERIMISMÄÄR":"numberofedits","NUMBEROFEDITS":"numberofedits","PEALKIRI":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","HETKEKUU":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","HETKEKUU1":"currentmonth1","CURRENTMONTH1":"currentmonth1","HETKEKUUNIMETUS":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","HETKEKUUOM":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","HETKEKUULÜH":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HETKEKUUPÄEV":"currentday","CURRENTDAY":"currentday","HETKEKUUPÄEV2":"currentday2","CURRENTDAY2":"currentday2","HETKENÄDALAPÄEV":"currentdayname","CURRENTDAYNAME":"currentdayname","HETKEAASTA":"currentyear","CURRENTYEAR":"currentyear","HETKEAEG":"currenttime","CURRENTTIME":"currenttime","HETKETUND":"currenthour","CURRENTHOUR":"currenthour","KOHALIKKUU": +"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","KOHALIKKUU1":"localmonth1","LOCALMONTH1":"localmonth1","KOHALIKKUUNIMETUS":"localmonthname","LOCALMONTHNAME":"localmonthname","KOHALIKKUUOM":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","KOHALIKKUULÜH":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","KOHALIKKUUPÄEV":"localday","LOCALDAY":"localday","KOHALIKKUUPÄEV2":"localday2","LOCALDAY2":"localday2","KOHALIKNÄDALAPÄEV":"localdayname","LOCALDAYNAME":"localdayname","KOHALIKAASTA":"localyear","LOCALYEAR":"localyear","KOHALIKAEG":"localtime","LOCALTIME":"localtime","KOHALIKTUND":"localhour","LOCALHOUR":"localhour","SAITANNIMI":"sitename","KOHANIMI":"sitename","SITENAME":"sitename","HETKENÄDAL":"currentweek","CURRENTWEEK":"currentweek","HETKENÄDALAPÄEV1":"currentdow","CURRENTDOW":"currentdow","KOHALIKNÄDAL":"localweek","LOCALWEEK":"localweek","KOHALIKNÄDALAPÄEV1":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize", +"CURRENTVERSION":"currentversion","HETKEAJATEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","KOHALIKAJATEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","VAIKEKEEL":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([äöõšüža-z]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vi.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vi.json new file mode 100644 index 0000000..a1a9382 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vi.json @@ -0,0 +1,15 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__không_mục_lục__":"notoc","__khôngmụclục__":"notoc","__notoc__":"notoc","__không_album__":"nogallery","__khôngalbum__":"nogallery","__nogallery__":"nogallery","__luôn_mục_lục__":"forcetoc","__luônmụclục__":"forcetoc","__forcetoc__":"forcetoc","__mục_lục__":"toc","__mụclục__":"toc","__toc__": +"toc","__không_nút_sửa_mục__":"noeditsection","__khôngnútsửamục__":"noeditsection","__noeditsection__":"noeditsection","__không_chuyển_tên__":"notitleconvert","__khôngchuyểntên__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__không_chuyển_nội_dung__":"nocontentconvert","__khôngchuyểnnộidung__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIÊN_KẾT_MỤC_MỚI__":"newsectionlink","__LIÊNKẾTMỤCMỚI__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__KHÔNG_LIÊN_KẾT_MỤC_MỚI__":"nonewsectionlink","__KHÔNGLIÊNKẾTMỤCMỚI__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__THỂ_LOẠI_ẨN__":"hiddencat","__THỂLOẠIẨN__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__CHỈ_MỤC__":"index","__CHỈMỤC__":"index","__INDEX__":"index","__KHÔNG_CHỈ_MỤC__":"noindex", +"__KHÔNGCHỈMỤC__":"noindex","__NOINDEX__":"noindex","__ĐỔI_HƯỚNG_NHẤT_ĐỊNH__":"staticredirect","__ĐỔIHƯỚNGNHẤTĐỊNH__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__ĐỊNHHƯỚNG__":"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"kgt":"ns","ns":"ns","nse":"nse","mã_hóa_url":"urlencode","mãhóaurl":"urlencode","mã_hoá_url":"urlencode","mãhoáurl":"urlencode","urlencode":"urlencode","chữ_đầu_hoa":"lcfirst","chữđầuhoa":"lcfirst","lcfirst":"lcfirst","chữ_đầu_thường":"ucfirst","chữđầuthường":"ucfirst","ucfirst":"ucfirst","chữ_hoa":"lc","chữhoa":"lc","lc":"lc","chữ_thường":"uc","chữthường":"uc","uc":"uc","url_địa_phương":"localurl","urlđịaphương":"localurl","localurl":"localurl","localurle":"localurle","url_đủ":"fullurl","urlđủ":"fullurl","fullurl":"fullurl","fullurle":"fullurle", +"url_chuẩn":"canonicalurl","urlchuẩn":"canonicalurl","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","phân_chia_số":"formatnum","phânchiasố":"formatnum","formatnum":"formatnum","ngữ_pháp":"grammar","ngữpháp":"grammar","grammar":"grammar","giống":"gender","gender":"gender","số_nhiều":"plural","sốnhiều":"plural","plural":"plural","bidi":"bidi","#ngôn_ngữ":"language","#ngônngữ":"language","#language":"language","padleft":"padleft","padright":"padright","mã_hóa_neo":"anchorencode","mãhóaneo":"anchorencode","mã_hoá_neo":"anchorencode","mãhoáneo":"anchorencode","anchorencode":"anchorencode","đường_dẫn_tập_tin":"filepath","đườngdẫntậptin":"filepath","filepath":"filepath","id_trang":"pageid","idtrang":"pageid","pageid":"pageid","nội":"int","int":"int","#đặc_biệt":"special","#special":"special","#speciale":"speciale","#thẻ":"tag","#tag":"tag","#định_dạng_ngày":"formatdate","#địnhdạngngày":"formatdate", +"#formatdate":"formatdate","#dateformat":"formatdate","#đích":"target","#target":"target","#babel":"babel","#coordinates":"coordinates","#gọi":"invoke","#invoke":"invoke","#related":"related","#nếu":"if","#if":"if","#nếu_bằng":"ifeq","#nếubằng":"ifeq","#ifeq":"ifeq","#switch":"switch","#nếu_tồn_tại":"ifexist","#nếutồntại":"ifexist","#ifexist":"ifexist","#nếu_công_thức":"ifexpr","#nếucôngthức":"ifexpr","#ifexpr":"ifexpr","#nếu_lỗi":"iferror","#nếulỗi":"iferror","#iferror":"iferror","#giờ":"time","#time":"time","#giờ_địa_phương":"timel","#giờđịaphương":"timel","#timel":"timel","#công_thức":"expr","#côngthức":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#cây_thể_loại":"categorytree","#câythểloại":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","không_liên_kết_ngôn_ngữ_ngoài": +"noexternallanglinks","khôngliênkếtngônngữngoài":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#thuộc_tính":"property","#thuộctính":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#cốvấn":"mentor","#cố_vấn":"mentor","#mentor":"mentor","đường_dẫn_bài":"articlepath","đườngdẫnbài":"articlepath","lối_bài":"articlepath","lốibài":"articlepath","articlepath":"articlepath","máy_chủ":"server","máychủ":"server","server":"server","tên_máy_chủ":"servername","tênmáychủ":"servername","servername":"servername","đường_dẫn_kịch_bản":"scriptpath","đườngdẫnkịchbản":"scriptpath","đường_dẫn_script":"scriptpath","đườngdẫnscript":"scriptpath","scriptpath":"scriptpath","đường_dẫn_kiểu":"stylepath","đườngdẫnkiểu":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","tên_kho_wb":"wbreponame","tênkhowb":"wbreponame", +"wbreponame":"wbreponame"},{"CỠ_NHÓM":"numberingroup","CỠNHÓM":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","XẾP_MẶC_ĐỊNH":"defaultsort","XẾPMẶCĐỊNH":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","CỠ_THỂ_LOẠI":"pagesincategory","CỠTHỂLOẠI":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","CỠ_TRANG":"pagesize","CỠTRANG":"pagesize","PAGESIZE":"pagesize","MỨC_KHÓA":"protectionlevel","MỨCKHÓA":"protectionlevel","MỨC_KHOÁ":"protectionlevel","MỨCKHOÁ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","TÊN_TRANG":"pagename","TÊNTRANG":"pagename","PAGENAME":"pagename","TÊN_TRANG_2":"pagenamee","TÊNTRANG2":"pagenamee","PAGENAMEE":"pagenamee","TÊN_TRANG_ĐỦ":"fullpagename","TÊNTRANGĐỦ":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee", +"TÊN_TRANG_PHỤ":"subpagename","TÊNTRANGPHỤ":"subpagename","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","TÊN_TRANG_GỐC":"basepagename","TÊNTRANGGỐC":"basepagename","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TÊN_TRANG_THẢO_LUẬN":"talkpagename","TÊNTRANGTHẢOLUẬN":"talkpagename","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","TÊN_TRANG_NỘI_DUNG":"subjectpagename","TÊNTRANGNỘIDUNG":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","SỐ_BẢN":"revisionid","SỐBẢN":"revisionid","REVISIONID":"revisionid","NGÀY_BẢN":"revisionday","NGÀYBẢN":"revisionday","REVISIONDAY":"revisionday","NGÀY_BẢN_2":"revisionday2","NGÀYBẢN2":"revisionday2","REVISIONDAY2":"revisionday2","THÁNG_BẢN":"revisionmonth","THÁNGBẢN":"revisionmonth", +"REVISIONMONTH":"revisionmonth","THÁNG_BẢN_1":"revisionmonth1","THÁNGBẢN1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","NĂM_BẢN":"revisionyear","NĂMBẢN":"revisionyear","REVISIONYEAR":"revisionyear","MỐC_THỜI_GIAN_BẢN":"revisiontimestamp","MỐCTHỜIGIANBẢN":"revisiontimestamp","DẤU_THỜI_GIAN_BẢN":"revisiontimestamp","DẤUTHỜIGIANBẢN":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","NGƯỜI_DÙNG_BẢN":"revisionuser","NGƯỜIDÙNGBẢN":"revisionuser","REVISIONUSER":"revisionuser","NGUỒN_THEO_TẦNG":"cascadingsources","NGUỒNTHEOTẦNG":"cascadingsources","CASCADINGSOURCES":"cascadingsources","KHÔNG_GIAN_TÊN":"namespace","KHÔNGGIANTÊN":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","SỐ_KHÔNG_GIAN_TÊN":"namespacenumber","SỐKHÔNGGIANTÊN":"namespacenumber","NAMESPACENUMBER":"namespacenumber","KGT_THẢO_LUẬN":"talkspace","KGTTHẢOLUẬN":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee", +"KGT_NỘI_DUNG":"subjectspace","KGTNỘIDUNG":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","SỐ_BÀI":"numberofarticles","SỐBÀI":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","SỐ_TẬP_TIN":"numberoffiles","SỐTẬPTIN":"numberoffiles","NUMBEROFFILES":"numberoffiles","SỐ_THÀNH_VIÊN":"numberofusers","SỐTHÀNHVIÊN":"numberofusers","NUMBEROFUSERS":"numberofusers","SỐ_THÀNH_VIÊN_TÍCH_CỰC":"numberofactiveusers","SỐTHÀNHVIÊNTÍCHCỰC":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","SỐ_TRANG":"numberofpages","SỐTRANG":"numberofpages","NUMBEROFPAGES":"numberofpages","SỐ_BẢO_QUẢN_VIÊN":"numberofadmins","SỐBẢOQUẢNVIÊN":"numberofadmins","SỐ_QUẢN_LÝ":"numberofadmins","SỐQUẢNLÝ":"numberofadmins","SỐ_QUẢN_LÍ":"numberofadmins","SỐQUẢNLÍ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","SỐ_SỬA_ĐỔI": +"numberofedits","SỐSỬAĐỔI":"numberofedits","NUMBEROFEDITS":"numberofedits","TÊN_HIỂN_THỊ":"displaytitle","TÊNHIỂNTHỊ":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","THÁNG_NÀY":"currentmonth","THÁNGNÀY":"currentmonth","THÁNG_NÀY_2":"currentmonth","THÁNGNÀY2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","THÁNG_NÀY_1":"currentmonth1","THÁNGNÀY1":"currentmonth1","CURRENTMONTH1":"currentmonth1","TÊN_THÁNG_NÀY":"currentmonthname","TÊNTHÁNGNÀY":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","TÊN_DÀI_THÁNG_NÀY":"currentmonthnamegen","TÊNDÀITHÁNGNÀY":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","TÊN_NGẮN_THÁNG_NÀY":"currentmonthabbrev","TÊNNGẮNTHÁNGNÀY":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","NGÀY_NÀY":"currentday","NGÀYNÀY":"currentday","CURRENTDAY":"currentday","NGÀY_NÀY_2":"currentday2","NGÀYNÀY2":"currentday2","CURRENTDAY2": +"currentday2","TÊN_NGÀY_NÀY":"currentdayname","TÊNNGÀYNÀY":"currentdayname","CURRENTDAYNAME":"currentdayname","NĂM_NÀY":"currentyear","NĂMNÀY":"currentyear","CURRENTYEAR":"currentyear","GIỜ_NÀY":"currenttime","GIỜNÀY":"currenttime","CURRENTTIME":"currenttime","GIỜ_HIỆN_TẠI":"currenthour","GIỜHIỆNTẠI":"currenthour","CURRENTHOUR":"currenthour","THÁNG_ĐỊA_PHƯƠNG":"localmonth","THÁNGĐỊAPHƯƠNG":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","THÁNG_ĐỊA_PHƯƠNG_1":"localmonth1","THÁNGĐỊAPHƯƠNG1":"localmonth1","LOCALMONTH1":"localmonth1","TÊN_THÁNG_ĐỊA_PHƯƠNG":"localmonthname","TÊNTHÁNGĐỊAPHƯƠNG":"localmonthname","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","THÁNG_ĐỊA_PHƯƠNG_TẮT":"localmonthabbrev","THÁNGĐỊAPHƯƠNGTẮT":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","NGÀY_ĐỊA_PHƯƠNG":"localday","NGÀYĐỊAPHƯƠNG":"localday","LOCALDAY":"localday", +"NGÀY_ĐỊA_PHƯƠNG_2":"localday2","NGÀYĐỊAPHƯƠNG2":"localday2","LOCALDAY2":"localday2","TÊN_NGÀY_ĐỊA_PHƯƠNG":"localdayname","TÊNNGÀYĐỊAPHƯƠNG":"localdayname","LOCALDAYNAME":"localdayname","NĂM_ĐỊA_PHƯƠNG":"localyear","NĂMĐỊAPHƯƠNG":"localyear","LOCALYEAR":"localyear","THỜI_GIAN_ĐỊA_PHƯƠNG":"localtime","THỜIGIANĐỊAPHƯƠNG":"localtime","LOCALTIME":"localtime","GIỜ_ĐỊA_PHƯƠNG":"localhour","GIỜĐỊAPHƯƠNG":"localhour","LOCALHOUR":"localhour","TÊN_MẠNG":"sitename","TÊNMẠNG":"sitename","SITENAME":"sitename","TUẦN_NÀY":"currentweek","TUẦNNÀY":"currentweek","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","TUẦN_ĐỊA_PHƯƠNG":"localweek","TUẦNĐỊAPHƯƠNG":"localweek","LOCALWEEK":"localweek","LOCALDOW":"localdow","CỠ_PHIÊN_BẢN":"revisionsize","CỠPHIÊNBẢN":"revisionsize","REVISIONSIZE":"revisionsize","BẢN_NÀY":"currentversion","BẢNNÀY":"currentversion","CURRENTVERSION":"currentversion", +"MỐC_THỜI_GIAN_NÀY":"currenttimestamp","MỐCTHỜIGIANNÀY":"currenttimestamp","DẤU_THỜI_GIAN_NÀY":"currenttimestamp","DẤUTHỜIGIANNÀY":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","MỐC_THỜI_GIAN_ĐỊA_PHƯƠNG":"localtimestamp","MỐCTHỜIGIANĐỊAPHƯƠNG":"localtimestamp","DẤU_THỜI_GIAN_ĐỊA_PHƯƠNG":"localtimestamp","DẤUTHỜIGIANĐỊAPHƯƠNG":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","DẤU_HƯỚNG_VIẾT":"directionmark","DẤUHƯỚNGVIẾT":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","NGÔN_NGỮ_NỘI_DUNG":"contentlanguage","NGÔNNGỮNỘIDUNG":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vls.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vls.json new file mode 100644 index 0000000..1f11fdf --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vls.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__geeninhoud__":"notoc","__notoc__":"notoc","__geen_galerij__":"nogallery","__nogallery__":"nogallery","__inhoud_dwingen__":"forcetoc","__forceerinhoud__":"forcetoc","__forcetoc__":"forcetoc","__inhoud__":"toc","__toc__":"toc","__nietbewerkbaresectie__":"noeditsection","__noeditsection__":"noeditsection", +"__geenpaginanaamconversie__":"notitleconvert","__geentitelconversie__":"notitleconvert","__geentc__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__geeninhoudconversie__":"nocontentconvert","__geenic__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NIEUWESECTIELINK__":"newsectionlink","__NIEUWESECTIEKOPPELING__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__GEENNIEUWKOPJEKOPPELING__":"nonewsectionlink","__GEENNIEUWESECTIELINK__":"nonewsectionlink","__GEENNIEUWKOPJEVERWIJZING__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERBORGENCAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__GEENINDEX__":"noindex","__NOINDEX__":"noindex","__STATISCHEDOORVERWIJZING__":"staticredirect","__STATISCHEREDIRECT__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__": +"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nre":"nse","nse":"nse","urlcoderen":"urlencode","codeerurl":"urlencode","urlencode":"urlencode","kleerste":"lcfirst","lcfirst":"lcfirst","gleerste":"ucfirst","hleerste":"ucfirst","ucfirst":"ucfirst","kl":"lc","lc":"lc","hl":"uc","uc":"uc","lokaleurl":"localurl","localurl":"localurl","lokaleurle":"localurle","localurle":"localurle","volledigeurl":"fullurl","fullurl":"fullurl","volledigeurle":"fullurle","fullurle":"fullurle","canoiekeurl":"canonicalurl","canonicalurl":"canonicalurl","canoniekeurle":"canonicalurle","canonicalurle":"canonicalurle","formatteernum":"formatnum","numformatteren":"formatnum","formatnum":"formatnum","grammatica":"grammar","grammar":"grammar","geslacht":"gender","gender":"gender","meervoud":"plural","plural":"plural","bidi":"bidi","#taal":"language","#language":"language","linksopvullen":"padleft","padleft":"padleft","rechtsopvullen":"padright", +"padright":"padright","ankercoderen":"anchorencode","codeeranker":"anchorencode","anchorencode":"anchorencode","bestandspad":"filepath","filepath":"filepath","paginaid":"pageid","pageid":"pageid","int":"int","#speciaal":"special","#special":"special","#speciaale":"speciale","#speciale":"speciale","#label":"tag","#tag":"tag","#datumopmaak":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#aanroepen":"invoke","#invoke":"invoke","#related":"related","#als":"if","#if":"if","#alsgelijk":"ifeq","#ifeq":"ifeq","#schakelen":"switch","#switch":"switch","#alsbestaat":"ifexist","#ifexist":"ifexist","#alsexpressie":"ifexpr","#ifexpr":"ifexpr","#alsfout":"iferror","#iferror":"iferror","#tijd":"time","#time":"time","#tijdl":"timel","#timel":"timel","#expressie":"expr","#expr":"expr","#relatiefnaarabsoluut":"rel2abs","#rel2abs":"rel2abs","#paginanaamdelen":"titleparts","#titleparts":"titleparts","#categorieboom": +"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","geenexternetaalkoppelingen":"noexternallanglinks","geenexternetaalverwijzingen":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschap":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","artikelpad":"articlepath","articlepath":"articlepath","server":"server","servernaam":"servername","servername":"servername","scriptpad":"scriptpath","scriptpath":"scriptpath","stijlpad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"AANTALINGROEP":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","STANDAARDSORTERING":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINASINCATEGORIE":"pagesincategory","PAGINASINCAT": +"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGINAGROOTTE":"pagesize","PAGESIZE":"pagesize","BEVEILIGINGSNIVEAU":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGINANAAM":"pagename","PAGENAME":"pagename","PAGINANAAME":"pagenamee","PAGENAMEE":"pagenamee","VOLLEDIGEPAGINANAAM":"fullpagename","FULLPAGENAME":"fullpagename","VOLLEDIGEPAGINANAAME":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","DEELPAGINANAAM":"subpagename","SUBPAGENAME":"subpagename","DEELPAGINANAAME":"subpagenamee","SUBPAGENAMEE":"subpagenamee","ROOTPAGINANAAM":"rootpagename","ROOTPAGENAME":"rootpagename","ROOTPAGINANAAME":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","BASISPAGINANAAM":"basepagename","BASEPAGENAME":"basepagename","BASISPAGINANAAME":"basepagenamee","BASEPAGENAMEE":"basepagenamee","OVERLEGPAGINANAAM":"talkpagename","TALKPAGENAME":"talkpagename","OVERLEGPAGINANAAME":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee", +"ONDERWERPPAGINANAAM":"subjectpagename","ARTIKELPAGINANAAM":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ONDERWERPPAGINANAAME":"subjectpagenamee","ARTIKELPAGINANAAME":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","VERSIEID":"revisionid","REVISIONID":"revisionid","VERSIEDAG":"revisionday","REVISIONDAY":"revisionday","VERSIEDAG2":"revisionday2","REVISIONDAY2":"revisionday2","VERSIEMAAND":"revisionmonth","REVISIONMONTH":"revisionmonth","VERSIEMAAND1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","VERSIEJAAR":"revisionyear","REVISIONYEAR":"revisionyear","VERSIETIJD":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","VERSIEGEBRUIKER":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAAMRUIMTE":"namespace","NAMESPACE":"namespace","NAAMRUIMTEE":"namespacee","NAMESPACEE":"namespacee","NAAMRUIMTENUMMER":"namespacenumber","NAMESPACENUMBER": +"namespacenumber","OVERLEGRUIMTE":"talkspace","TALKSPACE":"talkspace","OVERLEGRUIMTEE":"talkspacee","TALKSPACEE":"talkspacee","ONDERWERPRUIMTE":"subjectspace","ARTIKELRUIMTE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ONDERWERPRUIMTEE":"subjectspacee","ARTIKELRUIMTEE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","AANTALARTIKELEN":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","AANTALBESTANDEN":"numberoffiles","NUMBEROFFILES":"numberoffiles","AANTALGEBRUIKERS":"numberofusers","NUMBEROFUSERS":"numberofusers","AANTALACTIEVEGEBRUIKERS":"numberofactiveusers","ACTIEVEGEBRUIKERS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","AANTALPAGINAS":"numberofpages","AANTALPAGINA'S":"numberofpages","AANTALPAGINA’S":"numberofpages","NUMBEROFPAGES":"numberofpages","AANTALBEHEERDERS":"numberofadmins","AANTALADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","AANTALBEWERKINGEN":"numberofedits", +"NUMBEROFEDITS":"numberofedits","WEERGEGEVENTITEL":"displaytitle","TOONTITEL":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","HUIDIGEMAAND":"currentmonth","HUIDIGEMAAND2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","HUIDIGEMAAND1":"currentmonth1","CURRENTMONTH1":"currentmonth1","HUIDIGEMAANDNAAM":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","HUIDIGEMAANDGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","HUIDIGEMAANDAFK":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HUIDIGEDAG":"currentday","CURRENTDAY":"currentday","HUIDIGEDAG2":"currentday2","CURRENTDAY2":"currentday2","HUIDIGEDAGNAAM":"currentdayname","CURRENTDAYNAME":"currentdayname","HUIDIGJAAR":"currentyear","CURRENTYEAR":"currentyear","HUIDIGETIJD":"currenttime","CURRENTTIME":"currenttime","HUIDIGUUR":"currenthour","CURRENTHOUR":"currenthour","PLAATSELIJKEMAAND":"localmonth","LOKALEMAAND":"localmonth","LOKALEMAAND2":"localmonth", +"LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALEMAAND1":"localmonth1","LOCALMONTH1":"localmonth1","PLAATSELIJKEMAANDNAAM":"localmonthname","LOKALEMAANDNAAM":"localmonthname","LOCALMONTHNAME":"localmonthname","PLAATSELIJKEMAANDNAAMGEN":"localmonthnamegen","LOKALEMAANDNAAMGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","PLAATSELIJKEMAANDAFK":"localmonthabbrev","LOKALEMAANDAFK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","PLAATSELIJKEDAG":"localday","LOKALEDAG":"localday","LOCALDAY":"localday","PLAATSELIJKEDAG2":"localday2","LOKALEDAG2":"localday2","LOCALDAY2":"localday2","PLAATSELIJKEDAGNAAM":"localdayname","LOKALEDAGNAAM":"localdayname","LOCALDAYNAME":"localdayname","PLAATSELIJKJAAR":"localyear","LOKAALJAAR":"localyear","LOCALYEAR":"localyear","PLAATSELIJKETIJD":"localtime","LOKALETIJD":"localtime","LOCALTIME":"localtime","PLAATSELIJKUUR":"localhour","LOKAALUUR":"localhour","LOCALHOUR":"localhour","SITENAAM":"sitename","SITENAME":"sitename", +"HUIDIGEWEEK":"currentweek","CURRENTWEEK":"currentweek","HUIDIGEDVDW":"currentdow","CURRENTDOW":"currentdow","PLAATSELIJKEWEEK":"localweek","LOKALEWEEK":"localweek","LOCALWEEK":"localweek","PLAATSELIJKEDVDW":"localdow","LOKALEDVDW":"localdow","LOCALDOW":"localdow","VERSIEGROOTTE":"revisionsize","REVISIONSIZE":"revisionsize","HUIDIGEVERSIE":"currentversion","CURRENTVERSION":"currentversion","HUIDIGETIJDSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","PLAATSELIJKETIJDSTEMPEL":"localtimestamp","LOKALETIJDSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","RICHTINGMARKERING":"directionmark","RICHTINGSMARKERING":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHOUDSTAAL":"contentlanguage","INHOUDTAAL":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zäöüïëéèà]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vo.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-vo.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-wa.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-wa.json new file mode 100644 index 0000000..161ed79 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-wa.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode","initminus":"lcfirst","lcfirst":"lcfirst","initmajus": +"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int","#spécial":"special","#special":"special","#spéciale": +"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property","#property":"property","#statements":"statements", +"#commaseparatedlist":"commaSeparatedList","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","CLEFDETRI":"defaultsort", +"CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee","ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMPAGE":"pagename","PAGENAME":"pagename","NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename", +"FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid","REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday", +"REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev", +"JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL":"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL": +"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zåâêîôûçéè]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-war.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-war.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-war.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-wo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-wo.json new file mode 100644 index 0000000..30830cb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-wo.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__aucunsommaire__":"notoc","__aucunetdm__":"notoc","__notoc__":"notoc","__aucunegalerie__":"nogallery","__nogallery__":"nogallery","__forcersommaire__":"forcetoc","__forcertdm__":"forcetoc","__forcetoc__":"forcetoc","__sommaire__":"toc","__tdm__":"toc","__toc__":"toc","__sectionnoneditable__":"noeditsection", +"__noeditsection__":"noeditsection","__sansconversiontitre__":"notitleconvert","__sansct__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__sansconversioncontenu__":"nocontentconvert","__sanscc__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__LIENNOUVELLESECTION__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__AUCUNLIENNOUVELLESECTION__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__CATCACHEE__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__AUCUNINDEX__":"noindex","__NOINDEX__":"noindex","__REDIRECTIONSTATIQUE__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"espacen":"ns","ns":"ns","espacenx":"nse","nse":"nse","encodeurl":"urlencode","urlencode":"urlencode", +"initminus":"lcfirst","lcfirst":"lcfirst","initmajus":"ucfirst","initcapit":"ucfirst","ucfirst":"ucfirst","minus":"lc","lc":"lc","majus":"uc","capit":"uc","uc":"uc","urllocale":"localurl","localurl":"localurl","urllocalex":"localurle","localurle":"localurle","urlcomplete":"fullurl","fullurl":"fullurl","urlcompletex":"fullurle","fullurle":"fullurle","urlcanonique":"canonicalurl","canonicalurl":"canonicalurl","urlcanoniquex":"canonicalurle","canonicalurle":"canonicalurle","formatnombre":"formatnum","formatnum":"formatnum","grammaire":"grammar","grammar":"grammar","genre":"gender","gender":"gender","pluriel":"plural","plural":"plural","bidi":"bidi","#langue":"language","#language":"language","bourragegauche":"padleft","bourregauche":"padleft","padleft":"padleft","bourragedroite":"padright","bourredroite":"padright","padright":"padright","encodeancre":"anchorencode","anchorencode":"anchorencode","chemin":"filepath","filepath":"filepath","idpage":"pageid","pageid":"pageid","int":"int", +"#spécial":"special","#special":"special","#spéciale":"speciale","#speciale":"speciale","#balise":"tag","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoque":"invoke","#invoke":"invoke","#related":"related","#si":"if","#if":"if","#si=":"ifeq","#ifeq":"ifeq","#selon":"switch","#switch":"switch","#siexiste":"ifexist","#ifexist":"ifexist","#siexpr":"ifexpr","#ifexpr":"ifexpr","#sierreur":"iferror","#iferror":"iferror","#heure":"time","#time":"time","#heurel":"timel","#timel":"timel","#expr":"expr","#relenabs":"rel2abs","#rel2abs":"rel2abs","#partiestitre":"titleparts","#titleparts":"titleparts","#arbrecatégories":"categorytree","#arbrecats":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","sanslienexterne":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#propriété":"property", +"#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","cheminarticle":"articlepath","articlepath":"articlepath","serveur":"server","server":"server","nomserveur":"servername","servername":"servername","cheminscript":"scriptpath","scriptpath":"scriptpath","cheminstyle":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NOMBREDANSGROUPE":"numberingroup","NBDANSGROUPE":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","CLEFDETRI":"defaultsort","CLEDETRI":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESDANSCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","TAILLEPAGE":"pagesize","PAGESIZE":"pagesize","NIVEAUDEPROTECTION":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NOMPAGE":"pagename","PAGENAME":"pagename", +"NOMPAGEX":"pagenamee","PAGENAMEE":"pagenamee","NOMPAGECOMPLET":"fullpagename","FULLPAGENAME":"fullpagename","NOMPAGECOMPLETX":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","NOMSOUSPAGE":"subpagename","SUBPAGENAME":"subpagename","NOMSOUSPAGEX":"subpagenamee","SUBPAGENAMEE":"subpagenamee","NOMPAGERACINE":"rootpagename","ROOTPAGENAME":"rootpagename","NOMPAGERACINEX":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","NOMBASEDEPAGE":"basepagename","BASEPAGENAME":"basepagename","NOMBASEDEPAGEX":"basepagenamee","BASEPAGENAMEE":"basepagenamee","NOMPAGEDISCUSSION":"talkpagename","TALKPAGENAME":"talkpagename","NOMPAGEDISCUSSIONX":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","NOMPAGESUJET":"subjectpagename","NOMPAGEARTICLE":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","NOMPAGESUJETX":"subjectpagenamee","NOMPAGEARTICLEX":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","IDVERSION":"revisionid", +"REVISIONID":"revisionid","JOURVERSION":"revisionday","JOUR1VERSION":"revisionday","REVISIONDAY":"revisionday","JOUR2VERSION":"revisionday2","REVISIONDAY2":"revisionday2","MOISVERSION":"revisionmonth","REVISIONMONTH":"revisionmonth","MOISVERSION1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ANNEEVERSION":"revisionyear","REVISIONYEAR":"revisionyear","INSTANTVERSION":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","UTILISATEURVERSION":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ESPACENOMMAGE":"namespace","NAMESPACE":"namespace","ESPACENOMMAGEX":"namespacee","NAMESPACEE":"namespacee","NOMBREESPACENOMMAGE":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ESPACEDISCUSSION":"talkspace","TALKSPACE":"talkspace","ESPACEDISCUSSIONX":"talkspacee","TALKSPACEE":"talkspacee","ESPACESUJET":"subjectspace","ESPACEARTICLE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ESPACESUJETX":"subjectspacee", +"ESPACEARTICLEX":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NOMBREARTICLES":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","NOMBREFICHIERS":"numberoffiles","NUMBEROFFILES":"numberoffiles","NOMBREUTILISATEURS":"numberofusers","NUMBEROFUSERS":"numberofusers","NOMBREUTILISATEURSACTIFS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NOMBREPAGES":"numberofpages","NUMBEROFPAGES":"numberofpages","NOMBREADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NOMBREMODIFS":"numberofedits","NUMBEROFEDITS":"numberofedits","AFFICHERTITRE":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","=":"=","MOISACTUEL":"currentmonth","MOIS2ACTUEL":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","MOIS1ACTUEL":"currentmonth1","CURRENTMONTH1":"currentmonth1","NOMMOISACTUEL":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","NOMGENMOISACTUEL":"currentmonthnamegen","CURRENTMONTHNAMEGEN": +"currentmonthnamegen","ABREVMOISACTUEL":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","JOURACTUEL":"currentday","JOUR1ACTUEL":"currentday","CURRENTDAY":"currentday","JOUR2ACTUEL":"currentday2","CURRENTDAY2":"currentday2","NOMJOURACTUEL":"currentdayname","CURRENTDAYNAME":"currentdayname","ANNEEACTUELLE":"currentyear","CURRENTYEAR":"currentyear","HORAIREACTUEL":"currenttime","CURRENTTIME":"currenttime","HEUREACTUELLE":"currenthour","CURRENTHOUR":"currenthour","MOISLOCAL":"localmonth","MOIS2LOCAL":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","MOIS1LOCAL":"localmonth1","LOCALMONTH1":"localmonth1","NOMMOISLOCAL":"localmonthname","LOCALMONTHNAME":"localmonthname","NOMGENMOISLOCAL":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","ABREVMOISLOCAL":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","JOURLOCAL":"localday","JOUR1LOCAL":"localday","LOCALDAY":"localday","JOUR2LOCAL":"localday2","LOCALDAY2":"localday2","NOMJOURLOCAL": +"localdayname","LOCALDAYNAME":"localdayname","ANNEELOCALE":"localyear","LOCALYEAR":"localyear","HORAIRELOCAL":"localtime","LOCALTIME":"localtime","HEURELOCALE":"localhour","LOCALHOUR":"localhour","NOMSITE":"sitename","SITENAME":"sitename","SEMAINEACTUELLE":"currentweek","CURRENTWEEK":"currentweek","JDSACTUEL":"currentdow","CURRENTDOW":"currentdow","SEMAINELOCALE":"localweek","LOCALWEEK":"localweek","JDSLOCAL":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","VERSIONACTUELLE":"currentversion","CURRENTVERSION":"currentversion","INSTANTACTUEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","INSTANTLOCAL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","MARQUEDIRECTION":"directionmark","MARQUEDIR":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","LANGUECONTENU":"contentlanguage","LANGCONTENU":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zàâçéèêîôûäëïöüùÇÉÂÊÎÔÛÄËÏÖÜÀÈÙ]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-wuu.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-wuu.json new file mode 100644 index 0000000..8c64d83 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-wuu.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__无目录__":"notoc","__notoc__":"notoc","__無目錄__":"notoc","__无图库__":"nogallery","__nogallery__":"nogallery","__無圖庫__":"nogallery","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__強制目錄__":"forcetoc","__目录__":"toc","__toc__":"toc","__目錄__":"toc","__无编辑段落__": +"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__無段落編輯__":"noeditsection","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換標題__":"notitleconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert","__不轉換內容__":"nocontentconvert"},{"__新段落链接__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__无新段落链接__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__隱藏分類__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__索引__":"index","__INDEX__":"index","__无索引__":"noindex","__NOINDEX__":"noindex","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__靜態重新導向__":"staticredirect","__NOGLOBAL__":"noglobal","__消除歧义__": +"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"名称空间":"ns","ns":"ns","命名空間":"ns","名字空间":"ns","名称空间e":"nse","nse":"nse","命名空間e":"nse","名字空间e":"nse","url编码":"urlencode","urlencode":"urlencode","小写首字":"lcfirst","lcfirst":"lcfirst","大写首字":"ucfirst","ucfirst":"ucfirst","小写":"lc","lc":"lc","大写":"uc","uc":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","完整url":"fullurl","fullurl":"fullurl","完整url等同":"fullurle","fullurle":"fullurle","规范url":"canonicalurl","canonicalurl":"canonicalurl","规范url等同":"canonicalurle","canonicalurle":"canonicalurle","格式化数字":"formatnum","formatnum":"formatnum","语法":"grammar","grammar":"grammar","性别":"gender","gender":"gender","性別":"gender","性":"gender","复数":"plural","plural":"plural","bidi":"bidi","#语言": +"language","#language":"language","#語言":"language","左填充":"padleft","padleft":"padleft","右填充":"padright","padright":"padright","锚编码":"anchorencode","anchorencode":"anchorencode","文件路径":"filepath","filepath":"filepath","页面id":"pageid","pageid":"pageid","頁面id":"pageid","界面":"int","int":"int","#特殊":"special","#special":"special","#特殊等同":"speciale","#speciale":"speciale","#标记":"tag","#tag":"tag","#格式化日期":"formatdate","#日期格式化":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#目标":"target","#目標":"target","#target":"target","#巴别":"babel","#巴別":"babel","#babel":"babel","#coordinates":"coordinates","#调用":"invoke","#調動":"invoke","#invoke":"invoke","#related":"related","#若":"if","#如果":"if","#非空式":"if","#if":"if","#若相等":"ifeq","#如果相等":"ifeq","#相同式":"ifeq","#匹配式":"ifeq","#ifeq":"ifeq","#开关":"switch","#转换":"switch","#多选式":"switch" +,"#多条件式":"switch","#双射式":"switch","#轉換":"switch","#switch":"switch","#若有":"ifexist","#如有":"ifexist","#存在式":"ifexist","#ifexist":"ifexist","#若表达式":"ifexpr","#若表達式":"ifexpr","#ifexpr":"ifexpr","#如果错误":"iferror","#错误式":"iferror","#如果錯誤":"iferror","#iferror":"iferror","#时间":"time","#時間":"time","#time":"time","#时间l":"timel","#時間l":"timel","#timel":"timel","#表达式":"expr","#计算式":"expr","#表達式":"expr","#expr":"expr","#rel2abs":"rel2abs","#标题组成部分":"titleparts","#titleparts":"titleparts","#分类树":"categorytree","#分類樹":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","无外部语言连接":"noexternallanglinks","隱藏跨語言連結":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#属性":"property","#屬性":"property","#property":"property","#statements": +"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","条目路径":"articlepath","articlepath":"articlepath","服务器":"server","server":"server","伺服器":"server","服务器名":"servername","servername":"servername","伺服器名稱":"servername","脚本路径":"scriptpath","scriptpath":"scriptpath","样式路径":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wb报告名":"wbreponame","wb報表名稱":"wbreponame","wbreponame":"wbreponame"},{"组中用户数":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","默认排序":"defaultsort","默认排序关键字":"defaultsort","默认分类排序":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","分类中页面数":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","页面大小":"pagesize","PAGESIZE":"pagesize","保护级别":"protectionlevel","PROTECTIONLEVEL": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","頁面名稱":"pagename","页面名等同":"pagenamee","页面名称等同":"pagenamee","PAGENAMEE":"pagenamee","页面全称":"fullpagename","完整页面名称":"fullpagename","FULLPAGENAME":"fullpagename","完整页面名称等同":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","子页面名称":"subpagename","SUBPAGENAME":"subpagename","子页面名称等同":"subpagenamee","SUBPAGENAMEE":"subpagenamee","根页面名称":"rootpagename","ROOTPAGENAME":"rootpagename","根頁面名稱":"rootpagename","根页面名称等同":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","根頁面名稱E":"rootpagenamee","基础页面名称":"basepagename","BASEPAGENAME":"basepagename","基础页面名称等同":"basepagenamee","BASEPAGENAMEE":"basepagenamee","讨论页面名称":"talkpagename","对话页面名称":"talkpagename","TALKPAGENAME": +"talkpagename","讨论页面名称等同":"talkpagenamee","对话页面名称等同":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","主名字空间页面名称":"subjectpagename","条目页面名称":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主名字空间页面名称等同":"subjectpagenamee","条目页面名称等同":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","修订ID":"revisionid","REVISIONID":"revisionid","修订日":"revisionday","REVISIONDAY":"revisionday","修订日2":"revisionday2","REVISIONDAY2":"revisionday2","修订月":"revisionmonth","REVISIONMONTH":"revisionmonth","修订月1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","修订年":"revisionyear","REVISIONYEAR":"revisionyear","修订时间戳":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","修订用户":"revisionuser","REVISIONUSER":"revisionuser","修訂使用者":"revisionuser","级联来源": +"cascadingsources","CASCADINGSOURCES":"cascadingsources","名字空间":"namespace","NAMESPACE":"namespace","命名空間":"namespace","名字空间等同":"namespacee","NAMESPACEE":"namespacee","名字空间编号":"namespacenumber","NAMESPACENUMBER":"namespacenumber","命名空間數":"namespacenumber","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","對話空間":"talkspace","讨论空间等同":"talkspacee","讨论名字空间等同":"talkspacee","TALKSPACEE":"talkspacee","主名字空间":"subjectspace","条目名字空间":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主名字空间等同":"subjectspacee","条目名字空间等同":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","文章數":"numberofarticles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles","檔案數":"numberoffiles","用户数": +"numberofusers","NUMBEROFUSERS":"numberofusers","使用者人數量":"numberofusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","活躍使用者人數":"numberofactiveusers","页面数":"numberofpages","NUMBEROFPAGES":"numberofpages","頁面數":"numberofpages","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","管理員數":"numberofadmins","编辑数":"numberofedits","NUMBEROFEDITS":"numberofedits","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","顯示標題":"displaytitle","!":"!","=":"=","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","本月1":"currentmonth1","CURRENTMONTH1":"currentmonth1","本月名":"currentmonthname","本月名称":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","本月名属格":"currentmonthnamegen","本月名称属格":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月简称":"currentmonthabbrev" +,"CURRENTMONTHABBREV":"currentmonthabbrev","本月縮寫":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","今天2":"currentday2","CURRENTDAY2":"currentday2","星期":"currentdayname","今天名":"currentdayname","今天名称":"currentdayname","CURRENTDAYNAME":"currentdayname","今年":"currentyear","CURRENTYEAR":"currentyear","当前时间":"currenttime","此时":"currenttime","CURRENTTIME":"currenttime","目前時間":"currenttime","当前小时":"currenthour","CURRENTHOUR":"currenthour","本地月":"localmonth","本地月2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","本地月1":"localmonth1","LOCALMONTH1":"localmonth1","本地月份名":"localmonthname","LOCALMONTHNAME":"localmonthname","本地月历":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","本地月缩写":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","本地日":"localday","LOCALDAY":"localday","本地日2":"localday2","LOCALDAY2":"localday2", +"本地日名":"localdayname","LOCALDAYNAME":"localdayname","本地年":"localyear","LOCALYEAR":"localyear","本地时间":"localtime","LOCALTIME":"localtime","本地小时":"localhour","LOCALHOUR":"localhour","站点名称":"sitename","SITENAME":"sitename","網站名稱":"sitename","本周":"currentweek","CURRENTWEEK":"currentweek","当前DOW":"currentdow","CURRENTDOW":"currentdow","本地周":"localweek","LOCALWEEK":"localweek","本地DOW":"localdow","LOCALDOW":"localdow","修订大小":"revisionsize","REVISIONSIZE":"revisionsize","当前版本":"currentversion","CURRENTVERSION":"currentversion","目前版本":"currentversion","当前时间戳":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","本地时间戳":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","方向标记":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","內容語言": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^()(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-xal.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-xal.json new file mode 100644 index 0000000..94e42e2 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-xal.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__без_оглавления__":"notoc","__без_огл__":"notoc","__notoc__":"notoc","__без_галереи__":"nogallery","__nogallery__":"nogallery","__обязательное_оглавление__":"forcetoc","__обяз_огл__":"forcetoc","__forcetoc__":"forcetoc","__оглавление__":"toc","__огл__": +"toc","__toc__":"toc","__без_редактирования_раздела__":"noeditsection","__noeditsection__":"noeditsection","__без_преобразования_заголовка__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__без_преобразования_текста__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__ССЫЛКА_НА_НОВЫЙ_РАЗДЕЛ__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__БЕЗ_ССЫЛКИ_НА_НОВЫЙ_РАЗДЕЛ__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__СКРЫТАЯ_КАТЕГОРИЯ__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__ИНДЕКС__":"index","__INDEX__":"index","__БЕЗ_ИНДЕКСА__":"noindex","__NOINDEX__":"noindex","__СТАТИЧЕСКОЕ_ПЕРЕНАПРАВЛЕНИЕ__":"staticredirect","__STATICREDIRECT__":"staticredirect", +"__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"пи":"ns","ns":"ns","пик":"nse","nse":"nse","закодированный_адрес":"urlencode","urlencode":"urlencode","первая_буква_маленькая":"lcfirst","lcfirst":"lcfirst","первая_буква_большая":"ucfirst","ucfirst":"ucfirst","маленькими_буквами":"lc","lc":"lc","большими_буквами":"uc","uc":"uc","локальный_адрес":"localurl","localurl":"localurl","локальный_адрес_2":"localurle","localurle":"localurle","полный_адрес":"fullurl","fullurl":"fullurl","полный_адрес_2":"fullurle","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","форматировать_число":"formatnum","formatnum":"formatnum","падеж":"grammar","grammar":"grammar","пол":"gender","gender":"gender","множественное_число":"plural","plural":"plural","bidi": +"bidi","#язык":"language","#language":"language","заполнить_слева":"padleft","padleft":"padleft","заполнить_справа":"padright","padright":"padright","кодировать_метку":"anchorencode","anchorencode":"anchorencode","путь_к_файлу":"filepath","filepath":"filepath","идентификатор_страницы":"pageid","pageid":"pageid","внутр":"int","int":"int","#служебная":"special","#special":"special","#speciale":"speciale","#метка":"tag","#тег":"tag","#тэг":"tag","#tag":"tag","#форматдаты":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#деревокатегорий":"categorytree","#categorytree":"categorytree","#target":"target","#вавилон":"babel","#babel":"babel","#coordinates":"coordinates","#вызвать":"invoke","#invoke":"invoke","#related":"related","#если":"if","#if":"if","#ifeq":"ifeq","#переключатель":"switch","#switch":"switch", +"#ifexist":"ifexist","#ifexpr":"ifexpr","#еслиошибка":"iferror","#iferror":"iferror","#время":"time","#time":"time","#мвремя":"timel","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#свойство":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","путь_к_статье":"articlepath","articlepath":"articlepath","сервер":"server","server":"server","название_сервера":"servername","servername":"servername","путь_к_скрипту":"scriptpath","scriptpath":"scriptpath","путь_к_стилю":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"КОЛИЧЕСТВО_СТРАНИЦ":"numberofpages","NUMBEROFPAGES":"numberofpages", +"КОЛИЧЕСТВО_УЧАСТНИКОВ":"numberofusers","NUMBEROFUSERS":"numberofusers","КОЛИЧЕСТВО_АКТИВНЫХ_УЧАСТНИКОВ":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","КОЛИЧЕСТВО_СТАТЕЙ":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","КОЛИЧЕСТВО_ФАЙЛОВ":"numberoffiles","NUMBEROFFILES":"numberoffiles","КОЛИЧЕСТВО_АДМИНИСТРАТОРОВ":"numberofadmins","NUMBEROFADMINS":"numberofadmins","ЧИСЛО_В_ГРУППЕ":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","КОЛИЧЕСТВО_ПРАВОК":"numberofedits","NUMBEROFEDITS":"numberofedits","СОРТИРОВКА_ПО_УМОЛЧАНИЮ":"defaultsort","КЛЮЧ_СОРТИРОВКИ":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","СТРАНИЦ_В_КАТЕГОРИИ":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT": +"pagesincategory","РАЗМЕР_СТРАНИЦЫ":"pagesize","PAGESIZE":"pagesize","УРОВЕНЬ_ЗАЩИТЫ":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","ПРОСТРАНСТВО_ИМЁН_2":"namespacee","NAMESPACEE":"namespacee","НОМЕР_ПРОСТРАНСТВА_ИМЁН":"namespacenumber","NAMESPACENUMBER":"namespacenumber","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ":"talkspace","TALKSPACE":"talkspace","ПРОСТРАНСТВО_ОБСУЖДЕНИЙ_2":"talkspacee","TALKSPACEE":"talkspacee","ПРОСТРАНСТВО_СТАТЕЙ":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ПРОСТРАНСТВО_СТАТЕЙ_2":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","НАЗВАНИЕ_СТРАНИЦЫ":"pagename","PAGENAME":"pagename","НАЗВАНИЕ_СТРАНИЦЫ_2":"pagenamee","PAGENAMEE":"pagenamee","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ":"fullpagename", +"FULLPAGENAME":"fullpagename","ПОЛНОЕ_НАЗВАНИЕ_СТРАНИЦЫ_2":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ":"basepagename","BASEPAGENAME":"basepagename","ОСНОВА_НАЗВАНИЯ_СТРАНИЦЫ_2":"basepagenamee","BASEPAGENAMEE":"basepagenamee","НАЗВАНИЕ_ПОДСТРАНИЦЫ":"subpagename","SUBPAGENAME":"subpagename","НАЗВАНИЕ_ПОДСТРАНИЦЫ_2":"subpagenamee","SUBPAGENAMEE":"subpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ":"talkpagename","TALKPAGENAME":"talkpagename","НАЗВАНИЕ_СТРАНИЦЫ_ОБСУЖДЕНИЯ_2":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","НАЗВАНИЕ_СТРАНИЦЫ_СТАТЬИ_2":"subjectpagenamee","SUBJECTPAGENAMEE": +"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","ИД_ВЕРСИИ":"revisionid","REVISIONID":"revisionid","ДЕНЬ_ВЕРСИИ":"revisionday","REVISIONDAY":"revisionday","ДЕНЬ_ВЕРСИИ_2":"revisionday2","REVISIONDAY2":"revisionday2","МЕСЯЦ_ВЕРСИИ":"revisionmonth","REVISIONMONTH":"revisionmonth","МЕСЯЦ_ВЕРСИИ_1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","ГОД_ВЕРСИИ":"revisionyear","REVISIONYEAR":"revisionyear","ОТМЕТКА_ВРЕМЕНИ_ВЕРСИИ":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","ВЕРСИЯ_УЧАСТНИКА":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","ПРОСТРАНСТВО_ИМЁН":"namespace","NAMESPACE":"namespace","ПОКАЗАТЬ_ЗАГОЛОВОК":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","ТЕКУЩИЙ_МЕСЯЦ":"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2": +"currentmonth","ТЕКУЩИЙ_МЕСЯЦ_1":"currentmonth1","CURRENTMONTH1":"currentmonth1","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_РОД":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","НАЗВАНИЕ_ТЕКУЩЕГО_МЕСЯЦА_АБР":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","ТЕКУЩИЙ_ДЕНЬ":"currentday","CURRENTDAY":"currentday","ТЕКУЩИЙ_ДЕНЬ_2":"currentday2","CURRENTDAY2":"currentday2","НАЗВАНИЕ_ТЕКУЩЕГО_ДНЯ":"currentdayname","CURRENTDAYNAME":"currentdayname","ТЕКУЩИЙ_ГОД":"currentyear","CURRENTYEAR":"currentyear","ТЕКУЩЕЕ_ВРЕМЯ":"currenttime","CURRENTTIME":"currenttime","ТЕКУЩИЙ_ЧАС":"currenthour","CURRENTHOUR":"currenthour","МЕСТНЫЙ_МЕСЯЦ":"localmonth","МЕСТНЫЙ_МЕСЯЦ_2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2": +"localmonth","МЕСТНЫЙ_МЕСЯЦ_1":"localmonth1","LOCALMONTH1":"localmonth1","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА":"localmonthname","LOCALMONTHNAME":"localmonthname","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_РОД":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","НАЗВАНИЕ_МЕСТНОГО_МЕСЯЦА_АБР":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","МЕСТНЫЙ_ДЕНЬ":"localday","LOCALDAY":"localday","МЕСТНЫЙ_ДЕНЬ_2":"localday2","LOCALDAY2":"localday2","НАЗВАНИЕ_МЕСТНОГО_ДНЯ":"localdayname","LOCALDAYNAME":"localdayname","МЕСТНЫЙ_ГОД":"localyear","LOCALYEAR":"localyear","МЕСТНОЕ_ВРЕМЯ":"localtime","LOCALTIME":"localtime","МЕСТНЫЙ_ЧАС":"localhour","LOCALHOUR":"localhour","НАЗВАНИЕ_САЙТА":"sitename","SITENAME":"sitename","ТЕКУЩАЯ_НЕДЕЛЯ":"currentweek","CURRENTWEEK":"currentweek","ТЕКУЩИЙ_ДЕНЬ_НЕДЕЛИ":"currentdow", +"CURRENTDOW":"currentdow","МЕСТНАЯ_НЕДЕЛЯ":"localweek","LOCALWEEK":"localweek","МЕСТНЫЙ_ДЕНЬ_НЕДЕЛИ":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","ТЕКУЩАЯ_ВЕРСИЯ":"currentversion","CURRENTVERSION":"currentversion","ОТМЕТКА_ТЕКУЩЕГО_ВРЕМЕНИ":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","ОТМЕТКА_МЕСТНОГО_ВРЕМЕНИ":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","НАПРАВЛЕНИЕ_ПИСЬМА":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","ЯЗЫК_СОДЕРЖАНИЯ":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zабвгдеёжзийклмнопрстуфхцчшщъыьэюя]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-xh.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-xh.json new file mode 100644 index 0000000..8378010 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-xh.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related": +"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBEROFPAGES":"numberofpages","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort", +"DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday", +"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","DISPLAYTITLE":"displaytitle","!":"!","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME": +"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-xmf.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-xmf.json new file mode 100644 index 0000000..1ebb956 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-xmf.json @@ -0,0 +1,8 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__უგალერეო__":"nogallery","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{ +"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#სპეციალური":"special","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate": +"formatdate","#dateformat":"formatdate","#მიზანი":"target","#სამიზნე":"target","#target":"target","#ბაბილონი":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#დრო":"time","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#კატეგორიის_ხე":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{ +"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","გვერდის_სახელი":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","გვერდის_სრული_სახელი":"fullpagename","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday" +,"REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","სახელთა_სივრცე":"namespace","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","მიმდინარე_თვე":"currentmonth","მიმდინარე_თვე2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2": +"currentmonth","მიმდინარე_თვე1":"currentmonth1","CURRENTMONTH1":"currentmonth1","მიმდინარე_თვის_სახელი":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","მიმდინარე_თვის_სახელის_აბრევიატურა":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","მიმდინარე_დღე":"currentday","CURRENTDAY":"currentday","მიმდინარე_დღე2":"currentday2","CURRENTDAY2":"currentday2","მიმდინარე_დღის_სახელი":"currentdayname","CURRENTDAYNAME":"currentdayname","მიმდინარე_წელი":"currentyear","CURRENTYEAR":"currentyear","მიმდინარე_დრო":"currenttime","CURRENTTIME":"currenttime","მიმდინარე_საათი":"currenthour","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2": +"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","საიტის_სახელი":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zაბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰ“»]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-yi.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-yi.json new file mode 100644 index 0000000..8f18ad0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-yi.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__קיין_אינהאלט_טאבעלע__":"notoc","__ללא_תוכן_עניינים__":"notoc","__ללא_תוכן__":"notoc","__notoc__":"notoc","__קיין_גאלעריע__":"nogallery","__ללא_גלריה__":"nogallery","__nogallery__":"nogallery","__חייב_תוכן_עניינים__":"forcetoc","__חייב_תוכן__": +"forcetoc","__forcetoc__":"forcetoc","__אינהאלט__":"toc","__תוכן_עניינים__":"toc","__תוכן__":"toc","__toc__":"toc","__נישט_רעדאקטירן__":"noeditsection","__ללא_עריכה__":"noeditsection","__noeditsection__":"noeditsection","__ללא_המרת_כותרת__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__ללא_המרת_תוכן__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__יצירת_הערה__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__ללא_יצירת_הערה__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__באהאלטענע_קאטעגאריע__":"hiddencat","__באהאלטענע_קאט__":"hiddencat","__קטגוריה_מוסתרת__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__לחיפוש__":"index","__INDEX__":"index","__לא_לחיפוש__":"noindex","__NOINDEX__": +"noindex","__הפניה_קבועה__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__פירושונים__":"disambiguation","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"מרחב שם":"ns","ns":"ns","מרחב שם מקודד":"nse","nse":"nse","נתיב מקודד":"urlencode","urlencode":"urlencode","אות ראשונה קטנה":"lcfirst","lcfirst":"lcfirst","אות ראשונה גדולה":"ucfirst","ucfirst":"ucfirst","אותיות קטנות":"lc","lc":"lc","אותיות גדולות":"uc","uc":"uc","כתובת יחסית":"localurl","localurl":"localurl","כתובת יחסית מקודד":"localurle","localurle":"localurle","פֿולער_נאמען":"fullurl","כתובת מלאה":"fullurl","fullurl":"fullurl","כתובת מלאה מקודד":"fullurle","fullurle":"fullurle","כתובת קנונית":"canonicalurl","canonicalurl":"canonicalurl","כתובת קנונית מקודד":"canonicalurle","canonicalurle":"canonicalurle", +"עיצוב מספר":"formatnum","formatnum":"formatnum","גראמאטיק":"grammar","דקדוק":"grammar","grammar":"grammar","מגדר":"gender","gender":"gender","מערצאל":"plural","רבים":"plural","plural":"plural","bidi":"bidi","#שפראך":"language","#שפה":"language","#language":"language","ריפוד משמאל":"padleft","padleft":"padleft","ריפוד מימין":"padright","padright":"padright","עוגן מקודד":"anchorencode","anchorencode":"anchorencode","טעקעשטעג":"filepath","נתיב לקובץ":"filepath","filepath":"filepath","pageid":"pageid","הודעה":"int","int":"int","#באזונדער":"special","#מיוחד":"special","#special":"special","#speciale":"speciale","#טאג":"tag","#תג":"tag","#תגית":"tag","#tag":"tag","#עיצוב תאריך":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#קאטעגאריע_בוים":"categorytree","#עץ_קטגוריה":"categorytree","#categorytree":"categorytree","#target" +:"target","#בבל":"babel","#babel":"babel","#coordinates":"coordinates","#invoke":"invoke","#related":"related","#תנאי":"if","#if":"if","#גלייך":"ifeq","#שווה":"ifeq","#ifeq":"ifeq","#קלייב":"switch","#בחר":"switch","#switch":"switch","#עקזיסט":"ifexist","#קיים":"ifexist","#ifexist":"ifexist","#אויברעכן":"ifexpr","#חשב תנאי":"ifexpr","#ifexpr":"ifexpr","#תנאי שגיאה":"iferror","#iferror":"iferror","#צייט":"time","#זמן":"time","#time":"time","#צייטל":"timel","#זמןמ":"timel","#timel":"timel","#רעכן":"expr","#חשב":"expr","#expr":"expr","#יחסי למוחלט":"rel2abs","#rel2abs":"rel2abs","#חלק בכותרת":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#קטע":"lst","#lstx":"lstx","#section-x":"lstx","#בלי קטע":"lstx","#lsth":"lsth","#section-h":"lsth","ללא_קישורי_שפה":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#אייגנשאפט": +"property","#מאפיין":"property","#property":"property","#קביעות":"statements","#statements":"statements","#commaseparatedlist":"commaSeparatedList","נתיב הדפים":"articlepath","articlepath":"articlepath","כתובת השרת":"server","שרת":"server","server":"server","שם השרת":"servername","servername":"servername","נתיב הקבצים":"scriptpath","scriptpath":"scriptpath","נתיב הסגנון":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","שם_המאגר":"wbreponame","wbreponame":"wbreponame"},{"צאל_בלעטער":"numberofpages","מספר דפים כולל":"numberofpages","מספר דפים":"numberofpages","NUMBEROFPAGES":"numberofpages","צאל_באניצער":"numberofusers","מספר משתמשים":"numberofusers","NUMBEROFUSERS":"numberofusers","צאל_טעטיקע_באניצער":"numberofactiveusers","מספר משתמשים פעילים":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers", +"צאל_ארטיקלען":"numberofarticles","מספר ערכים":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","צאל_טעקעס":"numberoffiles","מספר קבצים":"numberoffiles","NUMBEROFFILES":"numberoffiles","מספר מפעילים":"numberofadmins","NUMBEROFADMINS":"numberofadmins","מספר בקבוצה":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","צאל_רעדאקטירונגען":"numberofedits","מספר עריכות":"numberofedits","NUMBEROFEDITS":"numberofedits","גרונטסארטיר":"defaultsort","מיון רגיל":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","דפים בקטגוריה":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","בלאטגרייס":"pagesize","גודל דף":"pagesize","PAGESIZE":"pagesize","רמת הגנה":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY": +"protectionexpiry","מרחב השם מקודד":"namespacee","NAMESPACEE":"namespacee","מספר_מרחב_השם":"namespacenumber","NAMESPACENUMBER":"namespacenumber","מרחב השיחה":"talkspace","TALKSPACE":"talkspace","מרחב השיחה מקודד":"talkspacee","TALKSPACEE":"talkspacee","מרחב הנושא":"subjectspace","מרחב הערכים":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","מרחב הנושא מקודד":"subjectspacee","מרחב הערכים מקודד":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","בלאטנאמען":"pagename","שם הדף":"pagename","PAGENAME":"pagename","שם הדף מקודד":"pagenamee","PAGENAMEE":"pagenamee","פולבלאטנאמען":"fullpagename","שם הדף המלא":"fullpagename","FULLPAGENAME":"fullpagename","שם הדף המלא מקודד":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee", +"שם דף הבסיס":"basepagename","BASEPAGENAME":"basepagename","שם דף הבסיס מקודד":"basepagenamee","BASEPAGENAMEE":"basepagenamee","אונטערבלאטנאמען":"subpagename","שם דף המשנה":"subpagename","SUBPAGENAME":"subpagename","שם דף המשנה מקודד":"subpagenamee","SUBPAGENAMEE":"subpagenamee","רעדנבלאטנאמען":"talkpagename","שם דף השיחה":"talkpagename","TALKPAGENAME":"talkpagename","שם דף השיחה מקודד":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","שם דף הנושא":"subjectpagename","שם הערך":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","שם דף הנושא מקודד":"subjectpagenamee","שם הערך מקודד":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","מזהה גרסה":"revisionid","REVISIONID":"revisionid","יום גרסה":"revisionday","REVISIONDAY":"revisionday","יום גרסה 2": +"revisionday2","REVISIONDAY2":"revisionday2","חודש גרסה":"revisionmonth","REVISIONMONTH":"revisionmonth","חודש גרסה 1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","שנת גרסה":"revisionyear","REVISIONYEAR":"revisionyear","זמן גרסה":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","כותב גרסה":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","נאמענטייל":"namespace","מרחב השם":"namespace","NAMESPACE":"namespace","ווייזן_קעפל":"displaytitle","כותרת תצוגה":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","לויפיקער_מאנאט":"currentmonth","חודש נוכחי":"currentmonth","חודש נוכחי 2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","חודש נוכחי 1":"currentmonth1","CURRENTMONTH1":"currentmonth1","שם חודש נוכחי":"currentmonthname","CURRENTMONTHNAME":"currentmonthname", +"שם חודש נוכחי קניין":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","קיצור חודש נוכחי":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","לויפיקער_טאג":"currentday","יום נוכחי":"currentday","CURRENTDAY":"currentday","יום נוכחי 2":"currentday2","CURRENTDAY2":"currentday2","שם יום נוכחי":"currentdayname","CURRENTDAYNAME":"currentdayname","לויפֿיקע_יאָר":"currentyear","שנה נוכחית":"currentyear","CURRENTYEAR":"currentyear","לויפֿיקע_צײַט":"currenttime","שעה נוכחית":"currenttime","CURRENTTIME":"currenttime","שעות נוכחיות":"currenthour","CURRENTHOUR":"currenthour","חודש מקומי":"localmonth","חודש מקומי 2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","חודש מקומי 1":"localmonth1","LOCALMONTH1":"localmonth1","שם חודש מקומי":"localmonthname","LOCALMONTHNAME":"localmonthname", +"שם חודש מקומי קניין":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","קיצור חודש מקומי":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","יום מקומי":"localday","LOCALDAY":"localday","יום מקומי 2":"localday2","LOCALDAY2":"localday2","שם יום מקומי":"localdayname","LOCALDAYNAME":"localdayname","שנה מקומית":"localyear","LOCALYEAR":"localyear","שעה מקומית":"localtime","LOCALTIME":"localtime","שעות מקומיות":"localhour","LOCALHOUR":"localhour","שם האתר":"sitename","SITENAME":"sitename","שבוע נוכחי":"currentweek","CURRENTWEEK":"currentweek","מספר יום נוכחי":"currentdow","CURRENTDOW":"currentdow","שבוע מקומי":"localweek","LOCALWEEK":"localweek","מספר יום מקומי":"localdow","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","גרסה נוכחית":"currentversion","CURRENTVERSION":"currentversion","זמן נוכחי":"currenttimestamp", +"CURRENTTIMESTAMP":"currenttimestamp","זמן מקומי":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","סימן כיווניות":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","שפת תוכן":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zא-ת]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-yo.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-yo.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-yo.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-yue.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-yue.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-yue.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-za.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-za.json new file mode 100644 index 0000000..ec4e2eb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-za.json @@ -0,0 +1,11 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__无目录__":"notoc","__notoc__":"notoc","__无图库__":"nogallery","__nogallery__":"nogallery","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__目录__":"toc","__toc__":"toc","__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__不转换标题__": +"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__新段落链接__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__无新段落链接__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__索引__":"index","__INDEX__":"index","__无索引__":"noindex","__NOINDEX__":"noindex","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__消除歧义__":"disambiguation","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"名称空间":"ns","ns":"ns","名称空间e":"nse","nse":"nse","url编码":"urlencode","urlencode":"urlencode","小写首字":"lcfirst","lcfirst":"lcfirst","大写首字":"ucfirst","ucfirst":"ucfirst","小写":"lc","lc":"lc","大写":"uc","uc": +"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","完整url":"fullurl","fullurl":"fullurl","完整url等同":"fullurle","fullurle":"fullurle","规范url":"canonicalurl","canonicalurl":"canonicalurl","规范url等同":"canonicalurle","canonicalurle":"canonicalurle","格式化数字":"formatnum","formatnum":"formatnum","语法":"grammar","grammar":"grammar","性别":"gender","gender":"gender","复数":"plural","plural":"plural","bidi":"bidi","#语言":"language","#language":"language","左填充":"padleft","padleft":"padleft","右填充":"padright","padright":"padright","锚编码":"anchorencode","anchorencode":"anchorencode","文件路径":"filepath","filepath":"filepath","页面id":"pageid","pageid":"pageid","界面":"int","int":"int","#特殊":"special","#special":"special","#特殊等同":"speciale","#speciale":"speciale","#标记":"tag","#tag":"tag","#格式化日期":"formatdate","#日期格式化":"formatdate","#formatdate": +"formatdate","#dateformat":"formatdate","#分类树":"categorytree","#categorytree":"categorytree","#目标":"target","#target":"target","#巴别":"babel","#babel":"babel","#coordinates":"coordinates","#调用":"invoke","#invoke":"invoke","#related":"related","#若":"if","#如果":"if","#if":"if","#若相等":"ifeq","#如果相等":"ifeq","#ifeq":"ifeq","#开关":"switch","#转换":"switch","#switch":"switch","#若有":"ifexist","#如有":"ifexist","#ifexist":"ifexist","#若表达式":"ifexpr","#ifexpr":"ifexpr","#如果错误":"iferror","#iferror":"iferror","#时间":"time","#time":"time","#时间l":"timel","#timel":"timel","#表达式":"expr","#expr":"expr","#rel2abs":"rel2abs","#标题组成部分":"titleparts","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","无外部语言连接":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#属性":"property","#property":"property","#statements": +"statements","#commaseparatedlist":"commaSeparatedList","条目路径":"articlepath","articlepath":"articlepath","服务器":"server","server":"server","服务器名":"servername","servername":"servername","脚本路径":"scriptpath","scriptpath":"scriptpath","样式路径":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wb报告名":"wbreponame","wbreponame":"wbreponame"},{"页面数":"numberofpages","NUMBEROFPAGES":"numberofpages","用户数":"numberofusers","NUMBEROFUSERS":"numberofusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","组中用户数":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","编辑数":"numberofedits","NUMBEROFEDITS":"numberofedits","默认排序":"defaultsort", +"默认排序关键字":"defaultsort","默认分类排序":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","分类中页面数":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","页面大小":"pagesize","PAGESIZE":"pagesize","保护级别":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","名字空间等同":"namespacee","NAMESPACEE":"namespacee","名字空间编号":"namespacenumber","NAMESPACENUMBER":"namespacenumber","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","讨论空间等同":"talkspacee","讨论名字空间等同":"talkspacee","TALKSPACEE":"talkspacee","主名字空间":"subjectspace","条目名字空间":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主名字空间等同":"subjectspacee","条目名字空间等同":"subjectspacee","SUBJECTSPACEE":"subjectspacee", +"ARTICLESPACEE":"subjectspacee","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","页面名等同":"pagenamee","页面名称等同":"pagenamee","PAGENAMEE":"pagenamee","页面全称":"fullpagename","完整页面名称":"fullpagename","FULLPAGENAME":"fullpagename","完整页面名称等同":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","根页面名称":"rootpagename","ROOTPAGENAME":"rootpagename","根页面名称等同":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","基础页面名称":"basepagename","BASEPAGENAME":"basepagename","基础页面名称等同":"basepagenamee","BASEPAGENAMEE":"basepagenamee","子页面名称":"subpagename","SUBPAGENAME":"subpagename","子页面名称等同":"subpagenamee","SUBPAGENAMEE":"subpagenamee","讨论页面名称":"talkpagename","对话页面名称":"talkpagename","TALKPAGENAME":"talkpagename","讨论页面名称等同":"talkpagenamee","对话页面名称等同":"talkpagenamee","TALKPAGENAMEE": +"talkpagenamee","主名字空间页面名称":"subjectpagename","条目页面名称":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主名字空间页面名称等同":"subjectpagenamee","条目页面名称等同":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","修订ID":"revisionid","REVISIONID":"revisionid","修订日":"revisionday","REVISIONDAY":"revisionday","修订日2":"revisionday2","REVISIONDAY2":"revisionday2","修订月":"revisionmonth","REVISIONMONTH":"revisionmonth","修订月1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","修订年":"revisionyear","REVISIONYEAR":"revisionyear","修订时间戳":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","修订用户":"revisionuser","REVISIONUSER":"revisionuser","级联来源":"cascadingsources","CASCADINGSOURCES":"cascadingsources","名字空间":"namespace","NAMESPACE":"namespace","显示标题":"displaytitle","DISPLAYTITLE" +:"displaytitle","!":"!","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","本月1":"currentmonth1","CURRENTMONTH1":"currentmonth1","本月名":"currentmonthname","本月名称":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","本月名属格":"currentmonthnamegen","本月名称属格":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月简称":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","今天2":"currentday2","CURRENTDAY2":"currentday2","星期":"currentdayname","今天名":"currentdayname","今天名称":"currentdayname","CURRENTDAYNAME":"currentdayname","今年":"currentyear","CURRENTYEAR":"currentyear","当前时间":"currenttime","此时":"currenttime","CURRENTTIME":"currenttime","当前小时":"currenthour","CURRENTHOUR":"currenthour","本地月":"localmonth","本地月2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2": +"localmonth","本地月1":"localmonth1","LOCALMONTH1":"localmonth1","本地月份名":"localmonthname","LOCALMONTHNAME":"localmonthname","本地月历":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","本地月缩写":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","本地日":"localday","LOCALDAY":"localday","本地日2":"localday2","LOCALDAY2":"localday2","本地日名":"localdayname","LOCALDAYNAME":"localdayname","本地年":"localyear","LOCALYEAR":"localyear","本地时间":"localtime","LOCALTIME":"localtime","本地小时":"localhour","LOCALHOUR":"localhour","站点名称":"sitename","SITENAME":"sitename","本周":"currentweek","CURRENTWEEK":"currentweek","当前DOW":"currentdow","CURRENTDOW":"currentdow","本地周":"localweek","LOCALWEEK":"localweek","本地DOW":"localdow","LOCALDOW":"localdow","修订大小":"revisionsize","REVISIONSIZE":"revisionsize","当前版本":"currentversion","CURRENTVERSION":"currentversion","当前时间戳":"currenttimestamp", +"CURRENTTIMESTAMP":"currenttimestamp","本地时间戳":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","方向标记":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^()(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zea.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zea.json new file mode 100644 index 0000000..bf511b6 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zea.json @@ -0,0 +1,12 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__geeninhoud__":"notoc","__notoc__":"notoc","__geen_galerij__":"nogallery","__nogallery__":"nogallery","__inhoud_dwingen__":"forcetoc","__forceerinhoud__":"forcetoc","__forcetoc__":"forcetoc","__inhoud__":"toc","__toc__":"toc","__nietbewerkbaresectie__":"noeditsection","__noeditsection__":"noeditsection", +"__geenpaginanaamconversie__":"notitleconvert","__geentitelconversie__":"notitleconvert","__geentc__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__geeninhoudconversie__":"nocontentconvert","__geenic__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NIEUWESECTIELINK__":"newsectionlink","__NIEUWESECTIEKOPPELING__":"newsectionlink","__NEWSECTIONLINK__":"newsectionlink","__GEENNIEUWKOPJEKOPPELING__":"nonewsectionlink","__GEENNIEUWESECTIELINK__":"nonewsectionlink","__GEENNIEUWKOPJEVERWIJZING__":"nonewsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__VERBORGENCAT__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__GEENINDEX__":"noindex","__NOINDEX__":"noindex","__STATISCHEDOORVERWIJZING__":"staticredirect","__STATISCHEREDIRECT__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__": +"disambiguation"}],"functionSynonyms":[{"nr":"ns","ns":"ns","nre":"nse","nse":"nse","urlcoderen":"urlencode","codeerurl":"urlencode","urlencode":"urlencode","kleerste":"lcfirst","lcfirst":"lcfirst","gleerste":"ucfirst","hleerste":"ucfirst","ucfirst":"ucfirst","kl":"lc","lc":"lc","hl":"uc","uc":"uc","lokaleurl":"localurl","localurl":"localurl","lokaleurle":"localurle","localurle":"localurle","volledigeurl":"fullurl","fullurl":"fullurl","volledigeurle":"fullurle","fullurle":"fullurle","canoiekeurl":"canonicalurl","canonicalurl":"canonicalurl","canoniekeurle":"canonicalurle","canonicalurle":"canonicalurle","formatteernum":"formatnum","numformatteren":"formatnum","formatnum":"formatnum","grammatica":"grammar","grammar":"grammar","geslacht":"gender","gender":"gender","meervoud":"plural","plural":"plural","bidi":"bidi","#taal":"language","#language":"language","linksopvullen":"padleft","padleft":"padleft","rechtsopvullen":"padright","padright":"padright","ankercoderen":"anchorencode", +"codeeranker":"anchorencode","anchorencode":"anchorencode","bestandspad":"filepath","filepath":"filepath","paginaid":"pageid","pageid":"pageid","int":"int","#speciaal":"special","#special":"special","#speciaale":"speciale","#speciale":"speciale","#label":"tag","#tag":"tag","#datumopmaak":"formatdate","#formatdate":"formatdate","#dateformat":"formatdate","#categorieboom":"categorytree","#categorytree":"categorytree","#target":"target","#babel":"babel","#coordinates":"coordinates","#aanroepen":"invoke","#invoke":"invoke","#related":"related","#als":"if","#if":"if","#alsgelijk":"ifeq","#ifeq":"ifeq","#schakelen":"switch","#switch":"switch","#alsbestaat":"ifexist","#ifexist":"ifexist","#alsexpressie":"ifexpr","#ifexpr":"ifexpr","#alsfout":"iferror","#iferror":"iferror","#tijd":"time","#time":"time","#tijdl":"timel","#timel":"timel","#expressie":"expr","#expr":"expr","#relatiefnaarabsoluut":"rel2abs","#rel2abs":"rel2abs","#paginanaamdelen":"titleparts","#titleparts":"titleparts","#lst": +"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","geenexternetaalkoppelingen":"noexternallanglinks","geenexternetaalverwijzingen":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#eigenschap":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","artikelpad":"articlepath","articlepath":"articlepath","server":"server","servernaam":"servername","servername":"servername","scriptpad":"scriptpath","scriptpath":"scriptpath","stijlpad":"stylepath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"AANTALPAGINAS":"numberofpages","AANTALPAGINA'S":"numberofpages","AANTALPAGINA’S":"numberofpages","NUMBEROFPAGES":"numberofpages","AANTALGEBRUIKERS":"numberofusers","NUMBEROFUSERS":"numberofusers","AANTALACTIEVEGEBRUIKERS":"numberofactiveusers","ACTIEVEGEBRUIKERS":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","AANTALARTIKELEN": +"numberofarticles","NUMBEROFARTICLES":"numberofarticles","AANTALBESTANDEN":"numberoffiles","NUMBEROFFILES":"numberoffiles","AANTALBEHEERDERS":"numberofadmins","AANTALADMINS":"numberofadmins","NUMBEROFADMINS":"numberofadmins","AANTALINGROEP":"numberingroup","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","AANTALBEWERKINGEN":"numberofedits","NUMBEROFEDITS":"numberofedits","STANDAARDSORTERING":"defaultsort","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGINASINCATEGORIE":"pagesincategory","PAGINASINCAT":"pagesincategory","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGINAGROOTTE":"pagesize","PAGESIZE":"pagesize","BEVEILIGINGSNIVEAU":"protectionlevel","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAAMRUIMTEE":"namespacee","NAMESPACEE":"namespacee","NAAMRUIMTENUMMER":"namespacenumber","NAMESPACENUMBER":"namespacenumber","OVERLEGRUIMTE":"talkspace","TALKSPACE":"talkspace", +"OVERLEGRUIMTEE":"talkspacee","TALKSPACEE":"talkspacee","ONDERWERPRUIMTE":"subjectspace","ARTIKELRUIMTE":"subjectspace","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","ONDERWERPRUIMTEE":"subjectspacee","ARTIKELRUIMTEE":"subjectspacee","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","PAGINANAAM":"pagename","PAGENAME":"pagename","PAGINANAAME":"pagenamee","PAGENAMEE":"pagenamee","VOLLEDIGEPAGINANAAM":"fullpagename","FULLPAGENAME":"fullpagename","VOLLEDIGEPAGINANAAME":"fullpagenamee","FULLPAGENAMEE":"fullpagenamee","ROOTPAGINANAAM":"rootpagename","ROOTPAGENAME":"rootpagename","ROOTPAGINANAAME":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","BASISPAGINANAAM":"basepagename","BASEPAGENAME":"basepagename","BASISPAGINANAAME":"basepagenamee","BASEPAGENAMEE":"basepagenamee","DEELPAGINANAAM":"subpagename","SUBPAGENAME":"subpagename","DEELPAGINANAAME":"subpagenamee","SUBPAGENAMEE":"subpagenamee","OVERLEGPAGINANAAM":"talkpagename","TALKPAGENAME":"talkpagename", +"OVERLEGPAGINANAAME":"talkpagenamee","TALKPAGENAMEE":"talkpagenamee","ONDERWERPPAGINANAAM":"subjectpagename","ARTIKELPAGINANAAM":"subjectpagename","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","ONDERWERPPAGINANAAME":"subjectpagenamee","ARTIKELPAGINANAAME":"subjectpagenamee","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","VERSIEID":"revisionid","REVISIONID":"revisionid","VERSIEDAG":"revisionday","REVISIONDAY":"revisionday","VERSIEDAG2":"revisionday2","REVISIONDAY2":"revisionday2","VERSIEMAAND":"revisionmonth","REVISIONMONTH":"revisionmonth","VERSIEMAAND1":"revisionmonth1","REVISIONMONTH1":"revisionmonth1","VERSIEJAAR":"revisionyear","REVISIONYEAR":"revisionyear","VERSIETIJD":"revisiontimestamp","REVISIONTIMESTAMP":"revisiontimestamp","VERSIEGEBRUIKER":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAAMRUIMTE":"namespace","NAMESPACE":"namespace","WEERGEGEVENTITEL":"displaytitle","TOONTITEL": +"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","HUIDIGEMAAND":"currentmonth","HUIDIGEMAAND2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","HUIDIGEMAAND1":"currentmonth1","CURRENTMONTH1":"currentmonth1","HUIDIGEMAANDNAAM":"currentmonthname","CURRENTMONTHNAME":"currentmonthname","HUIDIGEMAANDGEN":"currentmonthnamegen","CURRENTMONTHNAMEGEN":"currentmonthnamegen","HUIDIGEMAANDAFK":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","HUIDIGEDAG":"currentday","CURRENTDAY":"currentday","HUIDIGEDAG2":"currentday2","CURRENTDAY2":"currentday2","HUIDIGEDAGNAAM":"currentdayname","CURRENTDAYNAME":"currentdayname","HUIDIGJAAR":"currentyear","CURRENTYEAR":"currentyear","HUIDIGETIJD":"currenttime","CURRENTTIME":"currenttime","HUIDIGUUR":"currenthour","CURRENTHOUR":"currenthour","PLAATSELIJKEMAAND":"localmonth","LOKALEMAAND":"localmonth","LOKALEMAAND2":"localmonth","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOKALEMAAND1":"localmonth1", +"LOCALMONTH1":"localmonth1","PLAATSELIJKEMAANDNAAM":"localmonthname","LOKALEMAANDNAAM":"localmonthname","LOCALMONTHNAME":"localmonthname","PLAATSELIJKEMAANDNAAMGEN":"localmonthnamegen","LOKALEMAANDNAAMGEN":"localmonthnamegen","LOCALMONTHNAMEGEN":"localmonthnamegen","PLAATSELIJKEMAANDAFK":"localmonthabbrev","LOKALEMAANDAFK":"localmonthabbrev","LOCALMONTHABBREV":"localmonthabbrev","PLAATSELIJKEDAG":"localday","LOKALEDAG":"localday","LOCALDAY":"localday","PLAATSELIJKEDAG2":"localday2","LOKALEDAG2":"localday2","LOCALDAY2":"localday2","PLAATSELIJKEDAGNAAM":"localdayname","LOKALEDAGNAAM":"localdayname","LOCALDAYNAME":"localdayname","PLAATSELIJKJAAR":"localyear","LOKAALJAAR":"localyear","LOCALYEAR":"localyear","PLAATSELIJKETIJD":"localtime","LOKALETIJD":"localtime","LOCALTIME":"localtime","PLAATSELIJKUUR":"localhour","LOKAALUUR":"localhour","LOCALHOUR":"localhour","SITENAAM":"sitename","SITENAME":"sitename","HUIDIGEWEEK":"currentweek","CURRENTWEEK":"currentweek","HUIDIGEDVDW":"currentdow", +"CURRENTDOW":"currentdow","PLAATSELIJKEWEEK":"localweek","LOKALEWEEK":"localweek","LOCALWEEK":"localweek","PLAATSELIJKEDVDW":"localdow","LOKALEDVDW":"localdow","LOCALDOW":"localdow","VERSIEGROOTTE":"revisionsize","REVISIONSIZE":"revisionsize","HUIDIGEVERSIE":"currentversion","CURRENTVERSION":"currentversion","HUIDIGETIJDSTEMPEL":"currenttimestamp","CURRENTTIMESTAMP":"currenttimestamp","PLAATSELIJKETIJDSTEMPEL":"localtimestamp","LOKALETIJDSTEMPEL":"localtimestamp","LOCALTIMESTAMP":"localtimestamp","RICHTINGMARKERING":"directionmark","RICHTINGSMARKERING":"directionmark","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","INHOUDSTAAL":"contentlanguage","INHOUDTAAL":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols": +"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-zäöüïëéèà]+)(.*)$/sDu"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh-classical.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh-classical.json new file mode 100644 index 0000000..7363fdb --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh-classical.json @@ -0,0 +1,9 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__無目錄__":"notoc","__无目录__":"notoc","__notoc__":"notoc","__無圖庫__":"nogallery","__无图库__":"nogallery","__nogallery__":"nogallery","__強制目錄__":"forcetoc","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__目錄__":"toc","__目录__":"toc","__toc__":"toc","__無段落編輯__":"noeditsection", +"__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__不轉換標題__":"notitleconvert","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換內容__":"nocontentconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__隱藏分類__":"hiddencat","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__靜態重新導向__":"staticredirect","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"命名空間":"ns","名字空间":"ns","ns":"ns","命名空間e":"nse","名字空间e":"nse","nse":"nse","urlencode": +"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","性別":"gender","性":"gender","性别":"gender","gender":"gender","plural":"plural","bidi":"bidi","#語言":"language","#语言":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","頁面id":"pageid","页面id":"pageid","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#分類樹":"categorytree","#categorytree":"categorytree","#目標":"target","#target":"target","#巴別":"babel","#babel":"babel","#coordinates":"coordinates","#調動":"invoke","#invoke":"invoke","#related":"related","#若":"if","#if":"if", +"#ifeq":"ifeq","#轉換":"switch","#switch":"switch","#ifexist":"ifexist","#若表達式":"ifexpr","#ifexpr":"ifexpr","#如果錯誤":"iferror","#iferror":"iferror","#時間":"time","#time":"time","#時間l":"timel","#timel":"timel","#表達式":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","隱藏跨語言連結":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#屬性":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","伺服器":"server","服务器":"server","server":"server","伺服器名稱":"servername","服务器名":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wb報表名稱":"wbreponame","wbreponame":"wbreponame"},{"頁面數":"numberofpages","页面数":"numberofpages", +"NUMBEROFPAGES":"numberofpages","使用者人數量":"numberofusers","用户数":"numberofusers","NUMBEROFUSERS":"numberofusers","活躍使用者人數":"numberofactiveusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","文章數":"numberofarticles","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","檔案數":"numberoffiles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles","管理員數":"numberofadmins","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","命名空間數":"namespacenumber","名字空间编号":"namespacenumber", +"NAMESPACENUMBER":"namespacenumber","對話空間":"talkspace","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","頁面名稱":"pagename","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","根頁面名稱":"rootpagename","ROOTPAGENAME":"rootpagename","根頁面名稱E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY": +"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","修訂使用者":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","命名空間":"namespace","名字空间":"namespace","NAMESPACE":"namespace","顯示標題":"displaytitle","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月縮寫":"currentmonthabbrev","本月简称":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","目前時間":"currenttime","当前时间":"currenttime","此时": +"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","網站名稱":"sitename","站点名称":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","目前版本":"currentversion","当前版本":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","內容語言":"contentlanguage","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE": +"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh-min-nan.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh-min-nan.json new file mode 100644 index 0000000..45f25bf --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh-min-nan.json @@ -0,0 +1,9 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"charinsert":true,"inputbox":true,"imagemap":true,"source":true,"syntaxhighlight":true,"poem":true,"categorytree":true,"score":true,"templatestyles":true,"templatedata":true,"math":true,"ce":true,"chem":true,"graph":true,"maplink":true,"mapframe":true,"ref":true,"references":true,"section":true},"doubleUnderscore":[{"__無目錄__":"notoc","__无目录__":"notoc","__notoc__":"notoc","__無圖庫__":"nogallery","__无图库__":"nogallery","__nogallery__":"nogallery","__強制目錄__":"forcetoc","__强显目录__":"forcetoc","__forcetoc__":"forcetoc","__目錄__":"toc","__目录__":"toc","__toc__":"toc","__無段落編輯__": +"noeditsection","__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__noeditsection__":"noeditsection","__不轉換標題__":"notitleconvert","__不转换标题__":"notitleconvert","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不轉換內容__":"nocontentconvert","__不转换内容__":"nocontentconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__隱藏分類__":"hiddencat","__隐藏分类__":"hiddencat","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__靜態重新導向__":"staticredirect","__静态重定向__":"staticredirect","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation"}],"functionSynonyms":[{"命名空間":"ns","名字空间":"ns","ns":"ns","命名空間e":"nse","名字空间e":"nse","nse":"nse", +"urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","本地url":"localurl","localurl":"localurl","本地urle":"localurle","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","性別":"gender","性":"gender","性别":"gender","gender":"gender","plural":"plural","bidi":"bidi","#語言":"language","#语言":"language","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","頁面id":"pageid","页面id":"pageid","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#分類樹":"categorytree","#categorytree":"categorytree","#目標":"target","#target":"target","#巴別":"babel","#babel":"babel","#coordinates":"coordinates","#調動":"invoke","#invoke":"invoke","#related":"related","#若":"if", +"#if":"if","#ifeq":"ifeq","#轉換":"switch","#switch":"switch","#ifexist":"ifexist","#若表達式":"ifexpr","#ifexpr":"ifexpr","#如果錯誤":"iferror","#iferror":"iferror","#時間":"time","#time":"time","#時間l":"timel","#timel":"timel","#表達式":"expr","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","隱藏跨語言連結":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#屬性":"property","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","articlepath":"articlepath","伺服器":"server","服务器":"server","server":"server","伺服器名稱":"servername","服务器名":"servername","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wb報表名稱":"wbreponame","wbreponame":"wbreponame"},{"頁面數":"numberofpages","页面数":"numberofpages", +"NUMBEROFPAGES":"numberofpages","使用者人數量":"numberofusers","用户数":"numberofusers","NUMBEROFUSERS":"numberofusers","活躍使用者人數":"numberofactiveusers","活跃用户数":"numberofactiveusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","文章數":"numberofarticles","条目数":"numberofarticles","NUMBEROFARTICLES":"numberofarticles","檔案數":"numberoffiles","文件数":"numberoffiles","NUMBEROFFILES":"numberoffiles","管理員數":"numberofadmins","管理员数":"numberofadmins","NUMBEROFADMINS":"numberofadmins","NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","NUMBEROFEDITS":"numberofedits","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","NAMESPACEE":"namespacee","命名空間數":"namespacenumber","名字空间编号":"namespacenumber", +"NAMESPACENUMBER":"namespacenumber","對話空間":"talkspace","讨论空间":"talkspace","讨论名字空间":"talkspace","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","頁面名稱":"pagename","页名":"pagename","页面名":"pagename","页面名称":"pagename","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","根頁面名稱":"rootpagename","ROOTPAGENAME":"rootpagename","根頁面名稱E":"rootpagenamee","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY": +"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","修訂使用者":"revisionuser","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","命名空間":"namespace","名字空间":"namespace","NAMESPACE":"namespace","顯示標題":"displaytitle","显示标题":"displaytitle","DISPLAYTITLE":"displaytitle","!":"!","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月縮寫":"currentmonthabbrev","本月简称":"currentmonthabbrev","CURRENTMONTHABBREV":"currentmonthabbrev","今天":"currentday","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","目前時間":"currenttime","当前时间":"currenttime","此时": +"currenttime","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2","LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","網站名稱":"sitename","站点名称":"sitename","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","目前版本":"currentversion","当前版本":"currentversion","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","內容語言":"contentlanguage","内容语言":"contentlanguage","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE": +"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh-yue.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh-yue.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh-yue.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh.json new file mode 100644 index 0000000..113cdfa --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zh.json @@ -0,0 +1,13 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"quiz":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__无目录__":"notoc","__無目錄__":"notoc","__nogallery__":"nogallery","__无图库__":"nogallery","__無圖庫__":"nogallery","__forcetoc__":"forcetoc","__强显目录__":"forcetoc","__強制目錄__":"forcetoc","__toc__":"toc","__目录__":"toc","__目錄__":"toc","__noeditsection__": +"noeditsection","__无编辑段落__":"noeditsection","__无段落编辑__":"noeditsection","__無段落編輯__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__不转换标题__":"notitleconvert","__不轉換標題__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert","__不转换内容__":"nocontentconvert","__不轉換內容__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__新段落链接__":"newsectionlink","__NONEWSECTIONLINK__":"nonewsectionlink","__无新段落链接__":"nonewsectionlink","__HIDDENCAT__":"hiddencat","__隐藏分类__":"hiddencat","__隱藏分類__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__索引__":"index","__NOINDEX__":"noindex","__无索引__":"noindex","__STATICREDIRECT__":"staticredirect","__静态重定向__":"staticredirect","__靜態重新導向__":"staticredirect","__NOGLOBAL__":"noglobal","__消除歧义__": +"disambiguation","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","名称空间":"ns","命名空間":"ns","名字空间":"ns","nse":"nse","名称空间e":"nse","命名空間e":"nse","名字空间e":"nse","urlencode":"urlencode","url编码":"urlencode","lcfirst":"lcfirst","小写首字":"lcfirst","ucfirst":"ucfirst","大写首字":"ucfirst","lc":"lc","小写":"lc","uc":"uc","大写":"uc","localurl":"localurl","本地url":"localurl","localurle":"localurle","本地urle":"localurle","fullurl":"fullurl","完整url":"fullurl","fullurle":"fullurle","完整url等同":"fullurle","canonicalurl":"canonicalurl","规范url":"canonicalurl","canonicalurle":"canonicalurle","规范url等同":"canonicalurle","formatnum":"formatnum","格式化数字":"formatnum","grammar":"grammar","语法":"grammar","gender":"gender","性别":"gender","性別":"gender","性":"gender","plural":"plural","复数":"plural","bidi":"bidi","#language": +"language","#语言":"language","#語言":"language","padleft":"padleft","左填充":"padleft","padright":"padright","右填充":"padright","anchorencode":"anchorencode","锚编码":"anchorencode","filepath":"filepath","文件路径":"filepath","pageid":"pageid","页面id":"pageid","頁面id":"pageid","int":"int","界面":"int","#special":"special","#特殊":"special","#speciale":"speciale","#特殊等同":"speciale","#tag":"tag","#标记":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#格式化日期":"formatdate","#日期格式化":"formatdate","#目标":"target","#目標":"target","#target":"target","#巴别":"babel","#巴別":"babel","#babel":"babel","#coordinates":"coordinates","#调用":"invoke","#調動":"invoke","#invoke":"invoke","#related":"related","#非空式":"if","#若":"if","#如果":"if","#if":"if","#相同式":"ifeq","#匹配式":"ifeq","#若相等":"ifeq","#如果相等":"ifeq","#ifeq":"ifeq","#多选式":"switch","#多条件式":"switch","#双射式": +"switch","#开关":"switch","#转换":"switch","#轉換":"switch","#switch":"switch","#存在式":"ifexist","#若有":"ifexist","#如有":"ifexist","#ifexist":"ifexist","#若表达式":"ifexpr","#若表達式":"ifexpr","#ifexpr":"ifexpr","#错误式":"iferror","#如果错误":"iferror","#如果錯誤":"iferror","#iferror":"iferror","#时间":"time","#時間":"time","#time":"time","#时间l":"timel","#時間l":"timel","#timel":"timel","#计算式":"expr","#表达式":"expr","#表達式":"expr","#expr":"expr","#rel2abs":"rel2abs","#标题组成部分":"titleparts","#titleparts":"titleparts","#分类树":"categorytree","#分類樹":"categorytree","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","无外部语言连接":"noexternallanglinks","隱藏跨語言連結":"noexternallanglinks","noexternallanglinks":"noexternallanglinks","#属性":"property","#屬性":"property","#property":"property","#statements": +"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","条目路径":"articlepath","server":"server","服务器":"server","伺服器":"server","servername":"servername","服务器名":"servername","伺服器名稱":"servername","scriptpath":"scriptpath","脚本路径":"scriptpath","stylepath":"stylepath","样式路径":"stylepath","numberofwikis":"numberofwikis","wb报告名":"wbreponame","wb報表名稱":"wbreponame","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","组中用户数":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","默认排序":"defaultsort","默认排序关键字":"defaultsort","默认分类排序":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","分类中页面数":"pagesincategory","PAGESIZE":"pagesize","页面大小":"pagesize","PROTECTIONLEVEL":"protectionlevel","保护级别": +"protectionlevel","PROTECTIONEXPIRY":"protectionexpiry","PAGENAME":"pagename","页名":"pagename","页面名":"pagename","页面名称":"pagename","頁面名稱":"pagename","PAGENAMEE":"pagenamee","页面名等同":"pagenamee","页面名称等同":"pagenamee","FULLPAGENAME":"fullpagename","页面全称":"fullpagename","完整页面名称":"fullpagename","FULLPAGENAMEE":"fullpagenamee","完整页面名称等同":"fullpagenamee","SUBPAGENAME":"subpagename","子页面名称":"subpagename","SUBPAGENAMEE":"subpagenamee","子页面名称等同":"subpagenamee","ROOTPAGENAME":"rootpagename","根页面名称":"rootpagename","根頁面名稱":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","根页面名称等同":"rootpagenamee","根頁面名稱E":"rootpagenamee","BASEPAGENAME":"basepagename","基础页面名称":"basepagename","BASEPAGENAMEE":"basepagenamee","基础页面名称等同":"basepagenamee","TALKPAGENAME":"talkpagename","讨论页面名称":"talkpagename","对话页面名称": +"talkpagename","TALKPAGENAMEE":"talkpagenamee","讨论页面名称等同":"talkpagenamee","对话页面名称等同":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","主名字空间页面名称":"subjectpagename","条目页面名称":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","主名字空间页面名称等同":"subjectpagenamee","条目页面名称等同":"subjectpagenamee","REVISIONID":"revisionid","修订ID":"revisionid","REVISIONDAY":"revisionday","修订日":"revisionday","REVISIONDAY2":"revisionday2","修订日2":"revisionday2","REVISIONMONTH":"revisionmonth","修订月":"revisionmonth","REVISIONMONTH1":"revisionmonth1","修订月1":"revisionmonth1","REVISIONYEAR":"revisionyear","修订年":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","修订时间戳":"revisiontimestamp","REVISIONUSER":"revisionuser","修订用户":"revisionuser","修訂使用者":"revisionuser", +"CASCADINGSOURCES":"cascadingsources","级联来源":"cascadingsources","NAMESPACE":"namespace","名字空间":"namespace","命名空間":"namespace","NAMESPACEE":"namespacee","名字空间等同":"namespacee","NAMESPACENUMBER":"namespacenumber","名字空间编号":"namespacenumber","命名空間數":"namespacenumber","TALKSPACE":"talkspace","讨论空间":"talkspace","讨论名字空间":"talkspace","對話空間":"talkspace","TALKSPACEE":"talkspacee","讨论空间等同":"talkspacee","讨论名字空间等同":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE":"subjectspace","主名字空间":"subjectspace","条目名字空间":"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","主名字空间等同":"subjectspacee","条目名字空间等同":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","条目数":"numberofarticles","文章數":"numberofarticles","NUMBEROFFILES":"numberoffiles","文件数":"numberoffiles","檔案數":"numberoffiles", +"NUMBEROFUSERS":"numberofusers","用户数":"numberofusers","使用者人數量":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","活跃用户数":"numberofactiveusers","活躍使用者人數":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","页面数":"numberofpages","頁面數":"numberofpages","NUMBEROFADMINS":"numberofadmins","管理员数":"numberofadmins","管理員數":"numberofadmins","NUMBEROFEDITS":"numberofedits","编辑数":"numberofedits","DISPLAYTITLE":"displaytitle","显示标题":"displaytitle","顯示標題":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","本月":"currentmonth","本月2":"currentmonth","CURRENTMONTH1":"currentmonth1","本月1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","本月名":"currentmonthname","本月名称":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","本月名属格":"currentmonthnamegen","本月名称属格":"currentmonthnamegen","CURRENTMONTHABBREV": +"currentmonthabbrev","本月简称":"currentmonthabbrev","本月縮寫":"currentmonthabbrev","CURRENTDAY":"currentday","今天":"currentday","CURRENTDAY2":"currentday2","今天2":"currentday2","CURRENTDAYNAME":"currentdayname","星期":"currentdayname","今天名":"currentdayname","今天名称":"currentdayname","CURRENTYEAR":"currentyear","今年":"currentyear","CURRENTTIME":"currenttime","当前时间":"currenttime","此时":"currenttime","目前時間":"currenttime","CURRENTHOUR":"currenthour","当前小时":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","本地月":"localmonth","本地月2":"localmonth","LOCALMONTH1":"localmonth1","本地月1":"localmonth1","LOCALMONTHNAME":"localmonthname","本地月份名":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","本地月历":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","本地月缩写":"localmonthabbrev","LOCALDAY":"localday","本地日":"localday","LOCALDAY2":"localday2","本地日2": +"localday2","LOCALDAYNAME":"localdayname","本地日名":"localdayname","LOCALYEAR":"localyear","本地年":"localyear","LOCALTIME":"localtime","本地时间":"localtime","LOCALHOUR":"localhour","本地小时":"localhour","SITENAME":"sitename","站点名称":"sitename","網站名稱":"sitename","CURRENTWEEK":"currentweek","本周":"currentweek","CURRENTDOW":"currentdow","当前DOW":"currentdow","LOCALWEEK":"localweek","本地周":"localweek","LOCALDOW":"localdow","本地DOW":"localdow","REVISIONSIZE":"revisionsize","修订大小":"revisionsize","CURRENTVERSION":"currentversion","当前版本":"currentversion","目前版本":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","当前时间戳":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","本地时间戳":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","方向标记":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","内容语言":"contentlanguage","內容語言": +"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^()(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zu.json b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zu.json new file mode 100644 index 0000000..2b30d6c --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/config/codemirror-config-zu.json @@ -0,0 +1,6 @@ +{"extCodeMirrorConfig":{"pluginModules":["ext.CodeMirror.addons"],"tagModes":{"ref":"text/mediawiki","pre":"mw-tag-pre","nowiki":"mw-tag-nowiki"},"tags":{"pre":true,"nowiki":true,"gallery":true,"indicator":true,"langconvert":true,"timeline":true,"hiero":true,"source":true,"syntaxhighlight":true,"score":true,"templatestyles":true,"templatedata":true,"graph":true,"charinsert":true,"ref":true,"references":true,"inputbox":true,"imagemap":true,"poem":true,"categorytree":true,"section":true,"math":true,"ce":true,"chem":true,"maplink":true,"mapframe":true},"doubleUnderscore":[{"__notoc__":"notoc","__nogallery__":"nogallery","__forcetoc__":"forcetoc","__toc__":"toc","__noeditsection__":"noeditsection","__notitleconvert__":"notitleconvert","__notc__":"notitleconvert","__nocontentconvert__":"nocontentconvert","__nocc__":"nocontentconvert"},{"__NEWSECTIONLINK__":"newsectionlink","__NONEWSECTIONLINK__": +"nonewsectionlink","__HIDDENCAT__":"hiddencat","__EXPECTUNUSEDCATEGORY__":"expectunusedcategory","__INDEX__":"index","__NOINDEX__":"noindex","__STATICREDIRECT__":"staticredirect","__NOGLOBAL__":"noglobal","__DISAMBIG__":"disambiguation","__EXPECTED_UNCONNECTED_PAGE__":"expectedUnconnectedPage"}],"functionSynonyms":[{"ns":"ns","nse":"nse","urlencode":"urlencode","lcfirst":"lcfirst","ucfirst":"ucfirst","lc":"lc","uc":"uc","localurl":"localurl","localurle":"localurle","fullurl":"fullurl","fullurle":"fullurle","canonicalurl":"canonicalurl","canonicalurle":"canonicalurle","formatnum":"formatnum","grammar":"grammar","gender":"gender","plural":"plural","bidi":"bidi","#language":"language","padleft":"padleft","padright":"padright","anchorencode":"anchorencode","filepath":"filepath","pageid":"pageid","int":"int","#special":"special","#speciale":"speciale","#tag":"tag","#formatdate":"formatdate","#dateformat":"formatdate","#target":"target","#babel":"babel","#coordinates":"coordinates","#invoke" +:"invoke","#related":"related","#if":"if","#ifeq":"ifeq","#switch":"switch","#ifexist":"ifexist","#ifexpr":"ifexpr","#iferror":"iferror","#time":"time","#timel":"timel","#expr":"expr","#rel2abs":"rel2abs","#titleparts":"titleparts","#categorytree":"categorytree","#lst":"lst","#section":"lst","#lstx":"lstx","#section-x":"lstx","#lsth":"lsth","#section-h":"lsth","noexternallanglinks":"noexternallanglinks","#property":"property","#statements":"statements","#commaseparatedlist":"commaSeparatedList","#mentor":"mentor","articlepath":"articlepath","server":"server","servername":"servername","scriptpath":"scriptpath","stylepath":"stylepath","numberofwikis":"numberofwikis","wbreponame":"wbreponame"},{"NUMBERINGROUP":"numberingroup","NUMINGROUP":"numberingroup","DEFAULTSORT":"defaultsort","DEFAULTSORTKEY":"defaultsort","DEFAULTCATEGORYSORT":"defaultsort","PAGESINCATEGORY":"pagesincategory","PAGESINCAT":"pagesincategory","PAGESIZE":"pagesize","PROTECTIONLEVEL":"protectionlevel","PROTECTIONEXPIRY" +:"protectionexpiry","PAGENAME":"pagename","PAGENAMEE":"pagenamee","FULLPAGENAME":"fullpagename","FULLPAGENAMEE":"fullpagenamee","SUBPAGENAME":"subpagename","SUBPAGENAMEE":"subpagenamee","ROOTPAGENAME":"rootpagename","ROOTPAGENAMEE":"rootpagenamee","BASEPAGENAME":"basepagename","BASEPAGENAMEE":"basepagenamee","TALKPAGENAME":"talkpagename","TALKPAGENAMEE":"talkpagenamee","SUBJECTPAGENAME":"subjectpagename","ARTICLEPAGENAME":"subjectpagename","SUBJECTPAGENAMEE":"subjectpagenamee","ARTICLEPAGENAMEE":"subjectpagenamee","REVISIONID":"revisionid","REVISIONDAY":"revisionday","REVISIONDAY2":"revisionday2","REVISIONMONTH":"revisionmonth","REVISIONMONTH1":"revisionmonth1","REVISIONYEAR":"revisionyear","REVISIONTIMESTAMP":"revisiontimestamp","REVISIONUSER":"revisionuser","CASCADINGSOURCES":"cascadingsources","NAMESPACE":"namespace","NAMESPACEE":"namespacee","NAMESPACENUMBER":"namespacenumber","TALKSPACE":"talkspace","TALKSPACEE":"talkspacee","SUBJECTSPACE":"subjectspace","ARTICLESPACE": +"subjectspace","SUBJECTSPACEE":"subjectspacee","ARTICLESPACEE":"subjectspacee","NUMBEROFARTICLES":"numberofarticles","NUMBEROFFILES":"numberoffiles","NUMBEROFUSERS":"numberofusers","NUMBEROFACTIVEUSERS":"numberofactiveusers","NUMBEROFPAGES":"numberofpages","NUMBEROFADMINS":"numberofadmins","NUMBEROFEDITS":"numberofedits","DISPLAYTITLE":"displaytitle","!":"!","=":"=","CURRENTMONTH":"currentmonth","CURRENTMONTH2":"currentmonth","CURRENTMONTH1":"currentmonth1","CURRENTMONTHNAME":"currentmonthname","CURRENTMONTHNAMEGEN":"currentmonthnamegen","CURRENTMONTHABBREV":"currentmonthabbrev","CURRENTDAY":"currentday","CURRENTDAY2":"currentday2","CURRENTDAYNAME":"currentdayname","CURRENTYEAR":"currentyear","CURRENTTIME":"currenttime","CURRENTHOUR":"currenthour","LOCALMONTH":"localmonth","LOCALMONTH2":"localmonth","LOCALMONTH1":"localmonth1","LOCALMONTHNAME":"localmonthname","LOCALMONTHNAMEGEN":"localmonthnamegen","LOCALMONTHABBREV":"localmonthabbrev","LOCALDAY":"localday","LOCALDAY2":"localday2", +"LOCALDAYNAME":"localdayname","LOCALYEAR":"localyear","LOCALTIME":"localtime","LOCALHOUR":"localhour","SITENAME":"sitename","CURRENTWEEK":"currentweek","CURRENTDOW":"currentdow","LOCALWEEK":"localweek","LOCALDOW":"localdow","REVISIONSIZE":"revisionsize","CURRENTVERSION":"currentversion","CURRENTTIMESTAMP":"currenttimestamp","LOCALTIMESTAMP":"localtimestamp","DIRECTIONMARK":"directionmark","DIRMARK":"directionmark","CONTENTLANGUAGE":"contentlanguage","CONTENTLANG":"contentlanguage","PAGELANGUAGE":"pagelanguage"}],"urlProtocols":"bitcoin\\:|ftp\\:\\/\\/|ftps\\:\\/\\/|geo\\:|git\\:\\/\\/|gopher\\:\\/\\/|http\\:\\/\\/|https\\:\\/\\/|irc\\:\\/\\/|ircs\\:\\/\\/|magnet\\:|mailto\\:|mms\\:\\/\\/|news\\:|nntp\\:\\/\\/|redis\\:\\/\\/|sftp\\:\\/\\/|sip\\:|sips\\:|sms\\:|ssh\\:\\/\\/|svn\\:\\/\\/|tel\\:|telnet\\:\\/\\/|urn\\:|worldwind\\:\\/\\/|xmpp\\:|\\/\\/","linkTrailCharacters":"/^([a-z]+)(.*)$/sD"}} \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/addon/search/match-highlighter.js b/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/addon/search/match-highlighter.js new file mode 100644 index 0000000..fe1d465 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/addon/search/match-highlighter.js @@ -0,0 +1,165 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// Highlighting text that matches the selection +// +// Defines an option highlightSelectionMatches, which, when enabled, +// will style strings that match the selection throughout the +// document. +// +// The option can be set to true to simply enable it, or to a +// {minChars, style, wordsOnly, showToken, delay} object to explicitly +// configure it. minChars is the minimum amount of characters that should be +// selected for the behavior to occur, and style is the token style to +// apply to the matches. This will be prefixed by "cm-" to create an +// actual CSS class name. If wordsOnly is enabled, the matches will be +// highlighted only if the selected text is a word. showToken, when enabled, +// will cause the current token to be highlighted when nothing is selected. +// delay is used to specify how much time to wait, in milliseconds, before +// highlighting the matches. If annotateScrollbar is enabled, the occurences +// will be highlighted on the scrollbar via the matchesonscrollbar addon. + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); + })(function(CodeMirror) { + "use strict"; + + var defaults = { + style: "matchhighlight", + minChars: 2, + delay: 100, + wordsOnly: false, + annotateScrollbar: false, + showToken: false, + trim: true + } + + function State(options) { + this.options = {} + for (var name in defaults) + this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name] + this.overlay = this.timeout = null; + this.matchesonscroll = null; + this.active = false; + } + + CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + removeOverlay(cm); + clearTimeout(cm.state.matchHighlighter.timeout); + cm.state.matchHighlighter = null; + cm.off("cursorActivity", cursorActivity); + cm.off("focus", onFocus) + } + if (val) { + var state = cm.state.matchHighlighter = new State(val); + if (cm.hasFocus()) { + state.active = true + highlightMatches(cm) + } else { + cm.on("focus", onFocus) + } + cm.on("cursorActivity", cursorActivity); + } + }); + + function cursorActivity(cm) { + var state = cm.state.matchHighlighter; + if (state.active || cm.hasFocus()) scheduleHighlight(cm, state) + } + + function onFocus(cm) { + var state = cm.state.matchHighlighter + if (!state.active) { + state.active = true + scheduleHighlight(cm, state) + } + } + + function scheduleHighlight(cm, state) { + clearTimeout(state.timeout); + state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay); + } + + function addOverlay(cm, query, hasBoundary, style) { + var state = cm.state.matchHighlighter; + cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style)); + if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) { + var searchFor = hasBoundary ? new RegExp("\\b" + query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") + "\\b") : query; + state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false, + {className: "CodeMirror-selection-highlight-scrollbar"}); + } + } + + function removeOverlay(cm) { + var state = cm.state.matchHighlighter; + if (state.overlay) { + cm.removeOverlay(state.overlay); + state.overlay = null; + if (state.matchesonscroll) { + state.matchesonscroll.clear(); + state.matchesonscroll = null; + } + } + } + + function highlightMatches(cm) { + cm.operation(function() { + var state = cm.state.matchHighlighter; + removeOverlay(cm); + if (!cm.somethingSelected() && state.options.showToken) { + var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken; + var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start; + while (start && re.test(line.charAt(start - 1))) --start; + while (end < line.length && re.test(line.charAt(end))) ++end; + if (start < end) + addOverlay(cm, line.slice(start, end), re, state.options.style); + return; + } + var from = cm.getCursor("from"), to = cm.getCursor("to"); + if (from.line != to.line) return; + if (state.options.wordsOnly && !isWord(cm, from, to)) return; + var selection = cm.getRange(from, to) + if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "") + if (selection.length >= state.options.minChars) + addOverlay(cm, selection, false, state.options.style); + }); + } + + function isWord(cm, from, to) { + var str = cm.getRange(from, to); + if (str.match(/^\w+$/) !== null) { + if (from.ch > 0) { + var pos = {line: from.line, ch: from.ch - 1}; + var chr = cm.getRange(pos, from); + if (chr.match(/\W/) === null) return false; + } + if (to.ch < cm.getLine(from.line).length) { + var pos = {line: to.line, ch: to.ch + 1}; + var chr = cm.getRange(to, pos); + if (chr.match(/\W/) === null) return false; + } + return true; + } else return false; + } + + function boundariesAround(stream, re) { + return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) && + (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos))); + } + + function makeOverlay(query, hasBoundary, style) { + return {token: function(stream) { + if (stream.match(query) && + (!hasBoundary || boundariesAround(stream, hasBoundary))) + return style; + stream.next(); + stream.skipTo(query.charAt(0)) || stream.skipToEnd(); + }}; + } + }); \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/addon/search/search.js b/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/addon/search/search.js new file mode 100644 index 0000000..d633f90 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/addon/search/search.js @@ -0,0 +1,406 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// Define search commands. Depends on dialog.js or another +// implementation of the openDialog method. + +// Replace works a little oddly -- it will do the replace on the next +// Ctrl-G (or whatever is bound to findNext) press. You prevent a +// replace by making sure the match is no longer selected when hitting +// Ctrl-G. + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("./searchcursor")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "./searchcursor"], mod); + else // Plain browser env + mod(CodeMirror); + })(function(CodeMirror) { + "use strict"; + + function searchOverlay(cm, state, caseInsensitive) { + + var query = state.query; + var loopMatchPositionIndex = 0; + state.initialFocusedMatchIndex = -1; + + if (typeof query == "string") + query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g"); + else if (!query.global) + query = new RegExp(query.source, query.ignoreCase ? "gi" : "g"); + + return {token: function(stream) { + query.lastIndex = stream.pos; + var match = query.exec(stream.string); + if (match && match.index == stream.pos) { + stream.pos += match[0].length || 1; + + if (state.initialFocusedMatchIndex == -1) { + var fromCursor = cm.getCursor('from') + if (stream.lineOracle.line > fromCursor.line || (stream.lineOracle.line == fromCursor.line && stream.start >= fromCursor.ch)) { + state.initialFocusedMatchIndex = loopMatchPositionIndex; + } + loopMatchPositionIndex++; + } + + return "searching"; + } else if (match) { + stream.pos = match.index; + } else { + stream.skipToEnd(); + } + }}; + } + + function SearchState() { + this.posFrom = this.posTo = this.lastQuery = this.query = null; + this.overlay = null; + } + + function getSearchState(cm) { + return cm.state.search || (cm.state.search = new SearchState()); + } + + function queryCaseInsensitive(query) { + //codemirror default is if the query string is all lowercase, do a case insensitive search. + //commenting out here so it's case insensitive whether query is lowercase or not. + return typeof query == "string" // && query == query.toLowerCase(); + } + + function getSearchCursor(cm, query, pos) { + return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true}); + } + + function parseString(string) { + return string.replace(/\\(.)/g, function(_, ch) { + if (ch == "n") return "\n" + if (ch == "r") return "\r" + return ch + }) + } + + function parseQuery(query) { + var isRE = query.match(/^\/(.*)\/([a-z]*)$/); + if (isRE) { + try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); } + catch(e) {} // Not a regular expression after all, do a string search + } else { + query = parseString(query) + } + if (typeof query == "string" ? query == "" : query.test("")) + query = /x^/; + return query; + } + + function startSearch(cm, state, query) { + state.queryText = query; + state.query = parseQuery(query); + cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query)); + state.overlay = searchOverlay(cm, state, queryCaseInsensitive(state.query)); + cm.addOverlay(state.overlay); + if (cm.showMatchesOnScrollbar) { + if (state.annotate) { state.annotate.clear(); state.annotate = null; } + state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query)); + } + } + + function doSearch(cm, rev) { + var state = getSearchState(cm); + if (state.query) return findNext(cm, rev); + var q = cm.getSelection() || state.lastQuery; + if (q instanceof RegExp && q.source == "x^") q = null + const query = cm.state.query; + if (query && !state.query) cm.operation(function () { + startSearch(cm, state, query); + state.posFrom = state.posTo = cm.getCursor(); + findNext(cm, rev); + }); + } + + const ClassNames = { + searching: 'cm-searching', + searchingFocus: 'cm-searching-focus', + searchingReplaced: 'cm-searching-replaced', + searchingFocusIdPrefix: 'cm-searching-focus-id-' + } + + function clearFocusedMatches(cm) { + Array.from(document.getElementsByClassName(ClassNames.searchingFocus)).forEach(element => setFocusOnMatchElement(element, false)) + } + + function markReplacedText(cm, cursor) { + const state = getSearchState(cm); + const marker = cm.markText(cursor.from(), cursor.to(), { className: ClassNames.searchingReplaced }) + if (state.replacedMarkers) { + state.replacedMarkers.push(marker); + } else { + state.replacedMarkers = [marker]; + } + } + + function clearReplaced(state) { + if (state.replacedMarkers) { + state.replacedMarkers.forEach((marker) => { marker.clear() }); + state.replacedMarkers = null; + } + } + + function focusOnMatch(state, focus, forceIncrement) { + const matches = document.getElementsByClassName(ClassNames.searching); + const matchesCount = matches.length; + + var focusedMatchIndex; + //here we're using focus as a flag for whether they came from find next / prev or from replace. + //if they came from find next/previous, we are okay with focusedMatchIndex being -1 because it will get decremented/incremented below + //if they came from replace (where focus is null), we want to reset focusedMatchIndex to 0 but NOT increment + if (state.focusedMatchIndex != undefined && state.focusedMatchIndex != null && ((state.focusedMatchIndex > -1 && !focus) || (state.focusedMatchIndex >= -1 && focus) || (state.focusedMatchIndex == -1 && !focus && forceIncrement))) { + focusedMatchIndex = state.focusedMatchIndex; + } else if (state.initialFocusedMatchIndex != undefined && state.initialFocusedMatchIndex != null && state.initialFocusedMatchIndex > -1) { + focusedMatchIndex = state.initialFocusedMatchIndex; + } else { + focusedMatchIndex = 0; + } + + if (forceIncrement || focus) { + if (forceIncrement || focus.next) { + if (focusedMatchIndex >= matchesCount - 1) { + focusedMatchIndex = 0; + } else { + focusedMatchIndex++; + } + } else if (focus.prev) { + if (focusedMatchIndex > 0) { + focusedMatchIndex--; + } else { + focusedMatchIndex = matchesCount - 1; + } + } + } + + const focusedMatchID = `${ClassNames.searchingFocusIdPrefix}${focusedMatchIndex}`; + const focusedMatch = focusOnMatchAtIndex(matches, focusedMatchIndex, focusedMatchID); + + state.matchesCount = matchesCount; + state.focusedMatch = focusedMatch; + + if (state.focusedMatch) { + state.focusedMatchIndex = focusedMatchIndex; + state.focusedMatchID = focusedMatchID; + } else { + state.focusedMatchIndex = -1; + state.focusedMatchID = null; + } + + const message = { + findInPageMatchesCount: state.matchesCount, + findInPageFocusedMatchIndex: state.focusedMatchIndex, + findInPageFocusedMatchID: state.focusedMatchID + }; + + window.webkit.messageHandlers.codeMirrorSearchMessage.postMessage(message); + state.initialFocusedMatchIndex = -1; + } + + function setFocusOnMatchElement(element, enable, id = null) { + if (enable) { + element.classList.add(ClassNames.searchingFocus) + element.id = id + } else { + element.classList.remove(ClassNames.searchingFocus) + element.removeAttribute('id') + } + } + + function focusOnMatchAtIndex(matches, index, id) { + if (matches.length == 0) return null; + const match = matches[index]; + if (!match) return null; + setFocusOnMatchElement(match, true, id) + return match + } + + function selectLastFocusedMatch(cm) { + if (cm.hasFocus()) return; + if (cm.getSelection() !== "") return focusWithoutScroll(cm); + const scrollTop = document.body.scrollTop; + var state = getSearchState(cm); + var sel = window.getSelection() + if (sel.rangeCount > 0) { + sel.removeAllRanges(); + } + const range = document.createRange(); + range.selectNode(state.focusedMatch) + sel.addRange(range); + document.body.scrollTop = scrollTop; + } + + function findNext(cm, rev) {cm.operation(function() { + var state = getSearchState(cm); + var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo); + if (!cursor.find(rev)) { + cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0)); + state.focusedMatchIndex = -1; + state.initialFocusedMatchIndex = -1; + if (!cursor.find(rev)) return; + } + state.posFrom = cursor.from(); state.posTo = cursor.to(); + });} + + function clearSearch(cm) {cm.operation(function() { + var state = getSearchState(cm); + state.lastQuery = state.query; + if (!state.query) return; + clearFocusedMatches(cm); + clearReplaced(state); + state.focusedMatchIndex = null + state.initialFocusedMatchIndex = null; + state.query = state.queryText = null; + cm.removeOverlay(state.overlay); + if (state.annotate) { state.annotate.clear(); state.annotate = null; } + });} + + function replaceAll(cm, query, text) { + var count = 0; + cm.operation(function() { + for (var cursor = getSearchCursor(cm, query); cursor.findNext();) { + if (typeof query != "string") { + var match = cm.getRange(cursor.from(), cursor.to()).match(query); + cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];})); + } else { + cursor.replace(text); + markReplacedText(cm, cursor); + } + count++; + } + }); + cm.state.replaceAllCount = count; + } + + //same as replace(cm, all) but bypasses CodeMirror dialogs. Split this up into all & single variants for simplicity + function replaceAllWithoutDialogs(cm) { + var replaceText = cm.state.replaceText; + if (cm.getOption("readOnly")) return; + if (!replaceText) return; + + var query = cm.state.query; + query = parseQuery(query); + replaceText = parseString(replaceText); + cm.isReplacing = true; + replaceAll(cm, query, replaceText); + cm.isReplacing = false; + + //resets count to 0/0 + let state = getSearchState(cm); + focusOnMatch(state, null, false); + } + + function replaceSingleWithoutDialogs(cm) { + if (cm.getOption("readOnly")) return; + var replaceText = cm.state.replaceText; + if (!replaceText) return; + + var state = getSearchState(cm); + var query = state.query; + query = parseQuery(query); + replaceText = parseString(replaceText); + var cursor = getSearchCursor(cm, query, state.posFrom); + + var advance = function(shouldReplace) { + var start = cursor.from(), match; + if (!(match = cursor.findNext())) { + cursor = getSearchCursor(cm, query); + state.focusedMatchIndex = 0; + state.focusedMatchIndex = -1; + state.initialFocusedMatchIndex = -1; + if (!(match = cursor.findNext()) || (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) { + focusOnMatch(state, null, false); //resets count to 0/0 + return; + } + } + + state.posFrom = cursor.from(); state.posTo = cursor.to(); + if (shouldReplace) { + cm.isReplacing = true; + + cm.setCursor(state.posFrom) + + doReplace(cm, state); + cm.isReplacing = false; + } + } + var doReplace = function(cm, state) { + cursor.replace(replaceText); + markReplacedText(cm, cursor); + advance(false); + var forceIncrement = replaceText.includes(query) || replaceText.toLowerCase() === query.toLowerCase(); + focusOnMatch(state, null, forceIncrement); + }; + advance(true); + } + + function replace(cm, all) { + if (cm.getOption("readOnly")) return; + var query = cm.getSelection() || getSearchState(cm).lastQuery; + var dialogText = '' + (all ? cm.phrase("Replace all:") : cm.phrase("Replace:")) + ''; + dialog(cm, dialogText + getReplaceQueryDialog(cm), dialogText, query, function(query) { + if (!query) return; + query = parseQuery(query); + dialog(cm, getReplacementQueryDialog(cm), cm.phrase("Replace with:"), "", function(text) { + text = parseString(text) + if (all) { + replaceAll(cm, query, text) + } else { + clearSearch(cm); + var cursor = getSearchCursor(cm, query, cm.getCursor("from")); + var advance = function() { + var start = cursor.from(), match; + if (!(match = cursor.findNext())) { + cursor = getSearchCursor(cm, query); + if (!(match = cursor.findNext()) || + (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return; + } + cm.setSelection(cursor.from(), cursor.to()); + cm.scrollIntoView({from: cursor.from(), to: cursor.to()}); + confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase("Replace?"), + [function() {doReplace(match);}, advance, + function() {replaceAll(cm, query, text)}]); + }; + var doReplace = function(match) { + cursor.replace(typeof query == "string" ? text : + text.replace(/\$(\d)/g, function(_, i) {return match[i];})); + advance(); + }; + advance(); + } + }); + }); + } + + CodeMirror.commands.find = function(cm) { + cm.operation(function () { + clearSearch(cm); + doSearch(cm); + }); + focusOnMatch(getSearchState(cm), null, false); + }; + CodeMirror.commands.findNext = function(cm) { + cm.operation(function () { + clearFocusedMatches(cm); + doSearch(cm, false); + }); + focusOnMatch(getSearchState(cm), {next: true}, false); + }; + CodeMirror.commands.findPrev = function(cm) { + cm.operation(function () { + clearFocusedMatches(cm); + doSearch(cm, false); + }); + focusOnMatch(getSearchState(cm), {prev: true}, false); + }; + CodeMirror.commands.clearSearch = clearSearch; + CodeMirror.commands.replace = replace; + CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);}; + CodeMirror.commands.replaceAllWithoutDialogs = function(cm) { replaceAllWithoutDialogs(cm);}; + CodeMirror.commands.replaceSingleWithoutDialogs = function(cm) { replaceSingleWithoutDialogs(cm);}; + }); \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/addon/search/searchcursor.js b/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/addon/search/searchcursor.js new file mode 100644 index 0000000..6f40c9e --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/addon/search/searchcursor.js @@ -0,0 +1,293 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")) + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod) + else // Plain browser env + mod(CodeMirror) + })(function(CodeMirror) { + "use strict" + var Pos = CodeMirror.Pos + + function regexpFlags(regexp) { + var flags = regexp.flags + return flags != null ? flags : (regexp.ignoreCase ? "i" : "") + + (regexp.global ? "g" : "") + + (regexp.multiline ? "m" : "") + } + + function ensureFlags(regexp, flags) { + var current = regexpFlags(regexp), target = current + for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1) + target += flags.charAt(i) + return current == target ? regexp : new RegExp(regexp.source, target) + } + + function maybeMultiline(regexp) { + return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source) + } + + function searchRegexpForward(doc, regexp, start) { + regexp = ensureFlags(regexp, "g") + for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) { + regexp.lastIndex = ch + var string = doc.getLine(line), match = regexp.exec(string) + if (match) + return {from: Pos(line, match.index), + to: Pos(line, match.index + match[0].length), + match: match} + } + } + + function searchRegexpForwardMultiline(doc, regexp, start) { + if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start) + + regexp = ensureFlags(regexp, "gm") + var string, chunk = 1 + for (var line = start.line, last = doc.lastLine(); line <= last;) { + // This grows the search buffer in exponentially-sized chunks + // between matches, so that nearby matches are fast and don't + // require concatenating the whole document (in case we're + // searching for something that has tons of matches), but at the + // same time, the amount of retries is limited. + for (var i = 0; i < chunk; i++) { + if (line > last) break + var curLine = doc.getLine(line++) + string = string == null ? curLine : string + "\n" + curLine + } + chunk = chunk * 2 + regexp.lastIndex = start.ch + var match = regexp.exec(string) + if (match) { + var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n") + var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length + return {from: Pos(startLine, startCh), + to: Pos(startLine + inside.length - 1, + inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length), + match: match} + } + } + } + + function lastMatchIn(string, regexp) { + var cutOff = 0, match + for (;;) { + regexp.lastIndex = cutOff + var newMatch = regexp.exec(string) + if (!newMatch) return match + match = newMatch + cutOff = match.index + (match[0].length || 1) + if (cutOff == string.length) return match + } + } + + function searchRegexpBackward(doc, regexp, start) { + regexp = ensureFlags(regexp, "g") + for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) { + var string = doc.getLine(line) + if (ch > -1) string = string.slice(0, ch) + var match = lastMatchIn(string, regexp) + if (match) + return {from: Pos(line, match.index), + to: Pos(line, match.index + match[0].length), + match: match} + } + } + + function searchRegexpBackwardMultiline(doc, regexp, start) { + regexp = ensureFlags(regexp, "gm") + var string, chunk = 1 + for (var line = start.line, first = doc.firstLine(); line >= first;) { + for (var i = 0; i < chunk; i++) { + var curLine = doc.getLine(line--) + string = string == null ? curLine.slice(0, start.ch) : curLine + "\n" + string + } + chunk *= 2 + + var match = lastMatchIn(string, regexp) + if (match) { + var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n") + var startLine = line + before.length, startCh = before[before.length - 1].length + return {from: Pos(startLine, startCh), + to: Pos(startLine + inside.length - 1, + inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length), + match: match} + } + } + } + + var doFold, noFold + if (String.prototype.normalize) { + doFold = function(str) { return str.normalize("NFD").toLowerCase() } + noFold = function(str) { return str.normalize("NFD") } + } else { + doFold = function(str) { return str.toLowerCase() } + noFold = function(str) { return str } + } + + // Maps a position in a case-folded line back to a position in the original line + // (compensating for codepoints increasing in number during folding) + function adjustPos(orig, folded, pos, foldFunc) { + if (orig.length == folded.length) return pos + for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) { + if (min == max) return min + var mid = (min + max) >> 1 + var len = foldFunc(orig.slice(0, mid)).length + if (len == pos) return mid + else if (len > pos) max = mid + else min = mid + 1 + } + } + + function searchStringForward(doc, query, start, caseFold) { + // Empty string would match anything and never progress, so we + // define it to match nothing instead. + if (!query.length) return null + var fold = caseFold ? doFold : noFold + var lines = fold(query).split(/\r|\n\r?/) + + search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) { + var orig = doc.getLine(line).slice(ch), string = fold(orig) + if (lines.length == 1) { + var found = string.indexOf(lines[0]) + if (found == -1) continue search + var start = adjustPos(orig, string, found, fold) + ch + return {from: Pos(line, adjustPos(orig, string, found, fold) + ch), + to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)} + } else { + var cutFrom = string.length - lines[0].length + if (string.slice(cutFrom) != lines[0]) continue search + for (var i = 1; i < lines.length - 1; i++) + if (fold(doc.getLine(line + i)) != lines[i]) continue search + var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1] + if (endString.slice(0, lastLine.length) != lastLine) continue search + return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch), + to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))} + } + } + } + + function searchStringBackward(doc, query, start, caseFold) { + if (!query.length) return null + var fold = caseFold ? doFold : noFold + var lines = fold(query).split(/\r|\n\r?/) + + search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) { + var orig = doc.getLine(line) + if (ch > -1) orig = orig.slice(0, ch) + var string = fold(orig) + if (lines.length == 1) { + var found = string.lastIndexOf(lines[0]) + if (found == -1) continue search + return {from: Pos(line, adjustPos(orig, string, found, fold)), + to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))} + } else { + var lastLine = lines[lines.length - 1] + if (string.slice(0, lastLine.length) != lastLine) continue search + for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++) + if (fold(doc.getLine(start + i)) != lines[i]) continue search + var top = doc.getLine(line + 1 - lines.length), topString = fold(top) + if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search + return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)), + to: Pos(line, adjustPos(orig, string, lastLine.length, fold))} + } + } + } + + function SearchCursor(doc, query, pos, options) { + this.atOccurrence = false + this.doc = doc + pos = pos ? doc.clipPos(pos) : Pos(0, 0) + this.pos = {from: pos, to: pos} + + var caseFold + if (typeof options == "object") { + caseFold = options.caseFold + } else { // Backwards compat for when caseFold was the 4th argument + caseFold = options + options = null + } + + if (typeof query == "string") { + if (caseFold == null) caseFold = false + this.matches = function(reverse, pos) { + return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold) + } + } else { + query = ensureFlags(query, "gm") + if (!options || options.multiline !== false) + this.matches = function(reverse, pos) { + return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos) + } + else + this.matches = function(reverse, pos) { + return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos) + } + } + } + + SearchCursor.prototype = { + findNext: function() {return this.find(false)}, + findPrevious: function() {return this.find(true)}, + + find: function(reverse) { + var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to)) + + // Implements weird auto-growing behavior on null-matches for + // backwards-compatiblity with the vim code (unfortunately) + while (result && CodeMirror.cmpPos(result.from, result.to) == 0) { + if (reverse) { + if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1) + else if (result.from.line == this.doc.firstLine()) result = null + else result = this.matches(reverse, this.doc.clipPos(Pos(result.from.line - 1))) + } else { + if (result.to.ch < this.doc.getLine(result.to.line).length) result.to = Pos(result.to.line, result.to.ch + 1) + else if (result.to.line == this.doc.lastLine()) result = null + else result = this.matches(reverse, Pos(result.to.line + 1, 0)) + } + } + + if (result) { + this.pos = result + this.atOccurrence = true + return this.pos.match || true + } else { + var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0) + this.pos = {from: end, to: end} + return this.atOccurrence = false + } + }, + + from: function() {if (this.atOccurrence) return this.pos.from}, + to: function() {if (this.atOccurrence) return this.pos.to}, + + replace: function(newText, origin) { + if (!this.atOccurrence) return + var lines = CodeMirror.splitLines(newText) + this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin) + this.pos.to = Pos(this.pos.from.line + lines.length - 1, + lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0)) + } + } + + CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { + return new SearchCursor(this.doc, query, pos, caseFold) + }) + CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) { + return new SearchCursor(this, query, pos, caseFold) + }) + + CodeMirror.defineExtension("selectMatches", function(query, caseFold) { + var ranges = [] + var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold) + while (cur.findNext()) { + if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break + ranges.push({anchor: cur.from(), head: cur.to()}) + } + if (ranges.length) + this.setSelections(ranges, 0) + }) + }); \ No newline at end of file diff --git a/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/ext.CodeMirror.js b/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/ext.CodeMirror.js new file mode 100644 index 0000000..713c0c0 --- /dev/null +++ b/Apps/Wikipedia/Wikipedia/assets/codemirror/resources/ext.CodeMirror.js @@ -0,0 +1,287 @@ +( function () { + var useCodeMirror, codeMirror, api, originHooksTextarea, cmTextSelection, + enableContentEditable = true; + + if ( mw.config.get( 'wgCodeEditorCurrentLanguage' ) ) { // If the CodeEditor is used then just exit; + return; + } + + // Exit if WikiEditor is disabled + // usebetatoolbar can be the string "0" if the user disabled the preference - Bug T54542#555387 + if ( !( mw.loader.getState( 'ext.wikiEditor' ) && mw.user.options.get( 'usebetatoolbar' ) > 0 ) ) { + return; + } + + useCodeMirror = mw.user.options.get( 'usecodemirror' ) > 0; + api = new mw.Api(); + + originHooksTextarea = $.valHooks.textarea; + // define jQuery hook for searching and replacing text using JS if CodeMirror is enabled, see Bug: T108711 + $.valHooks.textarea = { + get: function ( elem ) { + if ( elem.id === 'wpTextbox1' && codeMirror ) { + return codeMirror.doc.getValue(); + } else if ( originHooksTextarea ) { + return originHooksTextarea.get( elem ); + } + return elem.value; + }, + set: function ( elem, value ) { + if ( elem.id === 'wpTextbox1' && codeMirror ) { + return codeMirror.doc.setValue( value ); + } else if ( originHooksTextarea ) { + return originHooksTextarea.set( elem, value ); + } + elem.value = value; + } + }; + + // Disable spellchecking for Firefox users on non-Mac systems (Bug T95104) + if ( navigator.userAgent.indexOf( 'Firefox' ) > -1 && + navigator.userAgent.indexOf( 'Mac' ) === -1 + ) { + enableContentEditable = false; + } + + // T174055: Do not redefine the browser history navigation keys (T175378: for PC only) + CodeMirror.keyMap.pcDefault[ 'Alt-Left' ] = false; + CodeMirror.keyMap.pcDefault[ 'Alt-Right' ] = false; + + // jQuery.textSelection overrides for CodeMirror. + // See jQuery.textSelection.js for method documentation + cmTextSelection = { + getContents: function () { + return codeMirror.doc.getValue(); + }, + setContents: function ( content ) { + codeMirror.doc.setValue( content ); + return this; + }, + getSelection: function () { + return codeMirror.doc.getSelection(); + }, + setSelection: function ( options ) { + codeMirror.focus(); + codeMirror.doc.setSelection( codeMirror.doc.posFromIndex( options.start ), codeMirror.doc.posFromIndex( options.end ) ); + return this; + }, + replaceSelection: function ( value ) { + codeMirror.doc.replaceSelection( value ); + return this; + }, + getCaretPosition: function ( options ) { + var caretPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( true ) ), + endPos = codeMirror.doc.indexFromPos( codeMirror.doc.getCursor( false ) ); + if ( options.startAndEnd ) { + return [ caretPos, endPos ]; + } + return caretPos; + }, + scrollToCaretPosition: function () { + codeMirror.scrollIntoView( null ); + return this; + } + }; + + /** + * Save CodeMirror enabled pref. + * + * @param {boolean} prefValue True, if CodeMirror should be enabled by default, otherwise false. + */ + function setCodeEditorPreference( prefValue ) { + useCodeMirror = prefValue; // Save state for function updateToolbarButton() + + if ( mw.user.isAnon() ) { // Skip it for anon users + return; + } + api.saveOption( 'usecodemirror', prefValue ? 1 : 0 ); + mw.user.options.set( 'usecodemirror', prefValue ? 1 : 0 ); + } + + /** + * Replaces the default textarea with CodeMirror + */ + function enableCodeMirror() { + var config = mw.config.get( 'extCodeMirrorConfig' ); + + mw.loader.using( config.pluginModules, function () { + var $codeMirror, + $textbox1 = $( '#wpTextbox1' ), + selectionStart = $textbox1.prop( 'selectionStart' ), + selectionEnd = $textbox1.prop( 'selectionEnd' ), + scrollTop = $textbox1.scrollTop(); + + // If CodeMirror is already loaded or wikEd gadget is enabled, abort. See T178348. + // FIXME: Would be good to replace the wikEd check with something more generic. + if ( codeMirror || mw.user.options.get( 'gadget-wikEd' ) > 0 ) { + return; + } + + codeMirror = CodeMirror.fromTextArea( $textbox1[ 0 ], { + mwConfig: config, + // styleActiveLine: true, // disabled since Bug: T162204, maybe should be optional + lineWrapping: true, + readOnly: $textbox1[ 0 ].readOnly, + // select mediawiki as text input mode + mode: 'text/mediawiki', + extraKeys: { + Tab: false, + 'Shift-Tab': false, + // T174514: Move the cursor at the beginning/end of the current wrapped line + Home: 'goLineLeft', + End: 'goLineRight' + }, + inputStyle: enableContentEditable ? 'contenteditable' : 'textarea', + spellcheck: enableContentEditable, + viewportMargin: Infinity + } ); + $codeMirror = $( codeMirror.getWrapperElement() ); + + // Allow textSelection() functions to work with CodeMirror editing field. + $codeMirror.textSelection( 'register', cmTextSelection ); + // Also override textSelection() functions for the "real" hidden textarea to route to + // CodeMirror. We unregister this when switching to normal textarea mode. + $textbox1.textSelection( 'register', cmTextSelection ); + + $codeMirror.resizable( { + handles: 'se', + resize: function ( event, ui ) { + ui.size.width = ui.originalSize.width; + } + } ); + + codeMirror.doc.setSelection( codeMirror.doc.posFromIndex( selectionEnd ), codeMirror.doc.posFromIndex( selectionStart ) ); + codeMirror.scrollTo( null, scrollTop ); + + // HACK: